├── .DS_Store ├── Collection ├── RecordCache.d.ts ├── RecordCache.js ├── index.d.ts └── index.js ├── Database ├── CollectionMap │ ├── index.d.ts │ └── index.js ├── LocalStorage │ ├── index.d.ts │ └── index.js ├── WorkQueue.d.ts ├── WorkQueue.js ├── index.d.ts └── index.js ├── DatabaseProvider ├── DatabaseContext.d.ts ├── DatabaseContext.js ├── index.d.ts ├── index.js ├── withDatabase.d.ts └── withDatabase.js ├── LICENSE ├── Model ├── helpers.d.ts ├── helpers.js ├── index.d.ts └── index.js ├── Query ├── helpers.d.ts ├── helpers.js ├── index.d.ts └── index.js ├── QueryDescription ├── helpers.d.ts ├── helpers.js ├── index.d.ts ├── index.js ├── operators.d.ts ├── operators.js ├── type.d.ts └── type.js ├── README.md ├── RawRecord ├── index.d.ts └── index.js ├── Relation ├── helpers.d.ts ├── helpers.js ├── index.d.ts └── index.js ├── Schema ├── index.d.ts ├── index.js └── migrations │ ├── getSyncChanges │ ├── index.d.ts │ └── index.js │ ├── index.d.ts │ ├── index.js │ ├── stepsForMigration.d.ts │ └── stepsForMigration.js ├── WatermelonDB.podspec ├── adapters ├── common.js ├── compat.d.ts ├── compat.js ├── error.js ├── lokijs │ ├── common.d.ts │ ├── common.js │ ├── dispatcher.d.ts │ ├── dispatcher.js │ ├── index.d.ts │ ├── index.js │ ├── type.d.ts │ ├── type.js │ └── worker │ │ ├── DatabaseBridge.js │ │ ├── DatabaseDriver.js │ │ ├── cloneMessage │ │ └── index.js │ │ ├── encodeQuery │ │ └── index.js │ │ ├── executeQuery.js │ │ ├── loki.worker.js │ │ ├── lokiExtensions.js │ │ ├── performJoins │ │ └── index.js │ │ └── synchronousWorker.js ├── sqlite │ ├── devtools.js │ ├── encodeBatch │ │ └── index.js │ ├── encodeQuery │ │ └── index.js │ ├── encodeSchema │ │ └── index.js │ ├── encodeValue │ │ └── index.js │ ├── index.d.ts │ ├── index.js │ ├── makeDispatcher │ │ ├── decodeQueryResult │ │ │ └── index.js │ │ ├── index.js │ │ └── index.native.js │ ├── sqlite-node │ │ ├── Database.js │ │ ├── DatabaseBridge.js │ │ └── DatabaseDriver.js │ ├── type.d.ts │ └── type.js ├── type.d.ts └── type.js ├── decorators ├── action │ ├── index.d.ts │ └── index.js ├── children │ ├── index.d.ts │ └── index.js ├── common.d.ts ├── common.js ├── date │ ├── index.d.ts │ └── index.js ├── field │ ├── index.d.ts │ └── index.js ├── immutableRelation │ ├── index.d.ts │ └── index.js ├── index.d.ts ├── index.js ├── json │ ├── index.d.ts │ └── index.js ├── lazy │ ├── index.d.ts │ └── index.js ├── nochange │ ├── index.d.ts │ └── index.js ├── readonly │ ├── index.d.ts │ └── index.js ├── relation │ ├── index.d.ts │ └── index.js └── text │ ├── index.d.ts │ └── index.js ├── hooks ├── index.d.ts ├── index.js └── use-database.js ├── index.d.ts ├── index.js ├── native ├── android-jsi │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── gradle.xml │ │ ├── misc.xml │ │ └── vcs.xml │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── DatabasePlatformAndroid.cpp │ │ ├── DatabasePlatformAndroid.h │ │ └── JSIInstaller.cpp │ │ └── java │ │ └── com │ │ └── nozbe │ │ └── watermelondb │ │ └── jsi │ │ ├── JSIInstaller.java │ │ ├── WatermelonDBJSIPackage.java │ │ └── WatermelonJSI.java ├── android │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── compiler.xml │ │ ├── gradle.xml │ │ ├── jarRepositories.xml │ │ ├── kotlinc.xml │ │ ├── misc.xml │ │ └── vcs.xml │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── nozbe │ │ └── watermelondb │ │ ├── Connection.java │ │ ├── DatabaseUtils.java │ │ ├── MigrationNeededError.java │ │ ├── Queries.java │ │ ├── SchemaNeededError.java │ │ ├── WMDatabase.java │ │ ├── WMDatabaseBridge.java │ │ ├── WMDatabaseDriver.java │ │ ├── WatermelonDBPackage.java │ │ └── utils │ │ ├── MigrationSet.java │ │ ├── Pair.java │ │ └── Schema.java ├── ios │ └── WatermelonDB │ │ ├── DatabasePlatformIOS.mm │ │ ├── FMDB │ │ ├── LICENSE.txt │ │ ├── README.markdown │ │ └── src │ │ │ └── fmdb │ │ │ ├── FMDB.h │ │ │ ├── FMDatabase.h │ │ │ ├── FMDatabase.m │ │ │ ├── FMDatabaseAdditions.h │ │ │ ├── FMDatabaseAdditions.m │ │ │ ├── FMDatabasePool.h │ │ │ ├── FMDatabasePool.m │ │ │ ├── FMDatabaseQueue.h │ │ │ ├── FMDatabaseQueue.m │ │ │ ├── FMResultSet.h │ │ │ └── FMResultSet.m │ │ ├── JSIInstaller.h │ │ ├── JSIInstaller.mm │ │ ├── WatermelonDB.h │ │ └── objc │ │ ├── WMDatabase.h │ │ ├── WMDatabase.m │ │ ├── WMDatabaseBridge.h │ │ ├── WMDatabaseBridge.m │ │ ├── WMDatabaseDriver.h │ │ └── WMDatabaseDriver.m ├── shared │ ├── Database-batch.cpp │ ├── Database-jsi.cpp │ ├── Database-query.cpp │ ├── Database-sqlite.cpp │ ├── Database-turboSync.cpp │ ├── Database.cpp │ ├── Database.h │ ├── DatabaseBridge.cpp │ ├── DatabasePlatform.h │ ├── JSIHelpers.h │ ├── Sqlite.cpp │ └── Sqlite.h └── sqlite-cipher-amalgamation │ ├── sqlite3.c │ ├── sqlite3.h │ └── sqlite3ext.h ├── observation ├── encodeMatcher │ ├── canEncode.js │ ├── index.js │ └── operators.js ├── subscribeToCount │ ├── index.d.ts │ └── index.js ├── subscribeToQuery.d.ts ├── subscribeToQuery.js ├── subscribeToQueryReloading │ ├── index.d.ts │ └── index.js ├── subscribeToQueryWithColumns │ └── index.js └── subscribeToSimpleQuery │ ├── index.js │ └── processChangeSet.js ├── package.json ├── react-native.config.js ├── src ├── Collection │ ├── RecordCache.js │ └── index.js ├── Database │ ├── CollectionMap │ │ └── index.js │ ├── LocalStorage │ │ └── index.js │ ├── WorkQueue.js │ └── index.js ├── DatabaseProvider │ ├── DatabaseContext.js │ ├── index.js │ └── withDatabase.js ├── Model │ ├── helpers.js │ └── index.js ├── Query │ ├── helpers.js │ └── index.js ├── QueryDescription │ ├── helpers.js │ ├── index.js │ ├── operators.js │ └── type.js ├── RawRecord │ └── index.js ├── Relation │ ├── helpers.js │ └── index.js ├── Schema │ ├── index.js │ └── migrations │ │ ├── getSyncChanges │ │ └── index.js │ │ ├── index.js │ │ └── stepsForMigration.js ├── adapters │ ├── common.js │ ├── compat.js │ ├── error.js │ ├── lokijs │ │ ├── common.js │ │ ├── dispatcher.js │ │ ├── index.js │ │ ├── type.js │ │ └── worker │ │ │ ├── DatabaseBridge.js │ │ │ ├── DatabaseDriver.js │ │ │ ├── cloneMessage │ │ │ └── index.js │ │ │ ├── encodeQuery │ │ │ └── index.js │ │ │ ├── executeQuery.js │ │ │ ├── loki.worker.js │ │ │ ├── lokiExtensions.js │ │ │ ├── performJoins │ │ │ └── index.js │ │ │ └── synchronousWorker.js │ ├── sqlite │ │ ├── devtools.js │ │ ├── encodeBatch │ │ │ └── index.js │ │ ├── encodeQuery │ │ │ └── index.js │ │ ├── encodeSchema │ │ │ └── index.js │ │ ├── encodeValue │ │ │ └── index.js │ │ ├── index.js │ │ ├── makeDispatcher │ │ │ ├── decodeQueryResult │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── index.native.js │ │ ├── sqlite-node │ │ │ ├── Database.js │ │ │ ├── DatabaseBridge.js │ │ │ └── DatabaseDriver.js │ │ └── type.js │ └── type.js ├── decorators │ ├── action │ │ └── index.js │ ├── children │ │ └── index.js │ ├── common.js │ ├── date │ │ └── index.js │ ├── field │ │ └── index.js │ ├── immutableRelation │ │ └── index.js │ ├── index.js │ ├── json │ │ └── index.js │ ├── lazy │ │ └── index.js │ ├── nochange │ │ └── index.js │ ├── readonly │ │ └── index.js │ ├── relation │ │ └── index.js │ └── text │ │ └── index.js ├── hooks │ ├── index.js │ └── use-database.js ├── index.js ├── observation │ ├── encodeMatcher │ │ ├── canEncode.js │ │ ├── index.js │ │ └── operators.js │ ├── subscribeToCount │ │ └── index.js │ ├── subscribeToQuery.js │ ├── subscribeToQueryReloading │ │ └── index.js │ ├── subscribeToQueryWithColumns │ │ └── index.js │ └── subscribeToSimpleQuery │ │ ├── index.js │ │ └── processChangeSet.js ├── sync │ ├── SyncLogger │ │ └── index.js │ ├── debugPrintChanges │ │ └── index.js │ ├── helpers.js │ ├── impl │ │ ├── applyRemote.js │ │ ├── fetchLocal.js │ │ ├── helpers.js │ │ ├── index.js │ │ ├── markAsSynced.js │ │ └── synchronize.js │ └── index.js ├── types.js └── utils │ ├── common │ ├── connectionTag │ │ └── index.js │ ├── deepFreeze │ │ └── index.js │ ├── deprecated │ │ └── index.js │ ├── devMeasureTime │ │ └── index.js │ ├── diagnosticError │ │ └── index.js │ ├── ensureSync │ │ └── index.js │ ├── index.js │ ├── invariant │ │ └── index.js │ ├── isRN │ │ ├── index.js │ │ └── index.native.js │ ├── logError │ │ └── index.js │ ├── logger │ │ └── index.js │ ├── makeDecorator │ │ └── index.js │ ├── memory │ │ └── index.js │ └── randomId │ │ ├── fallback.js │ │ ├── index.js │ │ ├── randomId.js │ │ ├── randomId.native.js │ │ └── randomId_v2.native.js │ ├── fp │ ├── Result │ │ └── index.js │ ├── allPass │ │ └── index.js │ ├── allPromises │ │ └── index.js │ ├── allPromisesObj │ │ └── index.js │ ├── anyPass │ │ └── index.js │ ├── areRecordsEqual │ │ └── index.js │ ├── arrayDifference │ │ └── index.js │ ├── arrayOrSpread │ │ └── index.js │ ├── checkName │ │ └── index.js │ ├── filterObj │ │ └── index.js │ ├── fromPairs │ │ └── index.js │ ├── groupBy │ │ └── index.js │ ├── identicalArrays │ │ └── index.js │ ├── identity │ │ └── index.js │ ├── index.js │ ├── isObj │ │ └── index.js │ ├── keys │ │ └── index.js │ ├── likeToRegexp │ │ └── index.js │ ├── mapObj │ │ └── index.js │ ├── noop │ │ └── index.js │ ├── pipe │ │ └── index.js │ ├── sortBy │ │ └── index.js │ ├── splitEvery │ │ └── index.js │ ├── toPairs │ │ └── index.js │ ├── unique │ │ └── index.js │ ├── unnest │ │ └── index.js │ └── values │ │ └── index.js │ ├── rx │ ├── __wmelonRxShim │ │ └── index.js │ ├── __wmelonRxShimESM2015 │ │ └── index.js │ ├── cacheWhileConnected │ │ └── index.js │ ├── doOnDispose │ │ └── index.js │ ├── doOnSubscribe │ │ └── index.js │ ├── index.js │ └── publishReplayLatestWhileConnected │ │ └── index.js │ └── subscriptions │ ├── SharedSubscribable │ └── index.js │ ├── index.js │ └── type.js ├── sync ├── SyncLogger │ ├── index.d.ts │ └── index.js ├── debugPrintChanges │ ├── index.d.ts │ └── index.js ├── helpers.d.ts ├── helpers.js ├── impl │ ├── applyRemote.d.ts │ ├── applyRemote.js │ ├── fetchLocal.d.ts │ ├── fetchLocal.js │ ├── helpers.d.ts │ ├── helpers.js │ ├── index.d.ts │ ├── index.js │ ├── markAsSynced.d.ts │ ├── markAsSynced.js │ ├── synchronize.d.ts │ └── synchronize.js ├── index.d.ts └── index.js ├── types.d.ts ├── types.js ├── utils ├── common │ ├── connectionTag │ │ ├── index.d.ts │ │ └── index.js │ ├── deepFreeze │ │ ├── index.d.ts │ │ └── index.js │ ├── deprecated │ │ ├── index.d.ts │ │ └── index.js │ ├── devMeasureTime │ │ ├── index.d.ts │ │ └── index.js │ ├── diagnosticError │ │ ├── index.d.ts │ │ └── index.js │ ├── ensureSync │ │ ├── index.d.ts │ │ └── index.js │ ├── index.d.ts │ ├── index.js │ ├── invariant │ │ ├── index.d.ts │ │ └── index.js │ ├── isRN │ │ ├── index.d.ts │ │ ├── index.js │ │ └── index.native.js │ ├── logError │ │ ├── index.d.ts │ │ └── index.js │ ├── logger │ │ ├── index.d.ts │ │ └── index.js │ ├── makeDecorator │ │ ├── index.d.ts │ │ └── index.js │ ├── memory │ │ ├── index.d.ts │ │ └── index.js │ └── randomId │ │ ├── fallback.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── randomId.js │ │ ├── randomId.native.js │ │ └── randomId_v2.native.js ├── fp │ ├── Result │ │ ├── index.d.ts │ │ └── index.js │ ├── allPass │ │ ├── index.d.ts │ │ └── index.js │ ├── allPromises │ │ ├── index.d.ts │ │ └── index.js │ ├── allPromisesObj │ │ ├── index.d.ts │ │ └── index.js │ ├── anyPass │ │ ├── index.d.ts │ │ └── index.js │ ├── areRecordsEqual │ │ ├── index.d.ts │ │ └── index.js │ ├── arrayDifference │ │ ├── index.d.ts │ │ └── index.js │ ├── arrayOrSpread │ │ ├── index.d.ts │ │ └── index.js │ ├── checkName │ │ ├── index.d.ts │ │ └── index.js │ ├── filterObj │ │ ├── index.d.ts │ │ └── index.js │ ├── fromPairs │ │ ├── index.d.ts │ │ └── index.js │ ├── groupBy │ │ ├── index.d.ts │ │ └── index.js │ ├── identicalArrays │ │ ├── index.d.ts │ │ └── index.js │ ├── identity │ │ ├── index.d.ts │ │ └── index.js │ ├── index.d.ts │ ├── index.js │ ├── isObj │ │ ├── index.d.ts │ │ └── index.js │ ├── keys │ │ ├── index.d.ts │ │ └── index.js │ ├── likeToRegexp │ │ ├── index.d.ts │ │ └── index.js │ ├── mapObj │ │ ├── index.d.ts │ │ └── index.js │ ├── noop │ │ ├── index.d.ts │ │ └── index.js │ ├── pipe │ │ ├── index.d.ts │ │ └── index.js │ ├── sortBy │ │ ├── index.d.ts │ │ └── index.js │ ├── splitEvery │ │ ├── index.d.ts │ │ └── index.js │ ├── toPairs │ │ ├── index.d.ts │ │ └── index.js │ ├── unique │ │ ├── index.d.ts │ │ └── index.js │ ├── unnest │ │ ├── index.d.ts │ │ └── index.js │ └── values │ │ ├── index.d.ts │ │ └── index.js ├── rx │ ├── __wmelonRxShim │ │ ├── index.d.ts │ │ └── index.js │ ├── __wmelonRxShimESM2015 │ │ ├── index.d.ts │ │ └── index.js │ ├── cacheWhileConnected │ │ ├── index.d.ts │ │ └── index.js │ ├── doOnDispose │ │ ├── index.d.ts │ │ └── index.js │ ├── doOnSubscribe │ │ ├── index.d.ts │ │ └── index.js │ ├── index.d.ts │ ├── index.js │ └── publishReplayLatestWhileConnected │ │ ├── index.d.ts │ │ └── index.js └── subscriptions │ ├── SharedSubscribable │ ├── index.d.ts │ └── index.js │ ├── index.d.ts │ ├── index.js │ ├── type.d.ts │ └── type.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skiff-org/cipher/8f29b80aeafb6909055e1b37455b2fc560656124/.DS_Store -------------------------------------------------------------------------------- /Collection/RecordCache.d.ts: -------------------------------------------------------------------------------- 1 | import type Model from '../Model' 2 | import type { RecordId } from '../Model' 3 | import type Collection from './index' 4 | import type { CachedQueryResult } from '../adapters/type' 5 | import type { TableName } from '../Schema' 6 | import type { RawRecord } from '../RawRecord' 7 | 8 | type Instantiator = (_: RawRecord) => T 9 | 10 | export default class RecordCache { 11 | map: Map 12 | 13 | tableName: TableName 14 | 15 | recordInsantiator: Instantiator 16 | 17 | _debugCollection: Collection 18 | 19 | constructor( 20 | tableName: TableName, 21 | recordInsantiator: Instantiator, 22 | collection: Collection, 23 | ) 24 | 25 | get(id: RecordId): Record | undefined 26 | 27 | add(record: Record): void 28 | 29 | delete(record: Record): void 30 | 31 | unsafeClear(): void 32 | 33 | recordsFromQueryResult(result: CachedQueryResult): Record[] 34 | 35 | recordFromQueryResult(result: RecordId | RawRecord): Record 36 | 37 | _cachedModelForId(id: RecordId): Record 38 | 39 | _modelForRaw(raw: RawRecord): Record 40 | } 41 | -------------------------------------------------------------------------------- /Database/CollectionMap/index.d.ts: -------------------------------------------------------------------------------- 1 | import type Model from '../../Model' 2 | import Collection from '../../Collection' 3 | import type { TableName } from '../../Schema' 4 | import type Database from '../index' 5 | 6 | export default class CollectionMap { 7 | map: { [tableName: TableName]: Collection } 8 | 9 | constructor(db: Database, modelClasses: Model[]) 10 | 11 | get(tableName: TableName): Collection 12 | } 13 | -------------------------------------------------------------------------------- /Database/CollectionMap/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _Collection = _interopRequireDefault(require("../../Collection")); 7 | var _common = require("../../utils/common"); 8 | var CollectionMap = /*#__PURE__*/function () { 9 | function CollectionMap(db, modelClasses) { 10 | var _this = this; 11 | this.map = Object.create(null); 12 | modelClasses.forEach(function (modelClass) { 13 | var { 14 | table: table 15 | } = modelClass; 16 | if ('production' !== process.env.NODE_ENV) { 17 | // TODO: move these checks to Collection? 18 | (0, _common.invariant)('string' === typeof table, "Model class ".concat(modelClass.name, " passed to Database constructor is missing \"static table = 'table_name'\"")); 19 | (0, _common.invariant)(db.schema.tables[table], "Model class ".concat(modelClass.name, " has static table defined that is missing in schema known by this database")); 20 | } 21 | _this.map[table] = new _Collection.default(db, modelClass); 22 | }); 23 | Object.freeze(this.map); 24 | } 25 | var _proto = CollectionMap.prototype; 26 | _proto.get = function get(tableName) { 27 | return this.map[tableName] || null; 28 | }; 29 | return CollectionMap; 30 | }(); 31 | exports.default = CollectionMap; -------------------------------------------------------------------------------- /Database/LocalStorage/index.d.ts: -------------------------------------------------------------------------------- 1 | import type Database from '..' 2 | 3 | export type LocalStorageKey = string 4 | 5 | export function localStorageKey(name: string): LocalStorageKey 6 | 7 | export default class LocalStorage { 8 | _db: Database 9 | 10 | constructor(database: Database) 11 | 12 | // Get value from LocalStorage (returns value deserialized from JSON) 13 | // Returns `undefined` if not found 14 | get(key: LocalStorageKey): Promise 15 | 16 | // Experimental: Same as get(), but can be called synchronously 17 | _getSync( 18 | key: LocalStorageKey, 19 | callback: (value: ValueType | void) => void, 20 | ): void 21 | 22 | // Set value to LocalStorage 23 | // Only JSON-serializable values are allowed and well-behaved: 24 | // strings, numbers, booleans, and null; as well as arrays and objects only containing those 25 | // 26 | // Serializing other values will either throw an error (e.g. function passed) or be serialized 27 | // such that deserializing it won't yield an equal value (e.g. NaN to null, Dates to a string) 28 | // See details: 29 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description 30 | set(key: LocalStorageKey, value: ValueType): Promise 31 | 32 | remove(key: LocalStorageKey): Promise 33 | } 34 | -------------------------------------------------------------------------------- /Database/WorkQueue.d.ts: -------------------------------------------------------------------------------- 1 | import type Model from '../Model' 2 | import type Database from './index' 3 | import { $ReadOnlyArray } from '../types'; 4 | 5 | export interface ReaderInterface { 6 | callReader(reader: () => Promise): Promise; 7 | } 8 | 9 | export interface WriterInterface extends ReaderInterface { 10 | callWriter(writer: () => Promise): Promise; 11 | batch(...records: $ReadOnlyArray): Promise; 12 | } 13 | 14 | type WorkQueueItem = { 15 | work: (_: ReaderInterface | WriterInterface) => Promise; 16 | isWriter: boolean; 17 | resolve: (value: T) => void; 18 | reject: (reason: any) => void; 19 | description?: string; 20 | } 21 | 22 | export default class WorkQueue { 23 | _db: Database 24 | 25 | _queue: WorkQueueItem[] 26 | 27 | _subActionIncoming: boolean 28 | 29 | constructor(db: Database) 30 | 31 | get isWriterRunning(): boolean 32 | 33 | enqueue( 34 | work: (_: ReaderInterface | WriterInterface) => Promise, 35 | description: string | undefined, 36 | isWriter: boolean, 37 | ): Promise 38 | 39 | subAction(work: () => Promise): Promise 40 | 41 | _executeNext(): Promise 42 | 43 | _abortPendingWork(): void 44 | } 45 | -------------------------------------------------------------------------------- /DatabaseProvider/DatabaseContext.d.ts: -------------------------------------------------------------------------------- 1 | import { Provider as ReactProvider, Consumer as ReactConsumer, Context } from 'react' 2 | 3 | type DatabaseContext = Context 4 | 5 | export type DatabaseConsumer = ReactConsumer 6 | export type Provider = ReactProvider 7 | 8 | export default DatabaseContext 9 | -------------------------------------------------------------------------------- /DatabaseProvider/DatabaseContext.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = exports.Provider = exports.DatabaseConsumer = void 0; 6 | var _react = _interopRequireDefault(require("react")); 7 | var DatabaseContext = _react.default.createContext(); 8 | var { 9 | Provider: Provider, 10 | Consumer: Consumer 11 | } = DatabaseContext; 12 | exports.DatabaseConsumer = Consumer; 13 | exports.Provider = Provider; 14 | var _default = DatabaseContext; 15 | exports.default = _default; -------------------------------------------------------------------------------- /DatabaseProvider/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | import Database from '../Database' 3 | 4 | export type Props = { 5 | database: Database 6 | children: ReactNode 7 | } 8 | 9 | /** 10 | * Database provider to create the database context 11 | * to allow child components to consume the database without prop drilling 12 | */ 13 | declare function DatabaseProvider({ children, database }: Props): JSX.Element 14 | 15 | export { default as withDatabase } from './withDatabase' 16 | export { default as DatabaseContext, DatabaseConsumer } from './DatabaseContext' 17 | export default DatabaseProvider 18 | -------------------------------------------------------------------------------- /DatabaseProvider/withDatabase.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { NonReactStatics } from 'hoist-non-react-statics' 3 | import type Database from '../Database' 4 | 5 | type WithDatabaseProps = T & { 6 | database: Database 7 | } 8 | 9 | type GetProps = C extends React.ComponentType ? P : never 10 | 11 | // HoC to inject the database into the props of consumers 12 | export default function withDatabase< 13 | C extends React.ComponentType, 14 | P = GetProps, 15 | R = Omit 16 | >(Component: C): React.FunctionComponent & NonReactStatics -------------------------------------------------------------------------------- /DatabaseProvider/withDatabase.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = withDatabase; 6 | var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); 7 | var _react = _interopRequireDefault(require("react")); 8 | var _hoistNonReactStatics = _interopRequireDefault(require("hoist-non-react-statics")); 9 | var _DatabaseContext = require("./DatabaseContext"); 10 | // HoC to inject the database into the props of consumers 11 | function withDatabase(Component) { 12 | return (0, _hoistNonReactStatics.default)(function (props) { 13 | return /*#__PURE__*/_react.default.createElement(_DatabaseContext.DatabaseConsumer, null, function (database) { 14 | return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({}, props, { 15 | database: database 16 | })); 17 | }); 18 | }, Component); 19 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Nozbe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Model/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import type Model from './index' 2 | import { $Exact } from '../types' 3 | 4 | type TimestampsObj = $Exact<{ created_at?: number; updated_at?: number }> 5 | export function createTimestampsFor(model: Model): TimestampsObj 6 | 7 | export function fetchDescendants(model: Model): Promise 8 | -------------------------------------------------------------------------------- /Query/helpers.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-named-as-default-member */ 2 | /* eslint-disable import/no-named-as-default */ 3 | // @flow 4 | 5 | import type Model from '../Model' 6 | import type Database from '../Database' 7 | import type { QueryDescription } from '../QueryDescription' 8 | 9 | import type { QueryAssociation } from './index' 10 | 11 | export function getAssociations( 12 | description: QueryDescription, 13 | modelClass: Model, 14 | db: Database, 15 | ): QueryAssociation[] 16 | -------------------------------------------------------------------------------- /QueryDescription/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import type { Clause, QueryDescription } from './type' 2 | 3 | export function buildQueryDescription(clauses: Clause[]): QueryDescription 4 | 5 | export function queryWithoutDeleted(query: QueryDescription): QueryDescription 6 | -------------------------------------------------------------------------------- /QueryDescription/index.d.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | NonNullValue, 3 | NonNullValues, 4 | Value, 5 | CompoundValue, 6 | Operator, 7 | ColumnDescription, 8 | ComparisonRight, 9 | Comparison, 10 | WhereDescription, 11 | SqlExpr, 12 | LokiExpr, 13 | Where, 14 | And, 15 | Or, 16 | On, 17 | SortOrder, 18 | SortBy, 19 | Take, 20 | Skip, 21 | JoinTables, 22 | NestedJoinTable, 23 | LokiTransformFunction, 24 | LokiTransform, 25 | SqlQuery, 26 | Clause, 27 | QueryDescription, 28 | } from './type' 29 | 30 | export { 31 | eq, 32 | notEq, 33 | gt, 34 | gte, 35 | weakGt, 36 | lt, 37 | lte, 38 | oneOf, 39 | notIn, 40 | between, 41 | like, 42 | notLike, 43 | sanitizeLikeString, 44 | includes, 45 | column, 46 | where, 47 | unsafeSqlExpr, 48 | unsafeLokiExpr, 49 | unsafeLokiTransform, 50 | and, 51 | or, 52 | asc, 53 | desc, 54 | sortBy, 55 | take, 56 | skip, 57 | on, 58 | ftsMatch, 59 | experimentalJoinTables, 60 | experimentalNestedJoin, 61 | unsafeSqlQuery, 62 | } from './operators' 63 | 64 | // NOTE: These probably shouldn't be exported from here, but kept for backwards compatibility 65 | export { buildQueryDescription, queryWithoutDeleted } from './helpers' 66 | -------------------------------------------------------------------------------- /QueryDescription/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cipher 2 | 3 | This is the build of a custom WMDB fork, that adds DB encryption capabilities 4 | to WMDB and FTS5. 5 | -------------------------------------------------------------------------------- /Relation/helpers.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-named-as-default-member */ 2 | /* eslint-disable import/no-named-as-default */ 3 | import type { Observable } from '../utils/rx' 4 | 5 | import type Relation from './index' 6 | import type Model from '../Model' 7 | 8 | export declare function createObservable(relation: Relation): Observable 9 | -------------------------------------------------------------------------------- /Relation/helpers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.createObservable = void 0; 5 | var _rx = require("../utils/rx"); 6 | var getImmutableObservable = function (relation) { 7 | return relation._model.collections.get(relation._relationTableName) 8 | // $FlowFixMe 9 | .findAndObserve(relation.id); 10 | }; 11 | var getObservable = function (relation) { 12 | return relation._model.observe() 13 | // $FlowFixMe 14 | .pipe((0, _rx.map)(function (model) { 15 | return model._getRaw(relation._columnName); 16 | }), (0, _rx.distinctUntilChanged)(), (0, _rx.switchMap)(function (id) { 17 | return id ? relation._model.collections.get(relation._relationTableName).findAndObserve(id) : (0, _rx.of)(null); 18 | })); 19 | }; 20 | 21 | // eslint-disable-next-line 22 | var createObservable = function (relation) { 23 | return relation._isImmutable ? getImmutableObservable(relation) : getObservable(relation); 24 | }; 25 | exports.createObservable = createObservable; -------------------------------------------------------------------------------- /Schema/migrations/getSyncChanges/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { SchemaMigrations } from '../index' 4 | import type { TableName, ColumnName, SchemaVersion } from '../../index' 5 | 6 | import { $Exact } from '../../../types' 7 | 8 | export type MigrationSyncChanges = $Exact<{ 9 | from: SchemaVersion 10 | tables: TableName[] 11 | columns: $Exact<{ 12 | table: TableName 13 | columns: ColumnName[] 14 | }>[] 15 | }> | null 16 | 17 | export default function getSyncChanges( 18 | migrations: SchemaMigrations, 19 | fromVersion: SchemaVersion, 20 | toVersion: SchemaVersion, 21 | ): MigrationSyncChanges 22 | -------------------------------------------------------------------------------- /Schema/migrations/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { $RE, $Exact } from '../../types' 2 | import type { ColumnSchema, TableName, TableSchema, TableSchemaSpec, SchemaVersion } from '../index' 3 | 4 | export type CreateTableMigrationStep = $RE<{ 5 | type: 'create_table' 6 | schema: TableSchema 7 | }> 8 | 9 | export type AddColumnsMigrationStep = $RE<{ 10 | type: 'add_columns' 11 | table: TableName 12 | columns: ColumnSchema[] 13 | unsafeSql?: (_: string) => string 14 | }> 15 | 16 | export type SqlMigrationStep = $RE<{ 17 | type: 'sql' 18 | sql: string 19 | }> 20 | 21 | export type MigrationStep = CreateTableMigrationStep | AddColumnsMigrationStep | SqlMigrationStep 22 | 23 | type Migration = $RE<{ 24 | toVersion: SchemaVersion 25 | steps: MigrationStep[] 26 | }> 27 | 28 | type SchemaMigrationsSpec = $RE<{ 29 | migrations: Migration[] 30 | }> 31 | 32 | export type SchemaMigrations = $RE<{ 33 | validated: true 34 | minVersion: SchemaVersion 35 | maxVersion: SchemaVersion 36 | sortedMigrations: Migration[] 37 | }> 38 | 39 | export function schemaMigrations(migrationSpec: SchemaMigrationsSpec): SchemaMigrations 40 | 41 | export function createTable(tableSchemaSpec: TableSchemaSpec): CreateTableMigrationStep 42 | 43 | export function addColumns({ 44 | table, 45 | columns, 46 | unsafeSql, 47 | }: $Exact<{ 48 | table: TableName 49 | columns: ColumnSchema[] 50 | unsafeSql?: (_: string) => string 51 | }>): AddColumnsMigrationStep 52 | 53 | export function unsafeExecuteSql(sql: string): SqlMigrationStep 54 | -------------------------------------------------------------------------------- /Schema/migrations/stepsForMigration.d.ts: -------------------------------------------------------------------------------- 1 | import { $Exact } from '../../types' 2 | 3 | import type { SchemaMigrations, MigrationStep } from './index' 4 | import type { SchemaVersion } from '../index' 5 | 6 | export function stepsForMigration({ 7 | migrations: schemaMigrations, 8 | fromVersion, 9 | toVersion, 10 | }: $Exact<{ 11 | migrations: SchemaMigrations 12 | fromVersion: SchemaVersion 13 | toVersion: SchemaVersion 14 | }>): MigrationStep[] | null 15 | -------------------------------------------------------------------------------- /Schema/migrations/stepsForMigration.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.stepsForMigration = stepsForMigration; 5 | var _fp = require("../../utils/fp"); 6 | function stepsForMigration({ 7 | migrations: schemaMigrations, 8 | fromVersion: fromVersion, 9 | toVersion: toVersion 10 | }) { 11 | var { 12 | sortedMigrations: sortedMigrations, 13 | minVersion: minVersion, 14 | maxVersion: maxVersion 15 | } = schemaMigrations; 16 | 17 | // see if migrations in this range are available 18 | if (fromVersion < minVersion || toVersion > maxVersion) { 19 | return null; 20 | } 21 | 22 | // return steps 23 | var matchingMigrations = sortedMigrations.filter(function ({ 24 | toVersion: version 25 | }) { 26 | return version > fromVersion && version <= toVersion; 27 | }); 28 | var allSteps = (0, _fp.unnest)(matchingMigrations.map(function (migration) { 29 | return migration.steps; 30 | })); 31 | return allSteps; 32 | } -------------------------------------------------------------------------------- /adapters/error.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); 7 | /* eslint-disable getter-return */ 8 | // Used as a placeholder during reset database to catch illegal 9 | // adapter calls 10 | var throwError = function (name) { 11 | throw new Error("Cannot call database.adapter.".concat(name, " while the database is being reset")); 12 | }; 13 | var ErrorAdapter = /*#__PURE__*/function () { 14 | function ErrorAdapter() { 15 | var _this = this; 16 | ['find', 'query', 'queryIds', 'count', 'batch', 'getDeletedRecords', 'destroyDeletedRecords', 'unsafeResetDatabase', 'getLocal', 'setLocal', 'removeLocal', 'testClone'].forEach(function (name) { 17 | // $FlowFixMe 18 | _this[name] = function () { 19 | return throwError(name); 20 | }; 21 | }); 22 | } 23 | (0, _createClass2.default)(ErrorAdapter, [{ 24 | key: "underlyingAdapter", 25 | get: function get() { 26 | throwError('underlyingAdapter'); 27 | } 28 | }, { 29 | key: "schema", 30 | get: function get() { 31 | throwError('schema'); 32 | } 33 | }, { 34 | key: "migrations", 35 | get: function get() { 36 | throwError('migrations'); 37 | } 38 | }]); 39 | return ErrorAdapter; 40 | }(); 41 | exports.default = ErrorAdapter; -------------------------------------------------------------------------------- /adapters/lokijs/common.d.ts: -------------------------------------------------------------------------------- 1 | import { type Result } from '../../utils/fp/Result' 2 | import type { CachedQueryResult, CachedFindResult } from '../type' 3 | import type { RecordId } from '../../Model' 4 | import { $Exact } from '../../types' 5 | 6 | export type WorkerExecutorType = 7 | | 'setUp' 8 | | 'find' 9 | | 'query' 10 | | 'queryIds' 11 | | 'unsafeQueryRaw' 12 | | 'count' 13 | | 'batch' 14 | | 'getDeletedRecords' 15 | | 'unsafeResetDatabase' 16 | | 'unsafeExecute' 17 | | 'getLocal' 18 | | 'setLocal' 19 | | 'removeLocal' 20 | | '_fatalError' 21 | | 'clearCachedRecords' 22 | 23 | export type WorkerExecutorPayload = any[] 24 | 25 | export type WorkerResponseData = CachedQueryResult | CachedFindResult | number | RecordId[] 26 | 27 | export type CloneMethod = 'shallowCloneDeepObjects' | 'immutable' | 'deep' 28 | 29 | export type WorkerAction = $Exact<{ 30 | id: number, 31 | type: WorkerExecutorType, 32 | payload: WorkerExecutorPayload, 33 | cloneMethod: CloneMethod, 34 | returnCloneMethod: CloneMethod, 35 | }> 36 | 37 | export type WorkerResponse = $Exact<{ 38 | id: number, 39 | result: Result, 40 | }> 41 | -------------------------------------------------------------------------------- /adapters/lokijs/common.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /adapters/lokijs/dispatcher.d.ts: -------------------------------------------------------------------------------- 1 | import type { ResultCallback } from '../../utils/fp/Result' 2 | import type { 3 | WorkerExecutorType, 4 | WorkerExecutorPayload, 5 | WorkerResponseData, 6 | CloneMethod, 7 | } from './common' 8 | 9 | type WorkerAction = { 10 | id: number, 11 | callback: ResultCallback, 12 | } 13 | type WorkerActions = WorkerAction[] 14 | 15 | 16 | export default class LokiDispatcher { 17 | _worker: Worker 18 | 19 | _pendingCalls: WorkerActions 20 | 21 | constructor(useWebWorker: boolean) 22 | 23 | // TODO: `any` return should be `WorkerResponsePayload` 24 | call( 25 | type: WorkerExecutorType, 26 | payload: WorkerExecutorPayload | undefined, 27 | callback: ResultCallback, 28 | // NOTE: This are used when not using web workers (otherwise, the data naturally is just copied) 29 | cloneMethod?: CloneMethod, 30 | returnCloneMethod?: CloneMethod, 31 | ): void 32 | } 33 | -------------------------------------------------------------------------------- /adapters/lokijs/type.d.ts: -------------------------------------------------------------------------------- 1 | export type Loki = any 2 | export type LokiCollection = any 3 | export type LokiResultset = any 4 | export type LokiMemoryAdapter = any 5 | -------------------------------------------------------------------------------- /adapters/lokijs/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /adapters/lokijs/worker/cloneMessage/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = cloneMessage; 5 | exports.shallowCloneDeepObjects = shallowCloneDeepObjects; 6 | // shallow-clones objects (without checking their contents), but copies arrays 7 | function shallowCloneDeepObjects(value) { 8 | if (Array.isArray(value)) { 9 | var returned = new Array(value.length); 10 | for (var i = 0, len = value.length; i < len; i += 1) { 11 | returned[i] = shallowCloneDeepObjects(value[i]); 12 | } 13 | return returned; 14 | } else if (value && 'object' === typeof value) { 15 | return Object.assign({}, value); 16 | } 17 | return value; 18 | } 19 | function cloneMessage(data) { 20 | // TODO: Even better, it would be great if we had zero-copy architecture (COW RawRecords?) and we didn't have to clone 21 | var method = data.cloneMethod; 22 | if ('shallowCloneDeepObjects' === method) { 23 | var clonedData = data; 24 | clonedData.payload = shallowCloneDeepObjects(clonedData.payload); 25 | return clonedData; 26 | } else if ('immutable' === method) { 27 | // we get a pinky promise that the payload is immutable so we don't need to copy 28 | return data; 29 | } 30 | throw new Error('Unknown data.clone method for cloneMessage'); 31 | } -------------------------------------------------------------------------------- /adapters/lokijs/worker/loki.worker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _DatabaseBridge = _interopRequireDefault(require("./DatabaseBridge")); 7 | /* eslint-disable no-restricted-globals */ 8 | var getDefaultExport = function () { 9 | self.workerClass = new _DatabaseBridge.default(self); 10 | return self; 11 | }; 12 | var _default = getDefaultExport(); 13 | exports.default = _default; -------------------------------------------------------------------------------- /adapters/lokijs/worker/synchronousWorker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _DatabaseBridge = _interopRequireDefault(require("./DatabaseBridge")); 7 | var _cloneMessage = _interopRequireDefault(require("./cloneMessage")); 8 | // Simulates the web worker API 9 | var SynchronousWorker = /*#__PURE__*/function () { 10 | function SynchronousWorker() { 11 | var _this = this; 12 | this.onmessage = function () {}; 13 | // $FlowFixMe 14 | this._workerContext = { 15 | postMessage: function postMessage(data) { 16 | _this.onmessage({ 17 | data: (0, _cloneMessage.default)(data) 18 | }); 19 | }, 20 | onmessage: function onmessage() {} 21 | }; 22 | // $FlowFixMe 23 | this._bridge = new _DatabaseBridge.default(this._workerContext); 24 | } 25 | var _proto = SynchronousWorker.prototype; 26 | _proto.postMessage = function postMessage(data) { 27 | this._workerContext.onmessage({ 28 | data: (0, _cloneMessage.default)(data) 29 | }); 30 | }; 31 | return SynchronousWorker; 32 | }(); 33 | exports.default = SynchronousWorker; -------------------------------------------------------------------------------- /adapters/sqlite/devtools.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | var _Query = _interopRequireDefault(require("../../Query")); 5 | var _encodeQuery = _interopRequireDefault(require("./encodeQuery")); 6 | // $FlowFixMe 7 | _Query.default.prototype._sql = function (count = false) { 8 | var query = this; 9 | var [sql] = (0, _encodeQuery.default)(query.serialize(), count); 10 | return sql; 11 | }; -------------------------------------------------------------------------------- /adapters/sqlite/encodeValue/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = encodeValue; 6 | var _sqlEscapeString = _interopRequireDefault(require("sql-escape-string")); 7 | var _common = require("../../../utils/common"); 8 | // Note: SQLite doesn't support literal TRUE and FALSE; expects 1 or 0 instead 9 | // It also doesn't encode strings the same way 10 | // Also: catches invalid values (undefined, NaN) early 11 | function encodeValue(value) { 12 | if (true === value) { 13 | return '1'; 14 | } else if (false === value) { 15 | return '0'; 16 | } else if (Number.isNaN(value)) { 17 | (0, _common.logError)('Passed NaN to query'); 18 | return 'null'; 19 | } else if (value === undefined) { 20 | (0, _common.logError)('Passed undefined to query'); 21 | return 'null'; 22 | } else if (null === value) { 23 | return 'null'; 24 | } else if ('number' === typeof value) { 25 | return "".concat(value); 26 | } else if ('string' === typeof value) { 27 | // TODO: We shouldn't ever encode SQL values directly — use placeholders 28 | return (0, _sqlEscapeString.default)(value); 29 | } 30 | throw new Error('Invalid value to encode into query'); 31 | } -------------------------------------------------------------------------------- /adapters/sqlite/makeDispatcher/decodeQueryResult/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = decodeQueryResult; 5 | // Compressed records have this syntax: 6 | // [ 7 | // ['id', 'body', ...], // 0: column names 8 | // ['foo', 'bar', ...], // values matching column names 9 | // 'id', // only cached id 10 | // ] 11 | function decodeQueryResult(compressedRecords) { 12 | var len = compressedRecords.length; 13 | if (!len) { 14 | return []; 15 | } 16 | var columnNames = compressedRecords[0]; 17 | var columnsLen = columnNames.length; 18 | var rawRecords = new Array(len - 1); 19 | var rawRecord, compressedRecord; 20 | for (var i = 1; i < len; i++) { 21 | compressedRecord = compressedRecords[i]; 22 | if ('string' === typeof compressedRecord) { 23 | rawRecord = compressedRecord; 24 | } else { 25 | rawRecord = {}; 26 | for (var j = 0; j < columnsLen; j++) { 27 | rawRecord[columnNames[j]] = compressedRecord[j]; 28 | } 29 | } 30 | rawRecords[i - 1] = rawRecord; 31 | } 32 | return rawRecords; 33 | } -------------------------------------------------------------------------------- /adapters/sqlite/makeDispatcher/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.getDispatcherType = getDispatcherType; 6 | exports.makeDispatcher = void 0; 7 | var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); 8 | var _DatabaseBridge = _interopRequireDefault(require("../sqlite-node/DatabaseBridge")); 9 | /* eslint-disable global-require */ 10 | var SqliteNodeDispatcher = /*#__PURE__*/function () { 11 | function SqliteNodeDispatcher(tag) { 12 | this._tag = tag; 13 | } 14 | var _proto = SqliteNodeDispatcher.prototype; 15 | _proto.call = function call(methodName, args, callback) { 16 | // $FlowFixMe 17 | var method = _DatabaseBridge.default[methodName].bind(_DatabaseBridge.default); 18 | method.apply(void 0, [this._tag].concat((0, _toConsumableArray2.default)(args), [function (value) { 19 | return callback({ 20 | value: value 21 | }); 22 | }, function (code, message, error) { 23 | return callback({ 24 | error: error 25 | }); 26 | }])); 27 | }; 28 | return SqliteNodeDispatcher; 29 | }(); 30 | var makeDispatcher = function (_type, tag) { 31 | return new SqliteNodeDispatcher(tag); 32 | }; 33 | exports.makeDispatcher = makeDispatcher; 34 | function getDispatcherType() { 35 | return 'asynchronous'; 36 | } -------------------------------------------------------------------------------- /adapters/sqlite/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /adapters/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /decorators/action/index.d.ts: -------------------------------------------------------------------------------- 1 | export const action: MethodDecorator 2 | 3 | export default action 4 | 5 | export const writer: MethodDecorator 6 | export const reader: MethodDecorator 7 | -------------------------------------------------------------------------------- /decorators/children/index.d.ts: -------------------------------------------------------------------------------- 1 | import { TableName } from '../../Schema' 2 | 3 | declare function children(childTable: TableName): PropertyDecorator 4 | export default children 5 | -------------------------------------------------------------------------------- /decorators/common.d.ts: -------------------------------------------------------------------------------- 1 | import type { ColumnName } from '../Schema' 2 | 3 | export function ensureDecoratorUsedProperly( 4 | columnName: ColumnName, 5 | target: Object, 6 | key: string, 7 | descriptor: Object, 8 | ): void 9 | -------------------------------------------------------------------------------- /decorators/common.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.ensureDecoratorUsedProperly = ensureDecoratorUsedProperly; 6 | var _invariant = _interopRequireDefault(require("../utils/common/invariant")); 7 | // eslint-disable-next-line 8 | function ensureDecoratorUsedProperly(columnName, target, key, descriptor) { 9 | (0, _invariant.default)(columnName, "Pass column name (raw field name) to the decorator - error in ".concat(target.constructor.name, ".prototype.").concat(key, " given.")); 10 | if (descriptor) { 11 | (0, _invariant.default)('initializer' in descriptor, "Model field decorators can only be used for simple properties - method, setter or getter ".concat(target.constructor.name, ".prototype.").concat(key, " given.")); 12 | (0, _invariant.default)('function' !== typeof descriptor.initializer, "Model field decorators must not be used on properties with a default value - error in \"".concat(target.constructor.name, ".prototype.").concat(key, "\".")); 13 | } 14 | } -------------------------------------------------------------------------------- /decorators/date/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ColumnName } from '../../Schema' 2 | 3 | declare function date(columnName: ColumnName): PropertyDecorator 4 | export default date 5 | -------------------------------------------------------------------------------- /decorators/field/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ColumnName } from '../../Schema' 2 | 3 | declare function field(columnName: ColumnName): PropertyDecorator 4 | export default field 5 | -------------------------------------------------------------------------------- /decorators/field/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _makeDecorator = _interopRequireDefault(require("../../utils/common/makeDecorator")); 7 | var _common = require("../common"); 8 | // Defines a model property 9 | // 10 | // Returns and sets values as-is, except that `undefined` and missing fields are normalized to `null` 11 | // If you have a more specific propety, use the correct decorator (@boolean, @text, etc.) 12 | // 13 | // Pass the database column name as an argument 14 | // 15 | // Example: 16 | // @field('some_field') someField 17 | var field = (0, _makeDecorator.default)(function (columnName) { 18 | return function (target, key, descriptor) { 19 | (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor); 20 | return { 21 | configurable: true, 22 | enumerable: true, 23 | get: function get() { 24 | // $FlowFixMe 25 | return this.asModel._getRaw(columnName); 26 | }, 27 | set: function set(value) { 28 | // $FlowFixMe 29 | this.asModel._setRaw(columnName, value); 30 | } 31 | }; 32 | }; 33 | }); 34 | var _default = field; 35 | exports.default = _default; -------------------------------------------------------------------------------- /decorators/immutableRelation/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ColumnName, TableName } from '../../Schema' 2 | 3 | declare function immutableRelation(relationTable: TableName, relationIdColumn: ColumnName): PropertyDecorator 4 | 5 | export default immutableRelation 6 | -------------------------------------------------------------------------------- /decorators/immutableRelation/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _relation = _interopRequireDefault(require("../relation")); 7 | // Defines a model property that fetches a record with a specific ID 8 | // The property defined must be *immutable*, i.e. the relation ID must never change 9 | // Returns an immutable Relation object. See watermelondb/Relation for more information 10 | // 11 | // If the property *can* change, use `relation` instead 12 | // 13 | // You can only assign a value inside a `collection.create()` or `collection.prepareCreate()` block 14 | // 15 | // relationIdColumn - name of the column with record ID 16 | // relationTable - name of the table containing desired recods 17 | // 18 | // Example: a Comment has an author (and an author can never change), so it may define: 19 | // @immutableRelation('team_member', 'author_id') author: Relation 20 | var immutableRelation = function (relationTable, relationIdColumn) { 21 | return (0, _relation.default)(relationTable, relationIdColumn, { 22 | isImmutable: true 23 | }); 24 | }; 25 | var _default = immutableRelation; 26 | exports.default = _default; -------------------------------------------------------------------------------- /decorators/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as action, writer, reader } from './action' 2 | export { default as children } from './children' 3 | export { default as json } from './json' 4 | export { default as nochange } from './nochange' 5 | export { default as field } from './field' 6 | export { default as date } from './date' 7 | export { default as text } from './text' 8 | export { default as readonly } from './readonly' 9 | export { default as lazy } from './lazy' 10 | export { default as relation } from './relation' 11 | export { default as immutableRelation } from './immutableRelation' 12 | -------------------------------------------------------------------------------- /decorators/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.writer = exports.text = exports.relation = exports.readonly = exports.reader = exports.nochange = exports.lazy = exports.json = exports.immutableRelation = exports.field = exports.date = exports.children = void 0; 6 | var _action = require("./action"); 7 | exports.writer = _action.writer; 8 | exports.reader = _action.reader; 9 | var _children = _interopRequireDefault(require("./children")); 10 | exports.children = _children.default; 11 | var _json = _interopRequireDefault(require("./json")); 12 | exports.json = _json.default; 13 | var _nochange = _interopRequireDefault(require("./nochange")); 14 | exports.nochange = _nochange.default; 15 | var _field = _interopRequireDefault(require("./field")); 16 | exports.field = _field.default; 17 | var _date = _interopRequireDefault(require("./date")); 18 | exports.date = _date.default; 19 | var _text = _interopRequireDefault(require("./text")); 20 | exports.text = _text.default; 21 | var _readonly = _interopRequireDefault(require("./readonly")); 22 | exports.readonly = _readonly.default; 23 | var _lazy = _interopRequireDefault(require("./lazy")); 24 | exports.lazy = _lazy.default; 25 | var _relation = _interopRequireDefault(require("./relation")); 26 | exports.relation = _relation.default; 27 | var _immutableRelation = _interopRequireDefault(require("./immutableRelation")); 28 | exports.immutableRelation = _immutableRelation.default; -------------------------------------------------------------------------------- /decorators/json/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ColumnName } from '../../Schema' 2 | import Model from '../../Model' 3 | 4 | export type Sanitizer = (source: any, model?: Model) => any 5 | 6 | declare function json(rawFieldName: ColumnName, sanitizer: Sanitizer): PropertyDecorator 7 | 8 | export default json 9 | -------------------------------------------------------------------------------- /decorators/lazy/index.d.ts: -------------------------------------------------------------------------------- 1 | // Copied from lib.es5.d.ts, PropertyDecorator 2 | declare function lazy(target: Object, propertyKey: string | symbol): void 3 | declare function lazy(): PropertyDecorator 4 | export default lazy 5 | -------------------------------------------------------------------------------- /decorators/lazy/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = lazy; 5 | // Defines a property whose value is evaluated the first time it is accessed 6 | // For example: 7 | // 8 | // class X { 9 | // @lazy date = new Date() 10 | // } 11 | // 12 | // `date` will be set to the current date not when constructed, but only when `xx.date` is called. 13 | // All subsequent calls will return the same value 14 | function lazy(target, key, descriptor) { 15 | var { 16 | configurable: configurable, 17 | enumerable: enumerable, 18 | initializer: initializer, 19 | value: value 20 | } = descriptor; 21 | return { 22 | configurable: configurable, 23 | enumerable: enumerable, 24 | get: function get() { 25 | // $FlowFixMe 26 | var that = this; 27 | // This happens if someone accesses the 28 | // property directly on the prototype 29 | if (that === target) { 30 | return undefined; 31 | } 32 | var returnValue = initializer ? initializer.call(that) : value; 33 | 34 | // Next time this property is called, skip the decorator, and just return the precomputed value 35 | Object.defineProperty(that, key, { 36 | configurable: configurable, 37 | enumerable: enumerable, 38 | writable: true, 39 | value: returnValue 40 | }); 41 | return returnValue; 42 | } // TODO: What should be the behavior on set? 43 | }; 44 | } 45 | 46 | // Implementation inspired by lazyInitialize from `core-decorators` -------------------------------------------------------------------------------- /decorators/nochange/index.d.ts: -------------------------------------------------------------------------------- 1 | // Copied from lib.es5.d.ts, PropertyDecorator 2 | declare function nochange(target: Object, propertyKey: string | symbol): void 3 | declare function nochange(): PropertyDecorator 4 | 5 | export default nochange 6 | -------------------------------------------------------------------------------- /decorators/nochange/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); 7 | var _makeDecorator = _interopRequireDefault(require("../../utils/common/makeDecorator")); 8 | var _invariant = _interopRequireDefault(require("../../utils/common/invariant")); 9 | // Marks a model field as immutable after create — you can set and change the value in 10 | // create() and prepareCreate(), but after it's saved to the database, it cannot be changed 11 | var nochange = (0, _makeDecorator.default)(function () { 12 | return function (target, key, descriptor) { 13 | (0, _invariant.default)(descriptor.set, "@nochange can only be applied to model fields (to properties with a setter)"); 14 | var errorMessage = "Attempt to set a new value on a @nochange field: ".concat(target.constructor.name, ".prototype.").concat(key); 15 | return (0, _extends2.default)({}, descriptor, { 16 | set: function set(value) { 17 | // $FlowFixMe 18 | var model = this; 19 | (0, _invariant.default)('create' === model.asModel._preparedState, errorMessage); 20 | descriptor.set.call(model, value); 21 | } 22 | }); 23 | }; 24 | }); 25 | var _default = nochange; 26 | exports.default = _default; -------------------------------------------------------------------------------- /decorators/readonly/index.d.ts: -------------------------------------------------------------------------------- 1 | // Copied from lib.es5.d.ts, PropertyDecorator 2 | declare function readonly(target: Object, propertyKey: string | symbol): void 3 | declare function readonly(): PropertyDecorator 4 | 5 | export default readonly 6 | -------------------------------------------------------------------------------- /decorators/readonly/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); 7 | var _makeDecorator = _interopRequireDefault(require("../../utils/common/makeDecorator")); 8 | var _invariant = _interopRequireDefault(require("../../utils/common/invariant")); 9 | // Marks a field as non-writable (throws an error when attempting to set a new value) 10 | // When using multiple decorators, remember to mark as @readonly *last* (leftmost) 11 | var readonly = (0, _makeDecorator.default)(function () { 12 | return function (target, key, descriptor) { 13 | // Set a new setter on getter/setter fields 14 | if (descriptor.get || descriptor.set) { 15 | return (0, _extends2.default)({}, descriptor, { 16 | set: function set() { 17 | (0, _invariant.default)(false, "Attempt to set new value on a property ".concat(target.constructor.name, ".prototype.").concat(key, " marked as @readonly")); 18 | } 19 | }); 20 | } 21 | 22 | // Mark as writable=false for simple fields 23 | descriptor.writable = false; 24 | return descriptor; 25 | }; 26 | }); 27 | var _default = readonly; 28 | exports.default = _default; -------------------------------------------------------------------------------- /decorators/relation/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ColumnName, TableName } from '../../Schema' 2 | import { Options } from '../../Relation' 3 | 4 | declare function relation( 5 | relationTable: TableName, 6 | relationIdColumn: ColumnName, 7 | options?: Options, 8 | ): PropertyDecorator 9 | 10 | export default relation 11 | -------------------------------------------------------------------------------- /decorators/text/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ColumnName } from '../../Schema' 2 | 3 | declare function text(columnName: ColumnName): PropertyDecorator 4 | 5 | export default text 6 | -------------------------------------------------------------------------------- /decorators/text/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _makeDecorator = _interopRequireDefault(require("../../utils/common/makeDecorator")); 7 | var _common = require("../common"); 8 | // Defines a model property representing user-input text 9 | // 10 | // On set, all strings are trimmed (whitespace is removed from beginning/end) 11 | // and all non-string values are converted to strings 12 | // (Except null which is passed as-is) 13 | // 14 | // Pass the database column name as an argument 15 | // 16 | // Examples: 17 | // @text(Column.name) name: string 18 | // @text('full_description') fullDescription: string 19 | var text = (0, _makeDecorator.default)(function (columnName) { 20 | return function (target, key, descriptor) { 21 | (0, _common.ensureDecoratorUsedProperly)(columnName, target, key, descriptor); 22 | return { 23 | configurable: true, 24 | enumerable: true, 25 | get: function get() { 26 | // $FlowFixMe 27 | return this.asModel._getRaw(columnName); 28 | }, 29 | set: function set(value) { 30 | // $FlowFixMe 31 | this.asModel._setRaw(columnName, 'string' === typeof value ? value.trim() : null); 32 | } 33 | }; 34 | }; 35 | }); 36 | var _default = text; 37 | exports.default = _default; -------------------------------------------------------------------------------- /hooks/index.d.ts: -------------------------------------------------------------------------------- 1 | import type Database from '../Database' 2 | 3 | export function useDatabase(): Database 4 | -------------------------------------------------------------------------------- /hooks/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.useDatabase = void 0; 5 | var _useDatabase = require("./use-database"); 6 | exports.useDatabase = _useDatabase.useDatabase; -------------------------------------------------------------------------------- /hooks/use-database.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.useDatabase = useDatabase; 6 | var _react = _interopRequireDefault(require("react")); 7 | var _DatabaseProvider = require("../DatabaseProvider"); 8 | var _invariant = _interopRequireDefault(require("../utils/common/invariant")); 9 | function useDatabase() { 10 | var database = _react.default.useContext(_DatabaseProvider.DatabaseContext); 11 | (0, _invariant.default)(database, 'Could not find database context, please make sure the component is wrapped in the '); 12 | return database; 13 | } -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as Q from './QueryDescription' 2 | 3 | export { default as Collection } from './Collection' 4 | export { default as Database } from './Database' 5 | export { default as Relation } from './Relation' 6 | export { default as Model, associations } from './Model' 7 | export { default as Query } from './Query' 8 | export { tableName, columnName, appSchema, tableSchema } from './Schema' 9 | 10 | export type { default as CollectionMap } from './Database/CollectionMap' 11 | 12 | export type { LocalStorageKey } from './Database/LocalStorage' 13 | export { localStorageKey } from './Database/LocalStorage' 14 | 15 | export type { DatabaseAdapter } from './adapters/type' 16 | export type { RawRecord, DirtyRaw } from './RawRecord' 17 | export type { RecordId } from './Model' 18 | export type { 19 | TableName, 20 | ColumnName, 21 | ColumnType, 22 | ColumnSchema, 23 | TableSchema, 24 | AppSchema, 25 | } from './Schema' 26 | export type { SchemaMigrations } from './Schema/migrations' 27 | 28 | export { Q } 29 | -------------------------------------------------------------------------------- /native/android-jsi/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .cxx 11 | 12 | testPath* 13 | -------------------------------------------------------------------------------- /native/android-jsi/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /native/android-jsi/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /native/android-jsi/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /native/android-jsi/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /native/android-jsi/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /native/android-jsi/src/main/cpp/DatabasePlatformAndroid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace watermelondb { 6 | namespace platform { 7 | 8 | void configureJNI(JNIEnv *env); 9 | void provideJson(int id, jbyteArray array); 10 | void destroy(); 11 | 12 | } // namespace platform 13 | } // namespace watermelondb 14 | -------------------------------------------------------------------------------- /native/android-jsi/src/main/cpp/JSIInstaller.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "Database.h" 6 | #include "DatabasePlatformAndroid.h" 7 | 8 | using namespace facebook; 9 | 10 | extern "C" JNIEXPORT void JNICALL Java_com_nozbe_watermelondb_jsi_JSIInstaller_installBinding(JNIEnv *env, jobject thiz, jlong runtimePtr) { 11 | jsi::Runtime *runtime = (jsi::Runtime *)runtimePtr; 12 | assert(runtime != nullptr); 13 | watermelondb::platform::configureJNI(env); 14 | watermelondb::Database::install(runtime); 15 | } 16 | 17 | extern "C" JNIEXPORT void JNICALL Java_com_nozbe_watermelondb_jsi_JSIInstaller_provideSyncJson(JNIEnv *env, jclass clazz, jint id, jbyteArray array) { 18 | watermelondb::platform::provideJson(id, array); 19 | } 20 | 21 | extern "C" JNIEXPORT void JNICALL Java_com_nozbe_watermelondb_jsi_JSIInstaller_destroy(JNIEnv *env, jclass clazz) { 22 | watermelondb::platform::destroy(); 23 | } 24 | -------------------------------------------------------------------------------- /native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb.jsi; 2 | 3 | import android.content.Context; 4 | class JSIInstaller { 5 | static void install(Context context, long javaScriptContextHolder) { 6 | JSIInstaller.context = context; 7 | new JSIInstaller().installBinding(javaScriptContextHolder); 8 | 9 | // call methods we're going to need from JNI - if we don't, Proguard/R8 will strip it from 10 | // release binaries. We could use @Keep or configure Proguard to keep it but that would be 11 | // error prone for lib users 12 | _resolveDatabasePath(""); 13 | } 14 | 15 | // Helper method called from C++ 16 | static String _resolveDatabasePath(String dbName) { 17 | // On some systems there is some kind of lock on `/databases` folder ¯\_(ツ)_/¯ 18 | return context.getDatabasePath(dbName + ".db").getPath().replace("/databases", ""); 19 | } 20 | 21 | private native void installBinding(long javaScriptContextHolder); 22 | 23 | static native void provideSyncJson(int id, byte[] json); 24 | 25 | static native void destroy(); 26 | 27 | private static Context context; 28 | 29 | static { 30 | System.loadLibrary("watermelondb-jsi"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/WatermelonDBJSIPackage.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb.jsi; 2 | 3 | import com.facebook.react.bridge.JSIModulePackage; 4 | import com.facebook.react.bridge.JSIModuleSpec; 5 | import com.facebook.react.bridge.JavaScriptContextHolder; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class WatermelonDBJSIPackage implements JSIModulePackage { 12 | @Override 13 | public List getJSIModules(ReactApplicationContext reactApplicationContext, JavaScriptContextHolder jsContextHolder) { 14 | synchronized(jsContextHolder) { 15 | JSIInstaller.install(reactApplicationContext.getApplicationContext(), jsContextHolder.get()); 16 | } 17 | return Arrays.asList(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/WatermelonJSI.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb.jsi; 2 | 3 | import android.app.Application; 4 | 5 | // Public interface to JSI-based Watermelon 6 | public class WatermelonJSI { 7 | public static void onTrimMemory(int level) { 8 | // TODO: Unimplemented 9 | } 10 | 11 | public static void provideSyncJson(int id, byte[] json) { 12 | JSIInstaller.provideSyncJson(id, json); 13 | } 14 | 15 | public static void onCatalystInstanceDestroy() { 16 | JSIInstaller.destroy(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /native/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | 11 | testPath* 12 | -------------------------------------------------------------------------------- /native/android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /native/android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /native/android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /native/android/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /native/android/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /native/android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /native/android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /native/android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | ext.getExtOrDefault = {name -> 4 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeWatermelonDB_' + name] 5 | } 6 | 7 | ext.getExtOrIntegerDefault = {name -> 8 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativeWatermelonDB_' + name]).toInteger() 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}") 17 | } 18 | } 19 | 20 | apply plugin: 'com.android.library' 21 | apply plugin: 'kotlin-android' 22 | 23 | android { 24 | compileSdkVersion getExtOrIntegerDefault('compileSdkVersion') 25 | buildToolsVersion getExtOrDefault('buildToolsVersion') 26 | 27 | defaultConfig { 28 | minSdkVersion getExtOrIntegerDefault('minSdkVersion') 29 | targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') 30 | versionCode 1 31 | versionName "1.0" 32 | } 33 | } 34 | 35 | dependencies { 36 | //noinspection GradleDynamicVersion 37 | implementation 'com.facebook.react:react-native:+' 38 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getExtOrDefault('kotlinVersion')}" 39 | } 40 | 41 | repositories { 42 | mavenCentral() 43 | } 44 | 45 | -------------------------------------------------------------------------------- /native/android/gradle.properties: -------------------------------------------------------------------------------- 1 | ReactNativeWatermelonDB_kotlinVersion=1.3.50 2 | ReactNativeWatermelonDB_compileSdkVersion=31 3 | ReactNativeWatermelonDB_buildToolsVersion=28.0.3 4 | ReactNativeWatermelonDB_targetSdkVersion=28 5 | ReactNativeWatermelonDB_minSdkVersion=16 6 | -------------------------------------------------------------------------------- /native/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/Connection.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb; 2 | 3 | import java.util.ArrayList; 4 | 5 | public abstract class Connection { 6 | public static class Connected extends Connection { 7 | public final WMDatabaseDriver driver; 8 | public Connected(WMDatabaseDriver driver) { 9 | this.driver = driver; 10 | } 11 | } 12 | 13 | public static class Waiting extends Connection { 14 | public final ArrayList queueInWaiting; 15 | public Waiting(ArrayList queueInWaiting) { 16 | this.queueInWaiting = queueInWaiting; 17 | } 18 | } 19 | 20 | public ArrayList getQueue() { 21 | if (this instanceof Connected) { 22 | return new ArrayList<>(); 23 | } else if (this instanceof Waiting) { 24 | return ((Waiting) this).queueInWaiting; 25 | } 26 | return null; 27 | } 28 | } -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/MigrationNeededError.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb; 2 | 3 | public class MigrationNeededError extends RuntimeException { 4 | public int databaseVersion; 5 | 6 | public MigrationNeededError(int databaseVersion) { 7 | this.databaseVersion = databaseVersion; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/Queries.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb; 2 | 3 | public class Queries { 4 | public static final String select_local_storage = "select value from local_storage where key = ?"; 5 | public static final String select_tables = "select * from sqlite_master where type='table'"; 6 | public static String dropTable(String table) { 7 | return "drop table if exists `" + table + "`"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/SchemaNeededError.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb; 2 | 3 | public class SchemaNeededError extends RuntimeException { 4 | } 5 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/WatermelonDBPackage.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.facebook.react.ReactPackage; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class WatermelonDBPackage implements ReactPackage { 13 | @NonNull 14 | @Override 15 | public List createNativeModules(@NonNull ReactApplicationContext reactAppContext) { 16 | List modules = new ArrayList<>(); 17 | modules.add(new WMDatabaseBridge(reactAppContext)); 18 | return modules; 19 | } 20 | 21 | @NonNull 22 | @Override 23 | public List createViewManagers(@NonNull ReactApplicationContext reactAppContext) { 24 | return Collections.emptyList(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/utils/MigrationSet.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb.utils; 2 | 3 | public class MigrationSet { 4 | public int from; 5 | public int to; 6 | public String sql; 7 | 8 | public MigrationSet(int from, int to, String sql) { 9 | this.from = from; 10 | this.to = to; 11 | this.sql = sql; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/utils/Pair.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb.utils; 2 | 3 | public class Pair { 4 | public K first; 5 | public V second; 6 | 7 | private Pair(K key, V value) { 8 | first = key; 9 | second = value; 10 | } 11 | 12 | public static Pair create(K key, V value) { 13 | return new Pair<>(key, value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /native/android/src/main/java/com/nozbe/watermelondb/utils/Schema.java: -------------------------------------------------------------------------------- 1 | package com.nozbe.watermelondb.utils; 2 | 3 | public class Schema { 4 | public int version; 5 | public String sql; 6 | 7 | public Schema(int version, String sql) { 8 | this.version = version; 9 | this.sql = sql; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /native/ios/WatermelonDB/FMDB/LICENSE.txt: -------------------------------------------------------------------------------- 1 | If you are using FMDB in your project, I'd love to hear about it. Let Gus know 2 | by sending an email to gus@flyingmeat.com. 3 | 4 | And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you 5 | might consider purchasing a drink of their choosing if FMDB has been useful to 6 | you. 7 | 8 | Finally, and shortly, this is the MIT License. 9 | 10 | Copyright (c) 2008-2014 Flying Meat Inc. 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE. -------------------------------------------------------------------------------- /native/ios/WatermelonDB/FMDB/src/fmdb/FMDB.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT double FMDBVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char FMDBVersionString[]; 5 | 6 | #import "FMDatabase.h" 7 | #import "FMResultSet.h" 8 | #import "FMDatabaseAdditions.h" 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabasePool.h" 11 | -------------------------------------------------------------------------------- /native/ios/WatermelonDB/JSIInstaller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #import 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif 9 | 10 | void installWatermelonJSI(RCTCxxBridge *bridge); 11 | void watermelondbProvideSyncJson(int id, NSData *json, NSError **errorPtr); 12 | 13 | #ifdef __cplusplus 14 | } // extern "C" 15 | #endif 16 | -------------------------------------------------------------------------------- /native/ios/WatermelonDB/JSIInstaller.mm: -------------------------------------------------------------------------------- 1 | #import "JSIInstaller.h" 2 | #import "Database.h" 3 | 4 | extern "C" void installWatermelonJSI(RCTCxxBridge *bridge) { 5 | if (bridge.runtime == nullptr) { 6 | return; 7 | } 8 | 9 | jsi::Runtime *runtime = (jsi::Runtime*) bridge.runtime; 10 | assert(runtime != nullptr); 11 | watermelondb::Database::install(runtime); 12 | } 13 | -------------------------------------------------------------------------------- /native/ios/WatermelonDB/WatermelonDB.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #import "./JSIInstaller.h" 4 | -------------------------------------------------------------------------------- /native/ios/WatermelonDB/objc/WMDatabase.h: -------------------------------------------------------------------------------- 1 | #import "FMDB.h" 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface WMDatabase : NSObject 6 | 7 | @property (readwrite, strong, nonatomic) FMDatabase *fmdb; 8 | @property (readwrite, nonatomic) long userVersion; 9 | 10 | #pragma mark - Initialization 11 | 12 | + (instancetype) databaseWithPath:(NSString *)path; 13 | 14 | #pragma mark - Executing queries 15 | 16 | - (BOOL) executeQuery:(NSString *)query args:(NSArray *)args error:(NSError **)errorPtr; 17 | /// Executes multiple queries separated by `;` 18 | - (BOOL) executeStatements:(NSString *)sql error:(NSError **)errorPtr; 19 | - (FMResultSet *) queryRaw:(NSString *)query args:(NSArray *)args error:(NSError **)errorPtr; 20 | - (NSNumber * _Nullable) count:(NSString *)query args:(NSArray *)args error:(NSError **)errorPtr; 21 | 22 | #pragma mark - Other database functions 23 | 24 | - (BOOL) inTransaction:(BOOL (^)(NSError**))transactionBlock error:(NSError**)errorPtr; 25 | - (BOOL) unsafeDestroyEverything:(NSError**)errorPtr; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /native/ios/WatermelonDB/objc/WMDatabaseBridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface WMDatabaseBridge : NSObject 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /native/shared/Database-jsi.cpp: -------------------------------------------------------------------------------- 1 | #include "Database.h" 2 | 3 | namespace watermelondb { 4 | 5 | using platform::consoleError; 6 | using platform::consoleLog; 7 | 8 | jsi::Runtime &Database::getRt() { 9 | return *runtime_; 10 | } 11 | 12 | jsi::JSError Database::dbError(std::string description) { 13 | // TODO: In serialized threading mode, those may be incorrect - probably smarter to pass result codes around? 14 | auto sqliteMessage = std::string(sqlite3_errmsg(db_->sqlite)); 15 | auto code = sqlite3_extended_errcode(db_->sqlite); 16 | auto message = description + " - sqlite error " + std::to_string(code) + " (" + sqliteMessage + ")"; 17 | // Note: logging to console in case another exception is thrown so that the original error isn't lost 18 | consoleError(message); 19 | 20 | auto &rt = getRt(); 21 | return jsi::JSError(rt, message); 22 | } 23 | 24 | jsi::Array Database::arrayFromStd(std::vector &vector) { 25 | // FIXME: Adding directly to a jsi::Array should be more efficient, but Hermes does not support 26 | // automatically resizing an Array by setting new values to it 27 | auto &rt = getRt(); 28 | jsi::Array array(rt, vector.size()); 29 | size_t i = 0; 30 | for (auto const &value : vector) { 31 | array.setValueAtIndex(rt, i, value); 32 | i++; 33 | } 34 | return array; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /native/shared/DatabasePlatform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Database.h" 5 | 6 | namespace watermelondb { 7 | namespace platform { 8 | 9 | // Logs to console 10 | void consoleLog(std::string message); 11 | 12 | // Logs error to console 13 | void consoleError(std::string message); 14 | 15 | // Called before a Sqlite object is constructed 16 | // Use to initialize sqlite, if necessary 17 | void initializeSqlite(); 18 | 19 | // Given a database name, returns a fully-qualified default database path 20 | // e.g. /Users/foo.app/.db 21 | std::string resolveDatabasePath(std::string path); 22 | 23 | // Removes database file located at `path`. 24 | // Throws an exception if it's not possible to delete this file 25 | void deleteDatabaseFile(std::string path, bool warnIfDoesNotExist); 26 | 27 | // Calls function when device memory is getting low 28 | void onMemoryAlert(std::function callback); 29 | 30 | // Returns sync json provided by the user 31 | std::string_view getSyncJson(int id); 32 | 33 | // Destroys sync json after it's used 34 | void deleteSyncJson(int id); 35 | 36 | // Called when React Native bridge is being torn down 37 | void onDestroy(std::function callback); 38 | 39 | } // namespace platform 40 | } // namespace watermelondb 41 | -------------------------------------------------------------------------------- /native/shared/Sqlite.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #import 4 | #ifdef SQLITE_HAS_CODEC 5 | #import "sqlite3.h" 6 | #else 7 | #import 8 | #endif 9 | 10 | namespace watermelondb { 11 | 12 | // Lightweight wrapper for handling sqlite3 lifetime 13 | class SqliteDb { 14 | public: 15 | SqliteDb(std::string path, const char *password); 16 | ~SqliteDb(); 17 | void destroy(); 18 | 19 | sqlite3 *sqlite; 20 | 21 | SqliteDb &operator=(const SqliteDb &) = delete; 22 | SqliteDb(const SqliteDb &) = delete; 23 | 24 | private: 25 | bool isDestroyed_; 26 | }; 27 | 28 | class SqliteStatement { 29 | public: 30 | SqliteStatement(sqlite3_stmt *statement); 31 | ~SqliteStatement(); 32 | 33 | sqlite3_stmt *stmt; 34 | 35 | void reset(); 36 | }; 37 | 38 | } // namespace watermelondb 39 | -------------------------------------------------------------------------------- /observation/encodeMatcher/canEncode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = canEncodeMatcher; 5 | exports.forbiddenError = void 0; 6 | var forbiddenError = "Queries with joins, sortBy, take, skip, lokiTransform can't be encoded into a matcher"; 7 | exports.forbiddenError = forbiddenError; 8 | function canEncodeMatcher(query) { 9 | var { 10 | joinTables: joinTables, 11 | nestedJoinTables: nestedJoinTables, 12 | sortBy: sortBy, 13 | take: take, 14 | skip: skip, 15 | lokiTransform: lokiTransform, 16 | sql: sql 17 | } = query; 18 | return !joinTables.length && !nestedJoinTables.length && !sortBy.length && !take && !skip && !lokiTransform && !sql; 19 | } -------------------------------------------------------------------------------- /observation/subscribeToCount/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-named-as-default-member */ 2 | /* eslint-disable import/no-named-as-default */ 3 | 4 | import type { Unsubscribe } from '../../utils/subscriptions' 5 | 6 | import type Query from '../../Query' 7 | import type Model from '../../Model' 8 | 9 | export function experimentalDisableObserveCountThrottling(): void 10 | 11 | // Produces an observable version of a query count by re-querying the database 12 | // when any change occurs in any of the relevant Stores. 13 | // 14 | // TODO: Potential optimizations: 15 | // - increment/decrement counter using matchers on insert/delete 16 | export default function subscribeToCount( 17 | query: Query, 18 | isThrottled: boolean, 19 | subscriber: (_: number) => void, 20 | ): Unsubscribe 21 | -------------------------------------------------------------------------------- /observation/subscribeToQuery.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-named-as-default-member */ 2 | /* eslint-disable import/no-named-as-default */ 3 | import type { Unsubscribe } from '../utils/subscriptions' 4 | 5 | import type Query from '../Query' 6 | import type Model from '../Model' 7 | 8 | export default function subscribeToQuery( 9 | query: Query, 10 | subscriber: (records: Record[]) => void, 11 | ): Unsubscribe 12 | -------------------------------------------------------------------------------- /observation/subscribeToQuery.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = subscribeToQuery; 6 | var _subscribeToQueryReloading = _interopRequireDefault(require("./subscribeToQueryReloading")); 7 | var _subscribeToSimpleQuery = _interopRequireDefault(require("./subscribeToSimpleQuery")); 8 | var _canEncode = _interopRequireDefault(require("./encodeMatcher/canEncode")); 9 | function subscribeToQuery(query, subscriber) { 10 | return (0, _canEncode.default)(query.description) ? (0, _subscribeToSimpleQuery.default)(query, subscriber) : (0, _subscribeToQueryReloading.default)(query, subscriber); 11 | } -------------------------------------------------------------------------------- /observation/subscribeToQueryReloading/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-named-as-default-member */ 2 | /* eslint-disable import/no-named-as-default */ 3 | import type { Unsubscribe } from '../../utils/subscriptions' 4 | 5 | import type Query from '../../Query' 6 | import type Model from '../../Model' 7 | 8 | // Produces an observable version of a query by re-querying the database 9 | // when any change occurs in any of the relevant Stores. 10 | // This is inefficient for simple queries, but necessary for complex queries 11 | export default function subscribeToQueryReloading( 12 | query: Query, 13 | subscriber: (records: Record[]) => void, 14 | // Emits `false` when query fetch begins + always emits even if no change - internal trick needed 15 | // by observeWithColumns 16 | shouldEmitStatus?: boolean, 17 | ): Unsubscribe 18 | -------------------------------------------------------------------------------- /observation/subscribeToSimpleQuery/processChangeSet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = processChangeSet; 5 | // WARN: Mutates arguments 6 | function processChangeSet(changeSet, matcher, mutableMatchingRecords) { 7 | var shouldEmit = false; 8 | changeSet.forEach(function (change) { 9 | var { 10 | record: record, 11 | type: type 12 | } = change; 13 | var index = mutableMatchingRecords.indexOf(record); 14 | var currentlyMatching = -1 < index; 15 | if ('destroyed' === type) { 16 | if (currentlyMatching) { 17 | // Remove if record was deleted 18 | mutableMatchingRecords.splice(index, 1); 19 | shouldEmit = true; 20 | } 21 | return; 22 | } 23 | var matches = matcher(record._raw); 24 | if (currentlyMatching && !matches) { 25 | // Remove if doesn't match anymore 26 | mutableMatchingRecords.splice(index, 1); 27 | shouldEmit = true; 28 | } else if (matches && !currentlyMatching) { 29 | // Add if should be included but isn't 30 | mutableMatchingRecords.push(record); 31 | shouldEmit = true; 32 | } 33 | }); 34 | return shouldEmit; 35 | } -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // This is for auto-linking WatermelonDB as a library 3 | dependency: { 4 | platforms: { 5 | android: { 6 | sourceDir: './native/android', 7 | }, 8 | }, 9 | }, 10 | // This is for WatermelonDB project internals 11 | project: { 12 | android: { 13 | sourceDir: './native/androidTest', 14 | }, 15 | ios: { 16 | sourceDir: './native/iosTest', 17 | }, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /src/Database/CollectionMap/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type Model from '../../Model' 4 | import Collection from '../../Collection' 5 | import type { TableName } from '../../Schema' 6 | import type Database from '../index' 7 | 8 | import { invariant } from '../../utils/common' 9 | 10 | export default class CollectionMap { 11 | map: { [TableName]: Collection } 12 | 13 | constructor(db: Database, modelClasses: Class[]): void { 14 | this.map = (Object.create(null): any) 15 | modelClasses.forEach((modelClass) => { 16 | const { table } = modelClass 17 | if (process.env.NODE_ENV !== 'production') { 18 | // TODO: move these checks to Collection? 19 | invariant( 20 | typeof table === 'string', 21 | `Model class ${modelClass.name} passed to Database constructor is missing "static table = 'table_name'"`, 22 | ) 23 | invariant( 24 | db.schema.tables[table], 25 | `Model class ${modelClass.name} has static table defined that is missing in schema known by this database`, 26 | ) 27 | } 28 | this.map[table] = new Collection(db, modelClass) 29 | }) 30 | Object.freeze(this.map) 31 | } 32 | 33 | get(tableName: TableName): Collection { 34 | return (this.map[tableName] || null: any) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DatabaseProvider/DatabaseContext.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react' 3 | 4 | const DatabaseContext = (React.createContext(): any) 5 | const { Provider, Consumer } = DatabaseContext 6 | 7 | export { Consumer as DatabaseConsumer, Provider } 8 | 9 | export default DatabaseContext 10 | -------------------------------------------------------------------------------- /src/DatabaseProvider/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react' 3 | import Database from '../Database' 4 | import { Provider } from './DatabaseContext' 5 | 6 | export type Props = { 7 | database: Database, 8 | children: React$Node, 9 | } 10 | 11 | /** 12 | * Database provider to create the database context 13 | * to allow child components to consume the database without prop drilling 14 | */ 15 | function DatabaseProvider({ children, database }: Props): React$Element { 16 | if (!(database instanceof Database)) { 17 | throw new Error('You must supply a valid database prop to the DatabaseProvider') 18 | } 19 | return {children} 20 | } 21 | 22 | export { default as withDatabase } from './withDatabase' 23 | export { default as DatabaseContext, DatabaseConsumer } from './DatabaseContext' 24 | export default DatabaseProvider 25 | -------------------------------------------------------------------------------- /src/DatabaseProvider/withDatabase.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react' 3 | import hoistNonReactStatics from 'hoist-non-react-statics' 4 | import type Database from '../Database' 5 | import { DatabaseConsumer } from './DatabaseContext' 6 | 7 | type WithDatabaseProps = { 8 | ...T, 9 | database: Database, 10 | } 11 | // HoC to inject the database into the props of consumers 12 | export default function withDatabase( 13 | Component: React$ComponentType>, 14 | ): React$ComponentType { 15 | function DatabaseComponent(props: any): React$Element { 16 | return ( 17 | 18 | {(database: Database) => } 19 | 20 | ) 21 | } 22 | 23 | return hoistNonReactStatics(DatabaseComponent, Component) 24 | } 25 | -------------------------------------------------------------------------------- /src/QueryDescription/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-use-before-define */ 3 | 4 | export type { 5 | NonNullValue, 6 | NonNullValues, 7 | Value, 8 | CompoundValue, 9 | Operator, 10 | ColumnDescription, 11 | ComparisonRight, 12 | Comparison, 13 | WhereDescription, 14 | SqlExpr, 15 | LokiExpr, 16 | Where, 17 | And, 18 | Or, 19 | On, 20 | SortOrder, 21 | SortBy, 22 | Take, 23 | Skip, 24 | JoinTables, 25 | NestedJoinTable, 26 | LokiTransformFunction, 27 | LokiTransform, 28 | SqlQuery, 29 | Clause, 30 | QueryDescription, 31 | } from './type' 32 | 33 | export { 34 | eq, 35 | notEq, 36 | gt, 37 | gte, 38 | weakGt, 39 | lt, 40 | lte, 41 | oneOf, 42 | notIn, 43 | between, 44 | like, 45 | notLike, 46 | sanitizeLikeString, 47 | includes, 48 | column, 49 | where, 50 | unsafeSqlExpr, 51 | unsafeLokiExpr, 52 | unsafeLokiTransform, 53 | and, 54 | or, 55 | asc, 56 | desc, 57 | sortBy, 58 | take, 59 | skip, 60 | on, 61 | ftsMatch, 62 | experimentalJoinTables, 63 | experimentalNestedJoin, 64 | unsafeSqlQuery, 65 | } from './operators' 66 | 67 | // NOTE: These probably shouldn't be exported from here, but kept for backwards compatibility 68 | export { buildQueryDescription, queryWithoutDeleted } from './helpers' 69 | -------------------------------------------------------------------------------- /src/Relation/helpers.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { 4 | type Observable, 5 | of as of$, 6 | map as map$, 7 | switchMap, 8 | distinctUntilChanged, 9 | } from '../utils/rx' 10 | 11 | import type Relation from './index' 12 | import type Model from '../Model' 13 | 14 | const getImmutableObservable = (relation: Relation): Observable => 15 | relation._model.collections 16 | .get(relation._relationTableName) 17 | // $FlowFixMe 18 | .findAndObserve(relation.id) 19 | 20 | const getObservable = (relation: Relation): Observable => 21 | relation._model 22 | .observe() 23 | // $FlowFixMe 24 | .pipe( 25 | map$((model) => model._getRaw(relation._columnName)), 26 | distinctUntilChanged(), 27 | switchMap((id) => 28 | id 29 | ? relation._model.collections.get(relation._relationTableName).findAndObserve(id) 30 | : of$(null), 31 | ), 32 | ) 33 | 34 | // eslint-disable-next-line 35 | export const createObservable = (relation: Relation): Observable => 36 | relation._isImmutable ? getImmutableObservable(relation) : getObservable(relation) 37 | -------------------------------------------------------------------------------- /src/Schema/migrations/stepsForMigration.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { unnest } from '../../utils/fp' 4 | 5 | import { type SchemaMigrations, type MigrationStep } from './index' 6 | import { type SchemaVersion } from '../index' 7 | 8 | export function stepsForMigration({ 9 | migrations: schemaMigrations, 10 | fromVersion, 11 | toVersion, 12 | }: $Exact<{ 13 | migrations: SchemaMigrations, 14 | fromVersion: SchemaVersion, 15 | toVersion: SchemaVersion, 16 | }>): ?(MigrationStep[]) { 17 | const { sortedMigrations, minVersion, maxVersion } = schemaMigrations 18 | 19 | // see if migrations in this range are available 20 | if (fromVersion < minVersion || toVersion > maxVersion) { 21 | return null 22 | } 23 | 24 | // return steps 25 | const matchingMigrations = sortedMigrations.filter( 26 | ({ toVersion: version }) => version > fromVersion && version <= toVersion, 27 | ) 28 | 29 | const allSteps = unnest(matchingMigrations.map((migration) => migration.steps)) 30 | return allSteps 31 | } 32 | -------------------------------------------------------------------------------- /src/adapters/error.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable getter-return */ 3 | 4 | // Used as a placeholder during reset database to catch illegal 5 | // adapter calls 6 | 7 | const throwError = (name: string) => { 8 | throw new Error(`Cannot call database.adapter.${name} while the database is being reset`) 9 | } 10 | 11 | export default class ErrorAdapter { 12 | constructor(): void { 13 | ;[ 14 | 'find', 15 | 'query', 16 | 'queryIds', 17 | 'count', 18 | 'batch', 19 | 'getDeletedRecords', 20 | 'destroyDeletedRecords', 21 | 'unsafeResetDatabase', 22 | 'getLocal', 23 | 'setLocal', 24 | 'removeLocal', 25 | 'testClone', 26 | ].forEach((name) => { 27 | // $FlowFixMe 28 | this[name] = () => throwError(name) 29 | }) 30 | } 31 | 32 | get underlyingAdapter(): void { 33 | throwError('underlyingAdapter') 34 | } 35 | 36 | get schema(): void { 37 | throwError('schema') 38 | } 39 | 40 | get migrations(): void { 41 | throwError('migrations') 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/adapters/lokijs/common.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { type Result } from '../../utils/fp/Result' 4 | import type { CachedQueryResult, CachedFindResult } from '../type' 5 | import type { RecordId } from '../../Model' 6 | 7 | export type WorkerExecutorType = 8 | | 'setUp' 9 | | 'find' 10 | | 'query' 11 | | 'queryIds' 12 | | 'unsafeQueryRaw' 13 | | 'count' 14 | | 'batch' 15 | | 'getDeletedRecords' 16 | | 'unsafeResetDatabase' 17 | | 'unsafeExecute' 18 | | 'getLocal' 19 | | 'setLocal' 20 | | 'removeLocal' 21 | | '_fatalError' 22 | | 'clearCachedRecords' 23 | 24 | export type WorkerExecutorPayload = any[] 25 | 26 | export type WorkerResponseData = CachedQueryResult | CachedFindResult | number | RecordId[] 27 | 28 | export type CloneMethod = 'shallowCloneDeepObjects' | 'immutable' | 'deep' 29 | 30 | export type WorkerAction = $Exact<{ 31 | id: number, 32 | type: WorkerExecutorType, 33 | payload: WorkerExecutorPayload, 34 | cloneMethod: CloneMethod, 35 | returnCloneMethod: CloneMethod, 36 | }> 37 | 38 | export type WorkerResponse = $Exact<{ 39 | id: number, 40 | result: Result, 41 | }> 42 | -------------------------------------------------------------------------------- /src/adapters/lokijs/type.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type Loki = $FlowFixMe 4 | export type LokiCollection = $FlowFixMe 5 | export type LokiResultset = $FlowFixMe 6 | export type LokiMemoryAdapter = $FlowFixMe 7 | -------------------------------------------------------------------------------- /src/adapters/lokijs/worker/cloneMessage/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // shallow-clones objects (without checking their contents), but copies arrays 4 | export function shallowCloneDeepObjects(value: any): any { 5 | if (Array.isArray(value)) { 6 | const returned = new Array(value.length) 7 | for (let i = 0, len = value.length; i < len; i += 1) { 8 | returned[i] = shallowCloneDeepObjects(value[i]) 9 | } 10 | return returned 11 | } else if (value && typeof value === 'object') { 12 | return Object.assign({}, value) 13 | } 14 | 15 | return value 16 | } 17 | 18 | export default function cloneMessage(data: any): any { 19 | // TODO: Even better, it would be great if we had zero-copy architecture (COW RawRecords?) and we didn't have to clone 20 | const method = data.cloneMethod 21 | if (method === 'shallowCloneDeepObjects') { 22 | const clonedData = data 23 | clonedData.payload = shallowCloneDeepObjects(clonedData.payload) 24 | return clonedData 25 | } else if (method === 'immutable') { 26 | // we get a pinky promise that the payload is immutable so we don't need to copy 27 | return data 28 | } 29 | 30 | throw new Error('Unknown data.clone method for cloneMessage') 31 | } 32 | -------------------------------------------------------------------------------- /src/adapters/lokijs/worker/loki.worker.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-restricted-globals */ 3 | 4 | import DatabaseBridge from './DatabaseBridge' 5 | import type Worker from './synchronousWorker' 6 | 7 | const getDefaultExport = (): any => { 8 | self.workerClass = new DatabaseBridge(self) 9 | return self 10 | } 11 | 12 | export default (getDefaultExport(): Worker) 13 | -------------------------------------------------------------------------------- /src/adapters/lokijs/worker/synchronousWorker.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import DatabaseBridge from './DatabaseBridge' 4 | import cloneMessage from './cloneMessage' 5 | 6 | // Simulates the web worker API 7 | export default class SynchronousWorker { 8 | _bridge: DatabaseBridge 9 | 10 | _workerContext: DedicatedWorkerGlobalScope 11 | 12 | onmessage: ({ data: any }) => void = () => {} 13 | 14 | constructor(): void { 15 | // $FlowFixMe 16 | this._workerContext = { 17 | postMessage: (data) => { 18 | this.onmessage({ data: cloneMessage(data) }) 19 | }, 20 | onmessage: () => {}, 21 | } 22 | // $FlowFixMe 23 | this._bridge = new DatabaseBridge(this._workerContext) 24 | } 25 | 26 | postMessage(data: any): void { 27 | this._workerContext.onmessage(({ data: cloneMessage(data) }: any)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/adapters/sqlite/devtools.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Query from '../../Query' 4 | import encodeQuery from './encodeQuery' 5 | import type { SQL } from './index' 6 | 7 | // $FlowFixMe 8 | Query.prototype._sql = function _sql(count: boolean = false): SQL { 9 | const query: Query = this 10 | const [sql] = encodeQuery(query.serialize(), count) 11 | return sql 12 | } 13 | -------------------------------------------------------------------------------- /src/adapters/sqlite/encodeValue/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import escapeString from 'sql-escape-string' 4 | import { logError } from '../../../utils/common' 5 | 6 | import type { Value } from '../../../QueryDescription' 7 | 8 | // Note: SQLite doesn't support literal TRUE and FALSE; expects 1 or 0 instead 9 | // It also doesn't encode strings the same way 10 | // Also: catches invalid values (undefined, NaN) early 11 | 12 | export default function encodeValue(value: Value): string { 13 | if (value === true) { 14 | return '1' 15 | } else if (value === false) { 16 | return '0' 17 | } else if (Number.isNaN(value)) { 18 | logError('Passed NaN to query') 19 | return 'null' 20 | } else if (value === undefined) { 21 | logError('Passed undefined to query') 22 | return 'null' 23 | } else if (value === null) { 24 | return 'null' 25 | } else if (typeof value === 'number') { 26 | return `${value}` 27 | } else if (typeof value === 'string') { 28 | // TODO: We shouldn't ever encode SQL values directly — use placeholders 29 | return escapeString(value) 30 | } 31 | throw new Error('Invalid value to encode into query') 32 | } 33 | -------------------------------------------------------------------------------- /src/adapters/sqlite/makeDispatcher/decodeQueryResult/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Compressed records have this syntax: 4 | // [ 5 | // ['id', 'body', ...], // 0: column names 6 | // ['foo', 'bar', ...], // values matching column names 7 | // 'id', // only cached id 8 | // ] 9 | export default function decodeQueryResult(compressedRecords: any[]): any[] { 10 | const len = compressedRecords.length 11 | if (!len) { 12 | return [] 13 | } 14 | const columnNames = compressedRecords[0] 15 | const columnsLen = columnNames.length 16 | 17 | const rawRecords = new Array(len - 1) 18 | let rawRecord, compressedRecord 19 | for (let i = 1; i < len; i++) { 20 | compressedRecord = compressedRecords[i] 21 | if (typeof compressedRecord === 'string') { 22 | rawRecord = compressedRecord 23 | } else { 24 | rawRecord = ({}: { [any]: any }) 25 | for (let j = 0; j < columnsLen; j++) { 26 | rawRecord[columnNames[j]] = compressedRecord[j] 27 | } 28 | } 29 | rawRecords[i - 1] = rawRecord 30 | } 31 | return rawRecords 32 | } 33 | -------------------------------------------------------------------------------- /src/adapters/sqlite/makeDispatcher/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable global-require */ 3 | 4 | import DatabaseBridge from '../sqlite-node/DatabaseBridge' 5 | import { type ConnectionTag } from '../../../utils/common' 6 | import { type ResultCallback } from '../../../utils/fp/Result' 7 | import type { 8 | DispatcherType, 9 | SQLiteAdapterOptions, 10 | SqliteDispatcher, 11 | SqliteDispatcherMethod, 12 | SqliteDispatcherOptions, 13 | } from '../type' 14 | 15 | class SqliteNodeDispatcher implements SqliteDispatcher { 16 | _tag: ConnectionTag 17 | 18 | constructor(tag: ConnectionTag): void { 19 | this._tag = tag 20 | } 21 | 22 | call(methodName: SqliteDispatcherMethod, args: any[], callback: ResultCallback): void { 23 | // $FlowFixMe 24 | const method = DatabaseBridge[methodName].bind(DatabaseBridge) 25 | method( 26 | this._tag, 27 | ...args, 28 | (value) => callback({ value }), 29 | (code, message, error) => callback({ error }), 30 | ) 31 | } 32 | } 33 | 34 | export const makeDispatcher = ( 35 | _type: DispatcherType, 36 | tag: ConnectionTag, 37 | _dbName: string, 38 | _options: SqliteDispatcherOptions, 39 | ): SqliteDispatcher => { 40 | return new SqliteNodeDispatcher(tag) 41 | } 42 | 43 | export function getDispatcherType(_options: SQLiteAdapterOptions): DispatcherType { 44 | return 'asynchronous' 45 | } 46 | -------------------------------------------------------------------------------- /src/decorators/action/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Descriptor } from '../../utils/common/makeDecorator' 4 | 5 | // Wraps function calls in `database.write(() => { ... })`. See docs for more details 6 | // You can use this on Model subclass methods (or methods of any object that has a `database` property) 7 | export function writer(target: Object, key: string, descriptor: Descriptor): Descriptor { 8 | const actionName = `${target.table || target.constructor.name}.${key}` 9 | return { 10 | ...descriptor, 11 | value(...args): Promise { 12 | // $FlowFixMe 13 | return this.database.write(() => descriptor.value.apply(this, args), actionName) 14 | }, 15 | } 16 | } 17 | 18 | // Wraps function calls in `database.read(() => { ... })`. See docs for more details 19 | // You can use this on Model subclass methods (or methods of any object that has a `database` property) 20 | export function reader(target: Object, key: string, descriptor: Descriptor): Descriptor { 21 | const actionName = `${target.table || target.constructor.name}.${key}` 22 | return { 23 | ...descriptor, 24 | value(...args): Promise { 25 | // $FlowFixMe 26 | return this.database.read(() => descriptor.value.apply(this, args), actionName) 27 | }, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/decorators/common.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import invariant from '../utils/common/invariant' 4 | 5 | import type { ColumnName } from '../Schema' 6 | 7 | // eslint-disable-next-line 8 | export function ensureDecoratorUsedProperly( 9 | columnName: ColumnName, 10 | target: Object, 11 | key: string, 12 | descriptor: Object, 13 | ): void { 14 | invariant( 15 | columnName, 16 | `Pass column name (raw field name) to the decorator - error in ${target.constructor.name}.prototype.${key} given.`, 17 | ) 18 | if (descriptor) { 19 | invariant( 20 | 'initializer' in descriptor, 21 | `Model field decorators can only be used for simple properties - method, setter or getter ${target.constructor.name}.prototype.${key} given.`, 22 | ) 23 | invariant( 24 | typeof descriptor.initializer !== 'function', 25 | `Model field decorators must not be used on properties with a default value - error in "${target.constructor.name}.prototype.${key}".`, 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/decorators/field/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import makeDecorator, { type Decorator } from '../../utils/common/makeDecorator' 4 | 5 | import { type Value } from '../../QueryDescription' 6 | import { type ColumnName } from '../../Schema' 7 | 8 | import { ensureDecoratorUsedProperly } from '../common' 9 | 10 | // Defines a model property 11 | // 12 | // Returns and sets values as-is, except that `undefined` and missing fields are normalized to `null` 13 | // If you have a more specific propety, use the correct decorator (@boolean, @text, etc.) 14 | // 15 | // Pass the database column name as an argument 16 | // 17 | // Example: 18 | // @field('some_field') someField 19 | 20 | const field: Decorator = makeDecorator( 21 | (columnName: ColumnName) => (target: Object, key: string, descriptor: Object) => { 22 | ensureDecoratorUsedProperly(columnName, target, key, descriptor) 23 | 24 | return { 25 | configurable: true, 26 | enumerable: true, 27 | get(): Value { 28 | // $FlowFixMe 29 | return this.asModel._getRaw(columnName) 30 | }, 31 | set(value: any): void { 32 | // $FlowFixMe 33 | this.asModel._setRaw(columnName, value) 34 | }, 35 | } 36 | }, 37 | ) 38 | 39 | export default field 40 | -------------------------------------------------------------------------------- /src/decorators/immutableRelation/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { type Decorator } from '../../utils/common/makeDecorator' 4 | import type { ColumnName, TableName } from '../../Schema' 5 | 6 | import relation from '../relation' 7 | 8 | // Defines a model property that fetches a record with a specific ID 9 | // The property defined must be *immutable*, i.e. the relation ID must never change 10 | // Returns an immutable Relation object. See watermelondb/Relation for more information 11 | // 12 | // If the property *can* change, use `relation` instead 13 | // 14 | // You can only assign a value inside a `collection.create()` or `collection.prepareCreate()` block 15 | // 16 | // relationIdColumn - name of the column with record ID 17 | // relationTable - name of the table containing desired recods 18 | // 19 | // Example: a Comment has an author (and an author can never change), so it may define: 20 | // @immutableRelation('team_member', 'author_id') author: Relation 21 | 22 | const immutableRelation: Decorator = ( 23 | relationTable: TableName, 24 | relationIdColumn: ColumnName, 25 | ) => relation(relationTable, relationIdColumn, { isImmutable: true }) 26 | 27 | export default immutableRelation 28 | -------------------------------------------------------------------------------- /src/decorators/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { writer, reader } from './action' 4 | export { default as children } from './children' 5 | export { default as json } from './json' 6 | export { default as nochange } from './nochange' 7 | export { default as field } from './field' 8 | export { default as date } from './date' 9 | export { default as text } from './text' 10 | export { default as readonly } from './readonly' 11 | export { default as lazy } from './lazy' 12 | export { default as relation } from './relation' 13 | export { default as immutableRelation } from './immutableRelation' 14 | -------------------------------------------------------------------------------- /src/decorators/lazy/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Descriptor } from '../../utils/common/makeDecorator' 4 | 5 | // Defines a property whose value is evaluated the first time it is accessed 6 | // For example: 7 | // 8 | // class X { 9 | // @lazy date = new Date() 10 | // } 11 | // 12 | // `date` will be set to the current date not when constructed, but only when `xx.date` is called. 13 | // All subsequent calls will return the same value 14 | 15 | export default function lazy(target: Object, key: string, descriptor: Descriptor): Descriptor { 16 | const { configurable, enumerable, initializer, value } = descriptor 17 | return { 18 | configurable, 19 | enumerable, 20 | get(): any { 21 | // $FlowFixMe 22 | const that = this 23 | // This happens if someone accesses the 24 | // property directly on the prototype 25 | if (that === target) { 26 | return undefined 27 | } 28 | 29 | const returnValue = initializer ? initializer.call(that) : value 30 | 31 | // Next time this property is called, skip the decorator, and just return the precomputed value 32 | Object.defineProperty(that, key, { 33 | configurable, 34 | enumerable, 35 | writable: true, 36 | value: returnValue, 37 | }) 38 | 39 | return returnValue 40 | }, 41 | // TODO: What should be the behavior on set? 42 | } 43 | } 44 | 45 | // Implementation inspired by lazyInitialize from `core-decorators` 46 | -------------------------------------------------------------------------------- /src/decorators/nochange/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import makeDecorator, { type Decorator } from '../../utils/common/makeDecorator' 4 | import invariant from '../../utils/common/invariant' 5 | 6 | // Marks a model field as immutable after create — you can set and change the value in 7 | // create() and prepareCreate(), but after it's saved to the database, it cannot be changed 8 | 9 | const nochange: Decorator = makeDecorator( 10 | () => (target: Object, key: string, descriptor: Object) => { 11 | invariant( 12 | descriptor.set, 13 | `@nochange can only be applied to model fields (to properties with a setter)`, 14 | ) 15 | 16 | const errorMessage = `Attempt to set a new value on a @nochange field: ${target.constructor.name}.prototype.${key}` 17 | 18 | return { 19 | ...descriptor, 20 | set(value: any): void { 21 | // $FlowFixMe 22 | const model = this 23 | invariant(model.asModel._preparedState === 'create', errorMessage) 24 | descriptor.set.call(model, value) 25 | }, 26 | } 27 | }, 28 | ) 29 | 30 | export default nochange 31 | -------------------------------------------------------------------------------- /src/decorators/readonly/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import makeDecorator, { type Decorator } from '../../utils/common/makeDecorator' 4 | import invariant from '../../utils/common/invariant' 5 | 6 | // Marks a field as non-writable (throws an error when attempting to set a new value) 7 | // When using multiple decorators, remember to mark as @readonly *last* (leftmost) 8 | 9 | const readonly: Decorator = makeDecorator( 10 | () => (target: Object, key: string, descriptor: Object) => { 11 | // Set a new setter on getter/setter fields 12 | if (descriptor.get || descriptor.set) { 13 | return { 14 | ...descriptor, 15 | set(): void { 16 | invariant( 17 | false, 18 | `Attempt to set new value on a property ${target.constructor.name}.prototype.${key} marked as @readonly`, 19 | ) 20 | }, 21 | } 22 | } 23 | 24 | // Mark as writable=false for simple fields 25 | descriptor.writable = false 26 | return descriptor 27 | }, 28 | ) 29 | 30 | export default readonly 31 | -------------------------------------------------------------------------------- /src/decorators/text/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import makeDecorator, { type Decorator } from '../../utils/common/makeDecorator' 4 | 5 | import { ensureDecoratorUsedProperly } from '../common' 6 | 7 | import { type ColumnName } from '../../Schema' 8 | 9 | // Defines a model property representing user-input text 10 | // 11 | // On set, all strings are trimmed (whitespace is removed from beginning/end) 12 | // and all non-string values are converted to strings 13 | // (Except null which is passed as-is) 14 | // 15 | // Pass the database column name as an argument 16 | // 17 | // Examples: 18 | // @text(Column.name) name: string 19 | // @text('full_description') fullDescription: string 20 | 21 | const text: Decorator = makeDecorator( 22 | (columnName: ColumnName) => (target: Object, key: string, descriptor: Object) => { 23 | ensureDecoratorUsedProperly(columnName, target, key, descriptor) 24 | 25 | return { 26 | configurable: true, 27 | enumerable: true, 28 | get(): ?string { 29 | // $FlowFixMe 30 | return this.asModel._getRaw(columnName) 31 | }, 32 | set(value: ?string): void { 33 | // $FlowFixMe 34 | this.asModel._setRaw(columnName, typeof value === 'string' ? value.trim() : null) 35 | }, 36 | } 37 | }, 38 | ) 39 | 40 | export default text 41 | -------------------------------------------------------------------------------- /src/hooks/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | export { useDatabase } from './use-database' 3 | -------------------------------------------------------------------------------- /src/hooks/use-database.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react' 3 | import { DatabaseContext } from '../DatabaseProvider' 4 | import invariant from '../utils/common/invariant' 5 | 6 | import type Database from '../Database' 7 | 8 | export function useDatabase(): Database { 9 | const database = React.useContext(DatabaseContext) 10 | 11 | invariant( 12 | database, 13 | 'Could not find database context, please make sure the component is wrapped in the ', 14 | ) 15 | 16 | return database 17 | } 18 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import * as Q from './QueryDescription' 4 | 5 | export { default as Collection } from './Collection' 6 | export { default as Database } from './Database' 7 | export { default as Relation } from './Relation' 8 | export { default as Model, associations } from './Model' 9 | export { default as Query } from './Query' 10 | export { tableName, columnName, appSchema, tableSchema } from './Schema' 11 | 12 | export type { default as CollectionMap } from './Database/CollectionMap' 13 | 14 | export type { LocalStorageKey } from './Database/LocalStorage' 15 | export { localStorageKey } from './Database/LocalStorage' 16 | 17 | export type { DatabaseAdapter } from './adapters/type' 18 | export type { RawRecord, DirtyRaw } from './RawRecord' 19 | export type { RecordId } from './Model' 20 | export type { 21 | TableName, 22 | ColumnName, 23 | ColumnType, 24 | ColumnSchema, 25 | TableSchema, 26 | AppSchema, 27 | } from './Schema' 28 | export type { SchemaMigrations } from './Schema/migrations' 29 | 30 | export { Q } 31 | -------------------------------------------------------------------------------- /src/observation/encodeMatcher/canEncode.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { QueryDescription } from '../../QueryDescription' 4 | 5 | export const forbiddenError = `Queries with joins, sortBy, take, skip, lokiTransform can't be encoded into a matcher` 6 | 7 | export default function canEncodeMatcher(query: QueryDescription): boolean { 8 | const { joinTables, nestedJoinTables, sortBy, take, skip, lokiTransform, sql } = query 9 | 10 | return ( 11 | !joinTables.length && 12 | !nestedJoinTables.length && 13 | !sortBy.length && 14 | !take && 15 | !skip && 16 | !lokiTransform && 17 | !sql 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/observation/subscribeToQuery.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { type Unsubscribe } from '../utils/subscriptions' 4 | 5 | import type Query from '../Query' 6 | import type Model from '../Model' 7 | 8 | import subscribeToQueryReloading from './subscribeToQueryReloading' 9 | import subscribeToSimpleQuery from './subscribeToSimpleQuery' 10 | import canEncodeMatcher from './encodeMatcher/canEncode' 11 | 12 | export default function subscribeToQuery( 13 | query: Query, 14 | subscriber: (Record[]) => void, 15 | ): Unsubscribe { 16 | return canEncodeMatcher(query.description) 17 | ? subscribeToSimpleQuery(query, subscriber) 18 | : subscribeToQueryReloading(query, subscriber) 19 | } 20 | -------------------------------------------------------------------------------- /src/observation/subscribeToSimpleQuery/processChangeSet.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { CollectionChangeSet } from '../../Collection' 4 | import type Model from '../../Model' 5 | import type { Matcher } from '../encodeMatcher' 6 | 7 | // WARN: Mutates arguments 8 | export default function processChangeSet( 9 | changeSet: CollectionChangeSet, 10 | matcher: Matcher, 11 | mutableMatchingRecords: Record[], 12 | ): boolean { 13 | let shouldEmit = false 14 | changeSet.forEach((change) => { 15 | const { record, type } = change 16 | const index = mutableMatchingRecords.indexOf(record) 17 | const currentlyMatching = index > -1 18 | 19 | if (type === 'destroyed') { 20 | if (currentlyMatching) { 21 | // Remove if record was deleted 22 | mutableMatchingRecords.splice(index, 1) 23 | shouldEmit = true 24 | } 25 | return 26 | } 27 | 28 | const matches = matcher(record._raw) 29 | 30 | if (currentlyMatching && !matches) { 31 | // Remove if doesn't match anymore 32 | mutableMatchingRecords.splice(index, 1) 33 | shouldEmit = true 34 | } else if (matches && !currentlyMatching) { 35 | // Add if should be included but isn't 36 | mutableMatchingRecords.push(record) 37 | shouldEmit = true 38 | } 39 | }) 40 | return shouldEmit 41 | } 42 | -------------------------------------------------------------------------------- /src/sync/helpers.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { ColumnName } from '..' 4 | import type { RawRecord } from '../RawRecord' 5 | 6 | export function addToRawSet(rawSet: string, value: string): string { 7 | const array = rawSet ? rawSet.split(',') : [] 8 | const set = new Set(array) 9 | set.add(value) 10 | return Array.from(set).join(',') 11 | } 12 | 13 | // Mutates `rawRecord` to mark `columName` as modified for sync purposes 14 | export function setRawColumnChange(rawRecord: RawRecord, columnName: ColumnName): void { 15 | rawRecord._changed = addToRawSet(rawRecord._changed, columnName) 16 | if (rawRecord._status !== 'created') { 17 | rawRecord._status = 'updated' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // TODO: Move more of the basic Watermelon types here 4 | 5 | export type $RE = $ReadOnly<$Exact> 6 | -------------------------------------------------------------------------------- /src/utils/common/connectionTag/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export opaque type ConnectionTag: number = number 4 | 5 | let previousTag = 0 6 | 7 | export default function connectionTag(): ConnectionTag { 8 | previousTag += 1 9 | return previousTag 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/common/deepFreeze/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import invariant from '../invariant' 4 | 5 | // Deep-freezes an object, but DOES NOT handle cycles 6 | export default function deepFreeze(object: T): T { 7 | invariant(object && typeof object === 'object', 'Invalid attempt to deepFreeze not-an-Object') 8 | 9 | Object.getOwnPropertyNames(object).forEach((name: string) => { 10 | const value = object[name] 11 | 12 | if (value && typeof value === 'object') { 13 | deepFreeze(value) 14 | } 15 | }) 16 | 17 | return Object.freeze(object) 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/common/deprecated/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import logger from '../logger' 4 | 5 | const deprecationsReported: { [string]: boolean } = {} 6 | 7 | export default function deprecated(name: string, deprecationInfo: string): void { 8 | if (!deprecationsReported[name]) { 9 | deprecationsReported[name] = true 10 | logger.warn( 11 | `DEPRECATION: ${name} is deprecated. ${deprecationInfo} See changelog & docs for more info.`, 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/common/devMeasureTime/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const getPreciseTimeFunction: () => () => number = () => { 4 | if (typeof global !== 'undefined' && global.nativePerformanceNow) { 5 | return global.nativePerformanceNow 6 | } else if (typeof window !== 'undefined' && window.performance && window.performance.now) { 7 | return window.performance.now.bind(window.performance) 8 | } 9 | 10 | // $FlowFixMe 11 | return Date.now 12 | } 13 | const getPreciseTime: () => number = getPreciseTimeFunction() 14 | 15 | export { getPreciseTime } 16 | 17 | export function devMeasureTime(executeBlock: () => T): [T, number] { 18 | const start = getPreciseTime() 19 | const result = executeBlock() 20 | const time = getPreciseTime() - start 21 | return [result, time] 22 | } 23 | 24 | export async function devMeasureTimeAsync(executeBlock: () => Promise): Promise<[T, number]> { 25 | const start = getPreciseTime() 26 | const result = await executeBlock() 27 | const time = getPreciseTime() - start 28 | return [result, time] 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/common/diagnosticError/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type DiagnosticErrorFunction = (string) => Error 4 | let customDiagnosticErrorFunction: ?DiagnosticErrorFunction = null 5 | 6 | // Use this to replace default diagnosticError function to inject your custom logic 7 | // (e.g. only display errors in development, or log errors to external service) 8 | export function useCustomDiagnosticErrorFunction( 9 | diagnosticErrorFunction: DiagnosticErrorFunction, 10 | ): void { 11 | customDiagnosticErrorFunction = diagnosticErrorFunction 12 | } 13 | 14 | export default function diagnosticError(errorMessage: string): Error { 15 | if (customDiagnosticErrorFunction) { 16 | return customDiagnosticErrorFunction(errorMessage) 17 | } 18 | 19 | const error: any = new Error(errorMessage) 20 | 21 | // hides `diagnosticError` from RN stack trace 22 | error.framesToPop = 1 23 | error.name = 'Diagnostic error' 24 | 25 | return error 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/common/ensureSync/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import invariant from '../invariant' 4 | 5 | // Throws if passed value is a Promise 6 | // Otherwise, returns the passed value as-is. 7 | // 8 | // Use to ensure API users aren't passing async functions 9 | 10 | export default function ensureSync(value: T): T { 11 | invariant( 12 | !(value instanceof Promise), 13 | 'Unexpected Promise. Passed function should be synchronous.', 14 | ) 15 | 16 | return value 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/common/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { getPreciseTime, devMeasureTime, devMeasureTimeAsync } from './devMeasureTime' 4 | export { default as randomId } from './randomId' 5 | export { default as makeDecorator } from './makeDecorator' 6 | export { default as ensureSync } from './ensureSync' 7 | export { default as invariant } from './invariant' 8 | export { default as logError } from './logError' 9 | export { default as logger } from './logger' 10 | export { default as isRN } from './isRN' 11 | export { default as deprecated } from './deprecated' 12 | export { default as connectionTag } from './connectionTag' 13 | export type { ConnectionTag } from './connectionTag' 14 | -------------------------------------------------------------------------------- /src/utils/common/invariant/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import diagnosticError from '../diagnosticError' 4 | 5 | // If `condition` is falsy, throws an Error with the passed message 6 | 7 | export default function invariant(condition: any, errorMessage?: string): void { 8 | if (!condition) { 9 | const error: any = diagnosticError(errorMessage || 'Broken invariant') 10 | error.framesToPop += 1 11 | throw error 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/common/isRN/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default false 4 | -------------------------------------------------------------------------------- /src/utils/common/isRN/index.native.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default true 4 | -------------------------------------------------------------------------------- /src/utils/common/logError/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import diagnosticError from '../diagnosticError' 4 | import logger from '../logger' 5 | 6 | // Logs an Error to the console with the given message 7 | // 8 | // Use when a *recoverable* error occurs (so you don't want it to throw) 9 | 10 | export default function logError(errorMessage: string): void { 11 | const error: any = diagnosticError(errorMessage) 12 | error.framesToPop += 1 13 | 14 | logger.error(error) 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/common/logger/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-console */ 3 | 4 | const formatMessages = (messages: Array) => { 5 | const [first, ...other] = messages 6 | return [typeof first === 'string' ? `[🍉] ${first}` : first, ...other] 7 | } 8 | 9 | class Logger { 10 | silent: boolean = false 11 | 12 | log(...messages: any[]): void { 13 | !this.silent && console.log(...formatMessages(messages)) 14 | } 15 | 16 | warn(...messages: any[]): void { 17 | !this.silent && console.warn(...formatMessages(messages)) 18 | } 19 | 20 | error(...messages: any[]): void { 21 | !this.silent && console.error(...formatMessages(messages)) 22 | } 23 | 24 | silence(): void { 25 | this.silent = true 26 | } 27 | } 28 | 29 | export default (new Logger(): Logger) 30 | -------------------------------------------------------------------------------- /src/utils/common/makeDecorator/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type Descriptor = Object 4 | export type RawDecorator = (target: Object, key: string, descriptor: Descriptor) => Descriptor 5 | export type Decorator = (...any) => Descriptor | RawDecorator 6 | 7 | // Converts a function with signature `(args) => (target, key, descriptor)` to a decorator 8 | // that works both when called `@decorator foo` and with arguments, like `@decorator(arg) foo` 9 | export default function makeDecorator(decorator: (...any) => RawDecorator): Decorator { 10 | return (...args) => { 11 | // Decorator called with an argument, JS expects a decorator function 12 | if (args.length < 3) { 13 | return decorator(...args) 14 | } 15 | 16 | // Decorator called without an argument, JS expects a descriptor object 17 | return decorator()(...args) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/common/memory/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | type Callback = () => void 4 | const lowMemoryCallbacks: Callback[] = [] 5 | 6 | export function onLowMemory(callback: Callback): void { 7 | lowMemoryCallbacks.push(callback) 8 | } 9 | 10 | // TODO: Not currently hooked up to anything 11 | export function _triggerOnLowMemory(): void { 12 | lowMemoryCallbacks.forEach((callback) => callback()) 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/common/randomId/fallback.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 4 | 5 | export default function fallbackRandomId(): string { 6 | let id = '' 7 | let v = 0 8 | for (let i = 0; i < 16; i += 1) { 9 | v = Math.floor(Math.random() * 62) 10 | id += alphabet[v % 62] 11 | } 12 | 13 | return id 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/common/randomId/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import randomId from './randomId' 4 | 5 | let generator: () => string = randomId 6 | 7 | // NOTE: It's is only safe for the ID to contain [a-zA-Z0-9._]. It must not contain other characters 8 | // (especially '"\/$). Never, ever allow the ID to be set by the user w/o validating - this breaks security! 9 | export const setGenerator = (newGenerator: () => string) => { 10 | if (typeof newGenerator() !== 'string') { 11 | throw new Error('RandomId generator function needs to return a string type.') 12 | } 13 | generator = newGenerator 14 | } 15 | 16 | export default (): string => generator() 17 | -------------------------------------------------------------------------------- /src/utils/common/randomId/randomId.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-bitwise */ 3 | 4 | const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 5 | 6 | const randomNumbers = new Uint8Array(256) 7 | let cur = 9999999 8 | 9 | /*:: declare var globalThis: WindowProxy */ 10 | 11 | function cryptoRandomId(): string { 12 | let id = '' 13 | let len = 0 14 | let v = 0 15 | while (len < 16) { 16 | if (cur < 256) { 17 | v = randomNumbers[cur] >> 2 18 | cur++ 19 | if (v < 62) { 20 | id += alphabet[v] 21 | len++ 22 | } 23 | } else { 24 | globalThis.crypto.getRandomValues(randomNumbers) 25 | cur = 0 26 | } 27 | } 28 | 29 | return id 30 | } 31 | 32 | const isCryptoAvailable: boolean = globalThis.crypto && globalThis.crypto.getRandomValues 33 | const randomId: () => string = isCryptoAvailable ? cryptoRandomId : require('./fallback').default 34 | 35 | export default randomId 36 | -------------------------------------------------------------------------------- /src/utils/common/randomId/randomId.native.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-bitwise */ 3 | import { NativeModules } from 'react-native' 4 | import nativeRandomId_v2 from './randomId_v2.native' 5 | 6 | const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 7 | 8 | let randomNumbers = [] 9 | let cur = 9999999 10 | 11 | // TODO: This is 3-5x slower than Math.random()-based implementation 12 | // Should be migrated to JSI, or simply implemented fully in native 13 | // (bridging is the bottleneck) 14 | function nativeRandomId_v1(): string { 15 | let id = '' 16 | let len = 0 17 | let v = 0 18 | while (len < 16) { 19 | if (cur < 256) { 20 | v = randomNumbers[cur] >> 2 21 | cur++ 22 | if (v < 62) { 23 | id += alphabet[v] 24 | len++ 25 | } 26 | } else { 27 | randomNumbers = NativeModules.WMDatabaseBridge.getRandomBytes(256) 28 | cur = 0 29 | } 30 | } 31 | 32 | return id 33 | } 34 | 35 | const isV2Available: boolean = !!NativeModules.WMDatabaseBridge.getRandomIds 36 | const nativeRandomId: () => string = isV2Available ? nativeRandomId_v2 : nativeRandomId_v1 37 | 38 | export default nativeRandomId 39 | -------------------------------------------------------------------------------- /src/utils/common/randomId/randomId_v2.native.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { NativeModules } from 'react-native' 3 | 4 | let randomIds = [] 5 | let cur = 9999 6 | 7 | // NOTE: This is 2x faster thn Math.random on iOS (6x faster than _v1) 8 | // Should be ported to Java too… or better yet, implemented in JSI 9 | export default function nativeRandomId_v2(): string { 10 | if (cur >= 64) { 11 | randomIds = NativeModules.WMDatabaseBridge.getRandomIds().split(',') 12 | cur = 0 13 | } 14 | 15 | return randomIds[cur++] 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/fp/Result/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // lightweight type-only Result (Success(T) | Error) monad 4 | export type Result = $Exact<{ value: T }> | $Exact<{ error: Error }> 5 | 6 | export type ResultCallback = (Result) => void 7 | 8 | export function toPromise(withCallback: (ResultCallback) => void): Promise { 9 | return new Promise((resolve, reject) => { 10 | withCallback((result) => { 11 | if (result.error) { 12 | reject(result.error) 13 | } 14 | 15 | // $FlowFixMe - yes, you do have a value 16 | resolve(result.value) 17 | }) 18 | }) 19 | } 20 | 21 | export function fromPromise(promise: Promise, callback: ResultCallback): void { 22 | promise.then( 23 | (value) => callback({ value }), 24 | (error) => callback({ error }), 25 | ) 26 | } 27 | 28 | export function mapValue(mapper: (T) => U, result: Result): Result { 29 | if (result.error) { 30 | return result 31 | } 32 | 33 | try { 34 | return { value: mapper(result.value) } 35 | } catch (error) { 36 | return { error } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/fp/allPass/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function allPass(predicates: Array<(T) => boolean>): (T) => boolean { 4 | const len = predicates.length 5 | return (obj) => { 6 | for (let i = 0; i < len; i++) { 7 | if (!predicates[i](obj)) { 8 | return false 9 | } 10 | } 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/fp/allPromises/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const allPromises = (action: (T) => Promise, promises: T[]): Promise => 4 | Promise.all(promises.map(action)) 5 | 6 | export default allPromises 7 | -------------------------------------------------------------------------------- /src/utils/fp/allPromisesObj/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | type UnpackPromise = (promise: Promise) => T 4 | 5 | export default function allPromisesObj }>( 6 | promisesObj: Spec, 7 | // $FlowFixMe 8 | ): Promise<$ObjMap> { 9 | return new Promise((resolve, reject) => { 10 | const keys = Object.keys(promisesObj) 11 | const len = keys.length 12 | 13 | Promise.all(Object.values(promisesObj)).then((result) => { 14 | const resultObj: { [string]: mixed } = {} 15 | for (let i = 0; i < len; i++) { 16 | resultObj[keys[i]] = result[i] 17 | } 18 | // $FlowFixMe 19 | resolve(resultObj) 20 | }, reject) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/fp/anyPass/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function anyPass(predicates: Array<(T) => boolean>): (T) => boolean { 4 | const len = predicates.length 5 | return (obj) => { 6 | for (let i = 0; i < len; i++) { 7 | if (predicates[i](obj)) { 8 | return true 9 | } 10 | } 11 | return false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/fp/areRecordsEqual/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // NOTE: Only use with records! Not guaranteed to work correctly if keys have undefineds as values 4 | export default function areRecordsEqual(left: T, right: T): boolean { 5 | if (left === right) { 6 | return true 7 | } 8 | 9 | const leftKeys = Object.keys(left) 10 | const leftKeysLen = leftKeys.length 11 | 12 | if (leftKeysLen !== Object.keys(right).length) { 13 | return false 14 | } 15 | 16 | let key 17 | for (let i = 0; i < leftKeysLen; i++) { 18 | key = leftKeys[i] 19 | if (left[key] !== right[key]) { 20 | return false 21 | } 22 | } 23 | return true 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/fp/arrayDifference/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type ArrayDiff = $Exact<{ added: T[], removed: T[] }> 4 | 5 | const arrayDifference = (previousList: T[], nextList: T[]): ArrayDiff => { 6 | const previous = new Set(previousList) 7 | const next = new Set(nextList) 8 | const added = [] 9 | const removed = [] 10 | 11 | let item 12 | for (let i = 0, len = previousList.length; i < len; i++) { 13 | item = previousList[i] 14 | if (!next.has(item)) { 15 | removed.push(item) 16 | } 17 | } 18 | 19 | for (let i = 0, len = nextList.length; i < len; i++) { 20 | item = nextList[i] 21 | if (!previous.has(item)) { 22 | added.push(item) 23 | } 24 | } 25 | 26 | return { added, removed } 27 | } 28 | 29 | export default arrayDifference 30 | -------------------------------------------------------------------------------- /src/utils/fp/arrayOrSpread/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import invariant from '../../common/invariant' 3 | import logger from '../../common/logger' 4 | 5 | // Note: we have to write out three separate meanings of OnFunction because of a Babel bug 6 | // (it will remove the parentheses, changing the meaning of the flow type) 7 | type _SpreadFn = (...args: $ReadOnlyArray) => Return 8 | type _ArrayFn = (args: $ReadOnlyArray) => Return 9 | 10 | // This function takes either (...args: Arg[]) spread or (args: Arg[]) array argument 11 | export type ArrayOrSpreadFn = _SpreadFn & _ArrayFn 12 | 13 | // This helper makes it easy to make functions that can take either spread or array arguments 14 | export default function fromArrayOrSpread( 15 | args: any[], 16 | debugName: string, 17 | debugArgName: string, 18 | ): Arg[] { 19 | if (Array.isArray(args[0])) { 20 | invariant( 21 | args.length === 1, 22 | `${debugName} should be called with either a list of '${debugArgName}' arguments or a single array, but multiple arrays were passed`, 23 | ) 24 | return args[0] 25 | } 26 | 27 | if (process.env.NODE_ENV !== 'production') { 28 | if (args.length > 200) { 29 | logger.warn( 30 | `${debugName} was called with ${args.length} arguments. It might be a performance bug. For very large arrays, pass a single array instead of a spread to avoid "Maximum callstack exceeded" error.`, 31 | ) 32 | } 33 | } 34 | 35 | return args 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/fp/filterObj/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-restricted-syntax */ 3 | /* eslint-disable guard-for-in */ 4 | 5 | type FilterObj2 = boolean>( 6 | fn: Fn, 7 | obj: Obj, 8 | ) => $Shape 9 | type FilterObjCur = boolean>( 10 | fn: Fn, 11 | ) => (Obj) => $Shape 12 | type FilterObj = FilterObj2 & FilterObjCur 13 | 14 | function filterObj(predicate: (any, any, any) => any, obj: {}): any { 15 | if (arguments.length === 1) { 16 | // $FlowFixMe 17 | return (_obj) => filterObj(predicate, _obj) 18 | } 19 | const result: { [string]: any } = {} 20 | let value 21 | for (const prop in obj) { 22 | value = obj[prop] 23 | if (predicate(value, prop, obj)) { 24 | result[prop] = value 25 | } 26 | } 27 | return result 28 | } 29 | 30 | export default ((filterObj: any): FilterObj) 31 | -------------------------------------------------------------------------------- /src/utils/fp/fromPairs/index.js: -------------------------------------------------------------------------------- 1 | // inspired by ramda and rambda 2 | /* eslint-disable */ 3 | 4 | export default function fromPairs(pairs) { 5 | const result = {} 6 | 7 | for (var i = 0, l = pairs.length; i < l; i++) { 8 | result[pairs[i][0]] = pairs[i][1] 9 | } 10 | 11 | return result 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/fp/groupBy/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function groupBy( 4 | predicate: (Val) => Key, 5 | ): (list: Val[]) => { [Key]: Val[] } { 6 | return (list) => { 7 | const groupped: { [Key]: Val[] } = {} 8 | let item 9 | let key 10 | let group 11 | for (let i = 0, len = list.length; i < len; i++) { 12 | item = list[i] 13 | key = predicate(item) 14 | group = groupped[key] 15 | if (group) { 16 | group.push(item) 17 | } else { 18 | groupped[key] = [item] 19 | } 20 | } 21 | return groupped 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/fp/identicalArrays/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function identicalArrays(left: V, right: V): boolean { 4 | if (left.length !== right.length) { 5 | return false 6 | } 7 | 8 | for (let i = 0, len = left.length; i < len; i += 1) { 9 | if (left[i] !== right[i]) { 10 | return false 11 | } 12 | } 13 | 14 | return true 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/fp/identity/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // inspired by rambda and ramda 3 | 4 | export default function identity(value: T): T { 5 | return value 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/fp/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as groupBy } from './groupBy' 4 | export { default as allPromises } from './allPromises' 5 | export { default as identicalArrays } from './identicalArrays' 6 | export { default as isObj } from './isObj' 7 | export { default as noop } from './noop' 8 | export type { ArrayDiff } from './arrayDifference' 9 | export { default as fromPairs } from './fromPairs' 10 | export { default as toPairs } from './toPairs' 11 | export { default as unnest } from './unnest' 12 | export { default as identity } from './identity' 13 | export { default as unique } from './unique' 14 | export { default as keys } from './keys' 15 | export { default as values } from './values' 16 | export { default as pipe } from './pipe' 17 | export { default as mapObj } from './mapObj' 18 | export { default as filterObj } from './filterObj' 19 | export type { ArrayOrSpreadFn } from './arrayOrSpread' 20 | export { default as fromArrayOrSpread } from './arrayOrSpread' 21 | -------------------------------------------------------------------------------- /src/utils/fp/isObj/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function isObj(maybeObject: T): boolean { 4 | return maybeObject !== null && typeof maybeObject === 'object' && !Array.isArray(maybeObject) 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/fp/keys/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function keys(obj: T): Array<$Keys> { 4 | // $FlowFixMe 5 | return Object.keys(obj) 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/fp/likeToRegexp/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function likeToRegexp(likeQuery: string): RegExp { 4 | const regexp = `^${likeQuery}$`.replace(/%/g, '.*').replace(/_/g, '.') 5 | return new RegExp(regexp, 'is') 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/fp/mapObj/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-restricted-syntax */ 3 | /* eslint-disable guard-for-in */ 4 | 5 | type MapObj2 = U>( 6 | fn: Fn, 7 | obj: Obj, 8 | ) => $ObjMap U> 9 | type MapObjCur = U>( 10 | fn: Fn, 11 | ) => (Obj) => $ObjMap U> 12 | type MapObj = MapObj2 & MapObjCur 13 | 14 | function mapObj(fn: (any, string, any) => any, obj: {}): any { 15 | if (arguments.length === 1) { 16 | // $FlowFixMe 17 | return (_obj) => mapObj(fn, _obj) 18 | } 19 | const result: { [string]: any } = {} 20 | for (const prop in obj) { 21 | result[prop] = fn(obj[prop], prop, obj) 22 | } 23 | return result 24 | } 25 | 26 | export default ((mapObj: any): MapObj) 27 | -------------------------------------------------------------------------------- /src/utils/fp/noop/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Does nothing 4 | export default function noop(): void {} 5 | -------------------------------------------------------------------------------- /src/utils/fp/pipe/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | type Pipe = (( 4 | ab: (A) => B, 5 | bc: (B) => C, 6 | cd: (C) => D, 7 | de: (D) => E, 8 | ef: (E) => F, 9 | fg: (F) => G, 10 | ) => (A) => G) & 11 | (( 12 | ab: (A) => B, 13 | bc: (B) => C, 14 | cd: (C) => D, 15 | de: (D) => E, 16 | ef: (E) => F, 17 | ) => (A) => F) & 18 | ((ab: (A) => B, bc: (B) => C, cd: (C) => D, de: (D) => E) => (A) => E) & 19 | ((ab: (A) => B, bc: (B) => C, cd: (C) => D) => (A) => D) & 20 | ((ab: (A) => B, bc: (B) => C) => (A) => C) & 21 | ((ab: (A) => B) => (A) => B) 22 | 23 | function pipe(...fns: ((any) => any)[]): (any) => any { 24 | const fnsLen = fns.length 25 | return (...args) => { 26 | let result 27 | 28 | if (fnsLen) { 29 | result = fns[0](...args) 30 | for (let i = 1; i < fnsLen; i++) { 31 | result = fns[i](result) 32 | } 33 | } 34 | 35 | return result 36 | } 37 | } 38 | 39 | export default (pipe: Pipe) 40 | -------------------------------------------------------------------------------- /src/utils/fp/sortBy/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function sortBy(sorter: (T) => U, list: T[]): T[] { 4 | const clone = list.slice() 5 | let a 6 | let b 7 | return clone.sort((left, right) => { 8 | a = sorter(left) 9 | b = sorter(right) 10 | 11 | if (a === b) { 12 | return 0 13 | } 14 | // $FlowFixMe 15 | return a < b ? -1 : 1 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/fp/splitEvery/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function splitEvery(n: number, list: T[]): T[][] { 4 | const splitted = [] 5 | let position = 0 6 | const { length } = list 7 | while (position < length) { 8 | splitted.push(list.slice(position, (position += n))) 9 | } 10 | return splitted 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/fp/toPairs/index.js: -------------------------------------------------------------------------------- 1 | // inspired by ramda and rambda 2 | /* eslint-disable */ 3 | 4 | export default function toPairs(obj) { 5 | var pairs = [] 6 | 7 | if (obj) { 8 | var keys = Object.keys(obj) 9 | 10 | for (var i = 0, len = keys.length; i < len; i++) { 11 | var prop = keys[i] 12 | var value = obj[prop] 13 | if (prop in obj) { 14 | pairs[i] = [prop, value] 15 | } 16 | } 17 | } 18 | 19 | return pairs 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/fp/unique/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Returns a list of unique elements, compared by identity (===) 4 | // This is a replacement for rambdax uniq() which is based on slow equals() 5 | export default function unique(list: T[]): T[] { 6 | const result: T[] = [] 7 | 8 | for (let i = 0, len = list.length; i < len; i += 1) { 9 | const value = list[i] 10 | 11 | let isUnique = true 12 | for (let j = 0, resultLen = result.length; j < resultLen; j += 1) { 13 | if (value === result[j]) { 14 | isUnique = false 15 | break 16 | } 17 | } 18 | if (isUnique) { 19 | result.push(value) 20 | } 21 | } 22 | 23 | return result 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/fp/unnest/index.js: -------------------------------------------------------------------------------- 1 | // inspired by ramda and rambda 2 | /* eslint-disable */ 3 | 4 | export default function unnest(arr) { 5 | var result = [] 6 | 7 | for (var i = 0, l = arr.length; i < l; i++) { 8 | var value = arr[i] 9 | 10 | if (Array.isArray(value)) { 11 | result = result.concat(value) 12 | } else { 13 | result.push(value) 14 | } 15 | } 16 | 17 | return result 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/fp/values/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function values(obj: O): T[] { 4 | // $FlowFixMe 5 | return Object.values(obj) 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/rx/__wmelonRxShim/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // NOTE: All Rx imports in WatermelonDB MUST go through this file 4 | // this is a magic shim that can be replaced via babel onto another shim that imports Rx files 5 | // from different locations 6 | // This allows manual tree shaking on React Native 7 | 8 | export { 9 | // classes 10 | Observable, 11 | Subject, 12 | ReplaySubject, 13 | BehaviorSubject, 14 | // observables 15 | of, 16 | merge, 17 | defer, 18 | } from 'rxjs' 19 | export { 20 | // operators 21 | multicast, 22 | distinctUntilChanged, 23 | map, 24 | switchMap, 25 | throttleTime, 26 | startWith, 27 | } from 'rxjs/operators' 28 | export type { ConnectableObservable } from 'rxjs' 29 | -------------------------------------------------------------------------------- /src/utils/rx/__wmelonRxShimESM2015/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // classes 4 | export { Observable } from 'rxjs/dist/esm5/internal/Observable' 5 | export { Subject } from 'rxjs/dist/esm5/internal/Subject' 6 | export { ReplaySubject } from 'rxjs/dist/esm5/internal/ReplaySubject' 7 | export { BehaviorSubject } from 'rxjs/dist/esm5/internal/BehaviorSubject' 8 | 9 | // observables 10 | export { of } from 'rxjs/dist/esm5/internal/observable/of' 11 | export { merge } from 'rxjs/dist/esm5/internal/observable/merge' 12 | export { defer } from 'rxjs/dist/esm5/internal/observable/defer' 13 | 14 | // operators 15 | export { multicast } from 'rxjs/dist/esm5/internal/operators/multicast' 16 | export { distinctUntilChanged } from 'rxjs/dist/esm5/internal/operators/distinctUntilChanged' 17 | export { map } from 'rxjs/dist/esm5/internal/operators/map' 18 | export { switchMap } from 'rxjs/dist/esm5/internal/operators/switchMap' 19 | export { throttleTime } from 'rxjs/dist/esm5/internal/operators/throttleTime' 20 | export { startWith } from 'rxjs/dist/esm5/internal/operators/startWith' 21 | -------------------------------------------------------------------------------- /src/utils/rx/cacheWhileConnected/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { type Observable, distinctUntilChanged } from '../__wmelonRxShim' 4 | import publishReplayLatestWhileConnected from '../publishReplayLatestWhileConnected' 5 | 6 | // Equivalent to observable |> distinctUntilChanged |> publishReplayLatestWhileConnected |> refCount 7 | // 8 | // Creates an observable that shares the connection with and replays the latest value from the underlying 9 | // observable, and skips emissions that are the same as the previous one 10 | 11 | export default function cacheWhileConnected(source: Observable): Observable { 12 | return source.pipe(distinctUntilChanged(), publishReplayLatestWhileConnected).refCount() 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/rx/doOnDispose/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Observable } from '../__wmelonRxShim' 4 | 5 | // Performs an action when Observable is disposed; analogous to `Observable.do` 6 | 7 | export default function doOnDispose(onDispose: () => void): (Observable) => Observable { 8 | return (source) => 9 | Observable.create((observer) => { 10 | // $FlowFixMe 11 | const subscription = source.subscribe(observer) 12 | return () => { 13 | subscription.unsubscribe() 14 | onDispose() 15 | } 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/rx/doOnSubscribe/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { defer, type Observable } from '../__wmelonRxShim' 4 | 5 | // Performs an action when Observable is subscribed to; analogous to `Observable.do` 6 | 7 | export default function doOnSubscribe( 8 | onSubscribe: () => void, 9 | ): (Observable) => Observable { 10 | return (source) => 11 | defer(() => { 12 | onSubscribe() 13 | return source 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/rx/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as cacheWhileConnected } from './cacheWhileConnected' 4 | // export { default as doOnDispose } from './doOnDispose' 5 | // export { default as doOnSubscribe } from './doOnSubscribe' 6 | export { default as publishReplayLatestWhileConnected } from './publishReplayLatestWhileConnected' 7 | 8 | export { 9 | // classes 10 | Observable, 11 | Subject, 12 | ReplaySubject, 13 | BehaviorSubject, 14 | // observables 15 | of, 16 | merge, 17 | defer, 18 | // operators 19 | multicast, 20 | distinctUntilChanged, 21 | map, 22 | switchMap, 23 | throttleTime, 24 | startWith, 25 | } from './__wmelonRxShim' 26 | export type { ConnectableObservable } from './__wmelonRxShim' 27 | -------------------------------------------------------------------------------- /src/utils/rx/publishReplayLatestWhileConnected/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { 4 | type ConnectableObservable, 5 | type Observable, 6 | ReplaySubject, 7 | multicast, 8 | } from '../__wmelonRxShim' 9 | 10 | // Creates a Connectable observable, that, while connected, replays the latest emission 11 | // upon subscription. When disconnected, the replay cache is cleared. 12 | 13 | export default function publishReplayLatestWhileConnected( 14 | source: Observable, 15 | ): ConnectableObservable { 16 | return source.pipe(multicast(() => new ReplaySubject(1))) 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/subscriptions/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as SharedSubscribable } from './SharedSubscribable' 4 | export type { Unsubscribe } from './type' 5 | -------------------------------------------------------------------------------- /src/utils/subscriptions/type.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type Unsubscribe = () => void 4 | -------------------------------------------------------------------------------- /sync/SyncLogger/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { SyncLog } from '../index' 2 | 3 | export default class SyncLogger { 4 | _limit: number 5 | 6 | _logs: SyncLog[] 7 | 8 | constructor(limit: number) 9 | 10 | newLog(): SyncLog 11 | 12 | get logs(): SyncLog[] 13 | 14 | get formattedLogs(): string 15 | } 16 | -------------------------------------------------------------------------------- /sync/debugPrintChanges/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function debugPrintChanges(changes: null, isPush: boolean): void 2 | -------------------------------------------------------------------------------- /sync/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import type { ColumnName } from '..' 2 | import type { RawRecord } from '../RawRecord' 3 | 4 | export function addToRawSet(rawSet: string, value: string): string 5 | 6 | export function setRawColumnChange(rawRecord: RawRecord, columnName: ColumnName): void 7 | -------------------------------------------------------------------------------- /sync/helpers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.addToRawSet = addToRawSet; 5 | exports.setRawColumnChange = setRawColumnChange; 6 | function addToRawSet(rawSet, value) { 7 | var array = rawSet ? rawSet.split(',') : []; 8 | var set = new Set(array); 9 | set.add(value); 10 | return Array.from(set).join(','); 11 | } 12 | 13 | // Mutates `rawRecord` to mark `columName` as modified for sync purposes 14 | function setRawColumnChange(rawRecord, columnName) { 15 | rawRecord._changed = addToRawSet(rawRecord._changed, columnName); 16 | if ('created' !== rawRecord._status) { 17 | rawRecord._status = 'updated'; 18 | } 19 | } -------------------------------------------------------------------------------- /sync/impl/applyRemote.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..' 2 | 3 | import type { SyncDatabaseChangeSet, SyncLog, SyncConflictResolver } from '../index' 4 | 5 | export default function applyRemoteChanges( 6 | remoteChanges: SyncDatabaseChangeSet, 7 | opts: { 8 | db: Database, 9 | sendCreatedAsUpdated: boolean, 10 | log?: SyncLog, 11 | conflictResolver?: SyncConflictResolver, 12 | _unsafeBatchPerCollection?: boolean, 13 | } 14 | ): Promise 15 | -------------------------------------------------------------------------------- /sync/impl/fetchLocal.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database } from '../..' 2 | 3 | import type { SyncLocalChanges } from '../index' 4 | 5 | export default function fetchLocalChanges(db: Database): Promise 6 | 7 | export function hasUnsyncedChanges(db: Database): Promise 8 | -------------------------------------------------------------------------------- /sync/impl/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import type { Model, Collection, Database } from '../..' 2 | import type { RawRecord, DirtyRaw } from '../../RawRecord' 3 | import type { SyncLog, SyncDatabaseChangeSet, SyncConflictResolver } from '../index' 4 | 5 | // Returns raw record with naive solution to a conflict based on local `_changed` field 6 | // This is a per-column resolution algorithm. All columns that were changed locally win 7 | // and will be applied on top of the remote version. 8 | export function resolveConflict(local: RawRecord, remote: DirtyRaw): DirtyRaw 9 | 10 | export function prepareCreateFromRaw( 11 | collection: Collection, 12 | dirtyRaw: DirtyRaw, 13 | ): T 14 | 15 | export function prepareUpdateFromRaw( 16 | record: T, 17 | updatedDirtyRaw: DirtyRaw, 18 | log?: SyncLog, 19 | conflictResolver?: SyncConflictResolver, 20 | ): T 21 | 22 | export function prepareMarkAsSynced(record: T): T 23 | 24 | export function ensureSameDatabase(database: Database, initialResetCount: number): void 25 | 26 | export const isChangeSetEmpty: (_: SyncDatabaseChangeSet) => boolean 27 | 28 | export const changeSetCount: (_: SyncDatabaseChangeSet) => number 29 | -------------------------------------------------------------------------------- /sync/impl/index.d.ts: -------------------------------------------------------------------------------- 1 | import { $Exact } from '../../types' 2 | import type { Database } from '../..' 3 | 4 | import type { Timestamp, SyncLog } from '../index' 5 | import type { SchemaVersion } from '../../Schema' 6 | import type { MigrationSyncChanges } from '../../Schema/migrations/getSyncChanges' 7 | 8 | export { default as applyRemoteChanges } from './applyRemote' 9 | export { default as fetchLocalChanges, hasUnsyncedChanges } from './fetchLocal' 10 | export { default as markLocalChangesAsSynced } from './markAsSynced' 11 | 12 | export function getLastPulledAt(database: Database): Promise 13 | 14 | export function setLastPulledAt(database: Database, timestamp: Timestamp): Promise 15 | 16 | export function getLastPulledSchemaVersion(database: Database): Promise 17 | 18 | export function setLastPulledSchemaVersion( 19 | database: Database, 20 | version: SchemaVersion, 21 | ): Promise 22 | 23 | type MigrationInfo = $Exact<{ 24 | schemaVersion: SchemaVersion 25 | migration: MigrationSyncChanges 26 | shouldSaveSchemaVersion: boolean 27 | }> 28 | 29 | export function getMigrationInfo( 30 | database: Database, 31 | log?: SyncLog, 32 | lastPulledAt?: Timestamp, 33 | migrationsEnabledAtVersion?: SchemaVersion, 34 | ): Promise 35 | -------------------------------------------------------------------------------- /sync/impl/markAsSynced.d.ts: -------------------------------------------------------------------------------- 1 | import type { Database, Model, TableName } from '../..' 2 | 3 | import type { SyncLocalChanges, SyncRejectedIds } from '../index' 4 | 5 | export default function markLocalChangesAsSynced( 6 | db: Database, 7 | syncedLocalChanges: SyncLocalChanges, 8 | rejectedIds?: SyncRejectedIds, 9 | ): Promise 10 | -------------------------------------------------------------------------------- /sync/impl/synchronize.d.ts: -------------------------------------------------------------------------------- 1 | import type { SyncArgs } from '../index' 2 | 3 | export default function synchronize({ 4 | database, 5 | pullChanges, 6 | onDidPullChanges, 7 | pushChanges, 8 | sendCreatedAsUpdated, 9 | migrationsEnabledAtVersion, 10 | log, 11 | conflictResolver, 12 | _unsafeBatchPerCollection, 13 | unsafeTurbo, 14 | }: SyncArgs): Promise 15 | -------------------------------------------------------------------------------- /sync/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.hasUnsyncedChanges = hasUnsyncedChanges; 5 | exports.synchronize = synchronize; 6 | // TODO: JSDoc'ify this 7 | /** 8 | * Synchronizes database with a remote server 9 | * 10 | * See docs for more details 11 | */ 12 | function synchronize(args) { 13 | return new Promise(function ($return, $error) { 14 | var synchronizeImpl; 15 | var $Try_1_Post = function () { 16 | try { 17 | return $return(); 18 | } catch ($boundEx) { 19 | return $error($boundEx); 20 | } 21 | }; 22 | var $Try_1_Catch = function (error) { 23 | try { 24 | args.log && (args.log.error = error); 25 | throw error; 26 | } catch ($boundEx) { 27 | return $error($boundEx); 28 | } 29 | }; 30 | try { 31 | synchronizeImpl = require('./impl/synchronize').default; 32 | return Promise.resolve(synchronizeImpl(args)).then(function () { 33 | try { 34 | return $Try_1_Post(); 35 | } catch ($boundEx) { 36 | return $Try_1_Catch($boundEx); 37 | } 38 | }, $Try_1_Catch); 39 | } catch (error) { 40 | $Try_1_Catch(error) 41 | } 42 | }); 43 | } 44 | 45 | /** 46 | * Returns `true` if database has any unsynced changes. 47 | * 48 | * Use this to check if you can safely log out (delete the database) 49 | */ 50 | function hasUnsyncedChanges({ 51 | database: database 52 | }) { 53 | return require('./impl').hasUnsyncedChanges(database); 54 | } -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | export type $Shape = T 2 | 3 | export type $NonMaybeType = T 4 | 5 | export type $ObjMap = { [K in keyof O]: T } 6 | 7 | export type $Exact = Type 8 | 9 | export type $RE = Readonly<$Exact> 10 | 11 | export type $Keys = { k: keyof Type } 12 | 13 | export type Array = Type[] 14 | 15 | // TODO: FIX TS 16 | export type $Call = any 17 | 18 | export type $ReadOnlyArray = T[] 19 | 20 | export type Class = new (...args: any[]) => T 21 | -------------------------------------------------------------------------------- /types.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /utils/common/connectionTag/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type ConnectionTag = number 4 | 5 | export default function connectionTag(): ConnectionTag 6 | -------------------------------------------------------------------------------- /utils/common/connectionTag/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = connectionTag; 5 | var previousTag = 0; 6 | function connectionTag() { 7 | previousTag += 1; 8 | return previousTag; 9 | } -------------------------------------------------------------------------------- /utils/common/deepFreeze/index.d.ts: -------------------------------------------------------------------------------- 1 | // Deep-freezes an object, but DOES NOT handle cycles 2 | export default function deepFreeze(object: T): T 3 | -------------------------------------------------------------------------------- /utils/common/deepFreeze/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = deepFreeze; 6 | var _invariant = _interopRequireDefault(require("../invariant")); 7 | // Deep-freezes an object, but DOES NOT handle cycles 8 | function deepFreeze(object) { 9 | (0, _invariant.default)(object && 'object' === typeof object, 'Invalid attempt to deepFreeze not-an-Object'); 10 | Object.getOwnPropertyNames(object).forEach(function (name) { 11 | var value = object[name]; 12 | if (value && 'object' === typeof value) { 13 | deepFreeze(value); 14 | } 15 | }); 16 | return Object.freeze(object); 17 | } -------------------------------------------------------------------------------- /utils/common/deprecated/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function deprecated(name: string, deprecationInfo: string): void 2 | -------------------------------------------------------------------------------- /utils/common/deprecated/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = deprecated; 6 | var _logger = _interopRequireDefault(require("../logger")); 7 | var deprecationsReported = {}; 8 | function deprecated(name, deprecationInfo) { 9 | if (!deprecationsReported[name]) { 10 | deprecationsReported[name] = true; 11 | _logger.default.warn("DEPRECATION: ".concat(name, " is deprecated. ").concat(deprecationInfo, " See changelog & docs for more info.")); 12 | } 13 | } -------------------------------------------------------------------------------- /utils/common/devMeasureTime/index.d.ts: -------------------------------------------------------------------------------- 1 | export const getPreciseTime: () => number 2 | 3 | export function devMeasureTime(executeBlock: () => T): [T, number] 4 | 5 | export function devMeasureTimeAsync(executeBlock: () => Promise): Promise<[T, number]> 6 | -------------------------------------------------------------------------------- /utils/common/devMeasureTime/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.devMeasureTime = devMeasureTime; 5 | exports.devMeasureTimeAsync = devMeasureTimeAsync; 6 | exports.getPreciseTime = void 0; 7 | var getPreciseTimeFunction = function () { 8 | if ('undefined' !== typeof global && global.nativePerformanceNow) { 9 | return global.nativePerformanceNow; 10 | } else if ('undefined' !== typeof window && window.performance && window.performance.now) { 11 | return window.performance.now.bind(window.performance); 12 | } 13 | 14 | // $FlowFixMe 15 | return Date.now; 16 | }; 17 | var getPreciseTime = getPreciseTimeFunction(); 18 | exports.getPreciseTime = getPreciseTime; 19 | function devMeasureTime(executeBlock) { 20 | var start = getPreciseTime(); 21 | var result = executeBlock(); 22 | var time = getPreciseTime() - start; 23 | return [result, time]; 24 | } 25 | function devMeasureTimeAsync(executeBlock) { 26 | return new Promise(function ($return, $error) { 27 | var start, result, time; 28 | start = getPreciseTime(); 29 | return Promise.resolve(executeBlock()).then(function ($await_1) { 30 | try { 31 | result = $await_1; 32 | time = getPreciseTime() - start; 33 | return $return([result, time]); 34 | } catch ($boundEx) { 35 | return $error($boundEx); 36 | } 37 | }, $error); 38 | }); 39 | } -------------------------------------------------------------------------------- /utils/common/diagnosticError/index.d.ts: -------------------------------------------------------------------------------- 1 | export type DiagnosticErrorFunction = (_: string) => Error 2 | 3 | export function useCustomDiagnosticErrorFunction( 4 | diagnosticErrorFunction: DiagnosticErrorFunction, 5 | ): void 6 | 7 | export default function diagnosticError(errorMessage: string): Error 8 | -------------------------------------------------------------------------------- /utils/common/diagnosticError/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = diagnosticError; 5 | exports.useCustomDiagnosticErrorFunction = useCustomDiagnosticErrorFunction; 6 | var customDiagnosticErrorFunction = null; 7 | 8 | // Use this to replace default diagnosticError function to inject your custom logic 9 | // (e.g. only display errors in development, or log errors to external service) 10 | function useCustomDiagnosticErrorFunction(diagnosticErrorFunction) { 11 | customDiagnosticErrorFunction = diagnosticErrorFunction; 12 | } 13 | function diagnosticError(errorMessage) { 14 | if (customDiagnosticErrorFunction) { 15 | return customDiagnosticErrorFunction(errorMessage); 16 | } 17 | var error = new Error(errorMessage); 18 | 19 | // hides `diagnosticError` from RN stack trace 20 | error.framesToPop = 1; 21 | error.name = 'Diagnostic error'; 22 | return error; 23 | } -------------------------------------------------------------------------------- /utils/common/ensureSync/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function ensureSync(value: T): T 2 | -------------------------------------------------------------------------------- /utils/common/ensureSync/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = ensureSync; 6 | var _invariant = _interopRequireDefault(require("../invariant")); 7 | // Throws if passed value is a Promise 8 | // Otherwise, returns the passed value as-is. 9 | // 10 | // Use to ensure API users aren't passing async functions 11 | function ensureSync(value) { 12 | (0, _invariant.default)(!(value instanceof Promise), 'Unexpected Promise. Passed function should be synchronous.'); 13 | return value; 14 | } -------------------------------------------------------------------------------- /utils/common/index.d.ts: -------------------------------------------------------------------------------- 1 | export { getPreciseTime, devMeasureTime, devMeasureTimeAsync } from './devMeasureTime' 2 | export { default as randomId } from './randomId' 3 | export { default as makeDecorator } from './makeDecorator' 4 | export { default as ensureSync } from './ensureSync' 5 | export { default as invariant } from './invariant' 6 | export { default as logError } from './logError' 7 | export { default as logger } from './logger' 8 | export { default as isRN } from './isRN' 9 | export { default as deprecated } from './deprecated' 10 | export { default as connectionTag } from './connectionTag' 11 | export type { ConnectionTag } from './connectionTag' 12 | -------------------------------------------------------------------------------- /utils/common/invariant/index.d.ts: -------------------------------------------------------------------------------- 1 | import diagnosticError from '../diagnosticError' 2 | 3 | export default function invariant(condition: any, errorMessage?: string): void 4 | -------------------------------------------------------------------------------- /utils/common/invariant/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = invariant; 6 | var _diagnosticError = _interopRequireDefault(require("../diagnosticError")); 7 | // If `condition` is falsy, throws an Error with the passed message 8 | function invariant(condition, errorMessage) { 9 | if (!condition) { 10 | var error = (0, _diagnosticError.default)(errorMessage || 'Broken invariant'); 11 | error.framesToPop += 1; 12 | throw error; 13 | } 14 | } -------------------------------------------------------------------------------- /utils/common/isRN/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const isRN: boolean 2 | 3 | export default isRN 4 | -------------------------------------------------------------------------------- /utils/common/isRN/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | var _default = false; 6 | exports.default = _default; -------------------------------------------------------------------------------- /utils/common/isRN/index.native.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | var _default = true; 6 | exports.default = _default; -------------------------------------------------------------------------------- /utils/common/logError/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function logError(errorMessage: string): void 2 | -------------------------------------------------------------------------------- /utils/common/logError/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = logError; 6 | var _diagnosticError = _interopRequireDefault(require("../diagnosticError")); 7 | var _logger = _interopRequireDefault(require("../logger")); 8 | // Logs an Error to the console with the given message 9 | // 10 | // Use when a *recoverable* error occurs (so you don't want it to throw) 11 | function logError(errorMessage) { 12 | var error = (0, _diagnosticError.default)(errorMessage); 13 | error.framesToPop += 1; 14 | _logger.default.error(error); 15 | } -------------------------------------------------------------------------------- /utils/common/logger/index.d.ts: -------------------------------------------------------------------------------- 1 | declare class Logger { 2 | silent: boolean 3 | 4 | log(...messages: any[]): void 5 | 6 | warn(...messages: any[]): void 7 | 8 | error(...messages: any[]): void 9 | 10 | silence(): void 11 | } 12 | 13 | declare const logger: Logger 14 | 15 | export default logger 16 | -------------------------------------------------------------------------------- /utils/common/logger/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); 7 | /* eslint-disable no-console */ 8 | var formatMessages = function (messages) { 9 | var [first, ...other] = messages; 10 | return ['string' === typeof first ? "[\uD83C\uDF49] ".concat(first) : first].concat((0, _toConsumableArray2.default)(other)); 11 | }; 12 | var Logger = /*#__PURE__*/function () { 13 | function Logger() { 14 | this.silent = false; 15 | } 16 | var _proto = Logger.prototype; 17 | _proto.log = function log(...messages) { 18 | var _console; 19 | this.silent || (_console = console).log.apply(_console, (0, _toConsumableArray2.default)(formatMessages(messages))); 20 | }; 21 | _proto.warn = function warn(...messages) { 22 | var _console2; 23 | this.silent || (_console2 = console).warn.apply(_console2, (0, _toConsumableArray2.default)(formatMessages(messages))); 24 | }; 25 | _proto.error = function error(...messages) { 26 | var _console3; 27 | this.silent || (_console3 = console).error.apply(_console3, (0, _toConsumableArray2.default)(formatMessages(messages))); 28 | }; 29 | _proto.silence = function silence() { 30 | this.silent = true; 31 | }; 32 | return Logger; 33 | }(); 34 | var _default = new Logger(); 35 | exports.default = _default; -------------------------------------------------------------------------------- /utils/common/makeDecorator/index.d.ts: -------------------------------------------------------------------------------- 1 | export type Descriptor = Object 2 | export type RawDecorator = (target: Object, key: string, descriptor: Descriptor) => Descriptor 3 | export type Decorator = (...any: any[]) => Descriptor | RawDecorator 4 | 5 | export default function makeDecorator(decorator: (...any: any[]) => RawDecorator): Decorator 6 | -------------------------------------------------------------------------------- /utils/common/makeDecorator/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = makeDecorator; 5 | // Converts a function with signature `(args) => (target, key, descriptor)` to a decorator 6 | // that works both when called `@decorator foo` and with arguments, like `@decorator(arg) foo` 7 | function makeDecorator(decorator) { 8 | return function (...args) { 9 | // Decorator called with an argument, JS expects a decorator function 10 | if (3 > args.length) { 11 | return decorator.apply(void 0, args); 12 | } 13 | 14 | // Decorator called without an argument, JS expects a descriptor object 15 | return decorator().apply(void 0, args); 16 | }; 17 | } -------------------------------------------------------------------------------- /utils/common/memory/index.d.ts: -------------------------------------------------------------------------------- 1 | type Callback = () => void 2 | 3 | export function onLowMemory(callback: Callback): void 4 | 5 | export function _triggerOnLowMemory(): void 6 | -------------------------------------------------------------------------------- /utils/common/memory/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports._triggerOnLowMemory = _triggerOnLowMemory; 5 | exports.onLowMemory = onLowMemory; 6 | var lowMemoryCallbacks = []; 7 | function onLowMemory(callback) { 8 | lowMemoryCallbacks.push(callback); 9 | } 10 | 11 | // TODO: Not currently hooked up to anything 12 | function _triggerOnLowMemory() { 13 | lowMemoryCallbacks.forEach(function (callback) { 14 | return callback(); 15 | }); 16 | } -------------------------------------------------------------------------------- /utils/common/randomId/fallback.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = fallbackRandomId; 5 | var alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 6 | function fallbackRandomId() { 7 | var id = ''; 8 | var v = 0; 9 | for (var i = 0; 16 > i; i += 1) { 10 | v = Math.floor(62 * Math.random()); 11 | id += alphabet[v % 62]; 12 | } 13 | return id; 14 | } -------------------------------------------------------------------------------- /utils/common/randomId/index.d.ts: -------------------------------------------------------------------------------- 1 | declare let generator: () => string 2 | 3 | export const setGenerator: (newGenerator: () => string) => void 4 | 5 | export default generator 6 | -------------------------------------------------------------------------------- /utils/common/randomId/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.setGenerator = exports.default = void 0; 6 | var _randomId = _interopRequireDefault(require("./randomId")); 7 | var generator = _randomId.default; 8 | 9 | // NOTE: It's is only safe for the ID to contain [a-zA-Z0-9._]. It must not contain other characters 10 | // (especially '"\/$). Never, ever allow the ID to be set by the user w/o validating - this breaks security! 11 | var setGenerator = function (newGenerator) { 12 | if ('string' !== typeof newGenerator()) { 13 | throw new Error('RandomId generator function needs to return a string type.'); 14 | } 15 | generator = newGenerator; 16 | }; 17 | exports.setGenerator = setGenerator; 18 | var _default = function () { 19 | return generator(); 20 | }; 21 | exports.default = _default; -------------------------------------------------------------------------------- /utils/common/randomId/randomId.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | /* eslint-disable no-bitwise */ 6 | var alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 7 | var randomNumbers = new Uint8Array(256); 8 | var cur = 9999999; 9 | 10 | /*:: declare var globalThis: WindowProxy */ 11 | 12 | function cryptoRandomId() { 13 | var id = ''; 14 | var len = 0; 15 | var v = 0; 16 | while (16 > len) { 17 | if (256 > cur) { 18 | v = randomNumbers[cur] >> 2; 19 | cur++; 20 | if (62 > v) { 21 | id += alphabet[v]; 22 | len++; 23 | } 24 | } else { 25 | globalThis.crypto.getRandomValues(randomNumbers); 26 | cur = 0; 27 | } 28 | } 29 | return id; 30 | } 31 | var isCryptoAvailable = globalThis.crypto && globalThis.crypto.getRandomValues; 32 | var randomId = isCryptoAvailable ? cryptoRandomId : require('./fallback').default; 33 | var _default = randomId; 34 | exports.default = _default; -------------------------------------------------------------------------------- /utils/common/randomId/randomId.native.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = void 0; 6 | var _reactNative = require("react-native"); 7 | var _randomId_v = _interopRequireDefault(require("./randomId_v2.native")); 8 | /* eslint-disable no-bitwise */ 9 | var alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 10 | var randomNumbers = []; 11 | var cur = 9999999; 12 | 13 | // TODO: This is 3-5x slower than Math.random()-based implementation 14 | // Should be migrated to JSI, or simply implemented fully in native 15 | // (bridging is the bottleneck) 16 | function nativeRandomId_v1() { 17 | var id = ''; 18 | var len = 0; 19 | var v = 0; 20 | while (16 > len) { 21 | if (256 > cur) { 22 | v = randomNumbers[cur] >> 2; 23 | cur++; 24 | if (62 > v) { 25 | id += alphabet[v]; 26 | len++; 27 | } 28 | } else { 29 | randomNumbers = _reactNative.NativeModules.WMDatabaseBridge.getRandomBytes(256); 30 | cur = 0; 31 | } 32 | } 33 | return id; 34 | } 35 | var isV2Available = !!_reactNative.NativeModules.WMDatabaseBridge.getRandomIds; 36 | var nativeRandomId = isV2Available ? _randomId_v.default : nativeRandomId_v1; 37 | var _default = nativeRandomId; 38 | exports.default = _default; -------------------------------------------------------------------------------- /utils/common/randomId/randomId_v2.native.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = nativeRandomId_v2; 5 | var _reactNative = require("react-native"); 6 | var randomIds = []; 7 | var cur = 9999; 8 | 9 | // NOTE: This is 2x faster thn Math.random on iOS (6x faster than _v1) 10 | // Should be ported to Java too… or better yet, implemented in JSI 11 | function nativeRandomId_v2() { 12 | if (64 <= cur) { 13 | randomIds = _reactNative.NativeModules.WMDatabaseBridge.getRandomIds().split(','); 14 | cur = 0; 15 | } 16 | return randomIds[cur++]; 17 | } -------------------------------------------------------------------------------- /utils/fp/Result/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { $Exact } from '../../../types' 3 | 4 | // lightweight type-only Result (Success(T) | Error) monad 5 | export type Result = $Exact<{ value: T }> | $Exact<{ error: Error }> 6 | 7 | export type ResultCallback = (r: Result) => void 8 | 9 | export function toPromise(withCallback: (r: ResultCallback) => void): Promise 10 | 11 | export function fromPromise(promise: Promise, callback: ResultCallback): void 12 | 13 | export function mapValue(mapper: (_: T) => U, result: Result): Result 14 | -------------------------------------------------------------------------------- /utils/fp/Result/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.fromPromise = fromPromise; 5 | exports.mapValue = mapValue; 6 | exports.toPromise = toPromise; 7 | // lightweight type-only Result (Success(T) | Error) monad 8 | function toPromise(withCallback) { 9 | return new Promise(function (resolve, reject) { 10 | withCallback(function (result) { 11 | if (result.error) { 12 | reject(result.error); 13 | } 14 | 15 | // $FlowFixMe - yes, you do have a value 16 | resolve(result.value); 17 | }); 18 | }); 19 | } 20 | function fromPromise(promise, callback) { 21 | promise.then(function (value) { 22 | return callback({ 23 | value: value 24 | }); 25 | }, function (error) { 26 | return callback({ 27 | error: error 28 | }); 29 | }); 30 | } 31 | function mapValue(mapper, result) { 32 | if (result.error) { 33 | return result; 34 | } 35 | try { 36 | return { 37 | value: mapper(result.value) 38 | }; 39 | } catch (error) { 40 | return { 41 | error: error 42 | }; 43 | } 44 | } -------------------------------------------------------------------------------- /utils/fp/allPass/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function allPass(predicates: Array<(_: T) => boolean>): (__: T) => boolean 2 | -------------------------------------------------------------------------------- /utils/fp/allPass/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = allPass; 5 | function allPass(predicates) { 6 | var len = predicates.length; 7 | return function (obj) { 8 | for (var i = 0; i < len; i++) { 9 | if (!predicates[i](obj)) { 10 | return false; 11 | } 12 | } 13 | return true; 14 | }; 15 | } -------------------------------------------------------------------------------- /utils/fp/allPromises/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function (action: (_: T) => Promise, promises: T[]): Promise 2 | -------------------------------------------------------------------------------- /utils/fp/allPromises/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | var allPromises = function (action, promises) { 6 | return Promise.all(promises.map(action)); 7 | }; 8 | var _default = allPromises; 9 | exports.default = _default; -------------------------------------------------------------------------------- /utils/fp/allPromisesObj/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Key } from 'react' 4 | import { $ObjMap } from '../../../types' 5 | 6 | type UnpackPromise = (promise: Promise) => T 7 | 8 | export default function allPromisesObj }>( 9 | promisesObj: Spec, 10 | ): Promise<$ObjMap> 11 | -------------------------------------------------------------------------------- /utils/fp/allPromisesObj/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = allPromisesObj; 5 | function allPromisesObj(promisesObj 6 | // $FlowFixMe 7 | ) { 8 | return new Promise(function (resolve, reject) { 9 | var keys = Object.keys(promisesObj); 10 | var len = keys.length; 11 | Promise.all(Object.values(promisesObj)).then(function (result) { 12 | var resultObj = {}; 13 | for (var i = 0; i < len; i++) { 14 | resultObj[keys[i]] = result[i]; 15 | } 16 | // $FlowFixMe 17 | resolve(resultObj); 18 | }, reject); 19 | }); 20 | } -------------------------------------------------------------------------------- /utils/fp/anyPass/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function anyPass(predicates: Array<(_: T) => boolean>): (__: T) => boolean 2 | -------------------------------------------------------------------------------- /utils/fp/anyPass/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = anyPass; 5 | function anyPass(predicates) { 6 | var len = predicates.length; 7 | return function (obj) { 8 | for (var i = 0; i < len; i++) { 9 | if (predicates[i](obj)) { 10 | return true; 11 | } 12 | } 13 | return false; 14 | }; 15 | } -------------------------------------------------------------------------------- /utils/fp/areRecordsEqual/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // NOTE: Only use with records! Not guaranteed to work correctly if keys have undefineds as values 4 | export default function areRecordsEqual(left: T, right: T): boolean 5 | -------------------------------------------------------------------------------- /utils/fp/areRecordsEqual/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = areRecordsEqual; 5 | // NOTE: Only use with records! Not guaranteed to work correctly if keys have undefineds as values 6 | function areRecordsEqual(left, right) { 7 | if (left === right) { 8 | return true; 9 | } 10 | var leftKeys = Object.keys(left); 11 | var leftKeysLen = leftKeys.length; 12 | if (leftKeysLen !== Object.keys(right).length) { 13 | return false; 14 | } 15 | var key; 16 | for (var i = 0; i < leftKeysLen; i++) { 17 | key = leftKeys[i]; 18 | if (left[key] !== right[key]) { 19 | return false; 20 | } 21 | } 22 | return true; 23 | } -------------------------------------------------------------------------------- /utils/fp/arrayDifference/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { $Exact } from '../../../types' 4 | 5 | export type ArrayDiff = $Exact<{ added: T[]; removed: T[] }> 6 | 7 | export default function (previousList: T[], nextList: T[]): ArrayDiff 8 | -------------------------------------------------------------------------------- /utils/fp/arrayDifference/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | var arrayDifference = function (previousList, nextList) { 6 | var previous = new Set(previousList); 7 | var next = new Set(nextList); 8 | var added = []; 9 | var removed = []; 10 | var item; 11 | for (var i = 0, len = previousList.length; i < len; i++) { 12 | item = previousList[i]; 13 | if (!next.has(item)) { 14 | removed.push(item); 15 | } 16 | } 17 | for (var _i = 0, _len = nextList.length; _i < _len; _i++) { 18 | item = nextList[_i]; 19 | if (!previous.has(item)) { 20 | added.push(item); 21 | } 22 | } 23 | return { 24 | added: added, 25 | removed: removed 26 | }; 27 | }; 28 | var _default = arrayDifference; 29 | exports.default = _default; -------------------------------------------------------------------------------- /utils/fp/arrayOrSpread/index.d.ts: -------------------------------------------------------------------------------- 1 | type _SpreadFn = (...args: Arg[]) => Return 2 | type _ArrayFn = (args: Arg[]) => Return 3 | 4 | // This function takes either (...args: Arg[]) spread or (args: Arg[]) array argument 5 | export type ArrayOrSpreadFn = _SpreadFn & _ArrayFn 6 | -------------------------------------------------------------------------------- /utils/fp/arrayOrSpread/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = fromArrayOrSpread; 6 | var _invariant = _interopRequireDefault(require("../../common/invariant")); 7 | var _logger = _interopRequireDefault(require("../../common/logger")); 8 | // This helper makes it easy to make functions that can take either spread or array arguments 9 | function fromArrayOrSpread(args, debugName, debugArgName) { 10 | if (Array.isArray(args[0])) { 11 | (0, _invariant.default)(1 === args.length, "".concat(debugName, " should be called with either a list of '").concat(debugArgName, "' arguments or a single array, but multiple arrays were passed")); 12 | return args[0]; 13 | } 14 | if ('production' !== process.env.NODE_ENV) { 15 | if (200 < args.length) { 16 | _logger.default.warn("".concat(debugName, " was called with ").concat(args.length, " arguments. It might be a performance bug. For very large arrays, pass a single array instead of a spread to avoid \"Maximum callstack exceeded\" error.")); 17 | } 18 | } 19 | return args; 20 | } -------------------------------------------------------------------------------- /utils/fp/checkName/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import invariant from '../../common/invariant' 4 | import type { TableName, ColumnName } from '../../../Schema' 5 | 6 | // Asserts that `name` (table or column name) should be safe for inclusion in SQL queries 7 | // and Loki queries (JS objects) 8 | // 9 | // IMPORTANT: This should NEVER be used as the only line of defense! These checks may be incomplete. 10 | // Any table or column name passed anywhere near the database should be hardcoded or whitelisted. 11 | // This is a "defense in depth" type of check - checking for common mistakes in case library user 12 | // is not following safe coding practices or the primary defense fails. 13 | // 14 | // This will throw an error on: 15 | // - JavaScript Object prototype properties 16 | // - Magic Loki and SQLite column names 17 | // - names starting with __ 18 | // - names that are not essentially alphanumeric 19 | // 20 | // Note that for SQL, you always MUST wrap table/column names with `'name'`, otherwise query may fail 21 | // for some keywords 22 | // 23 | // Note that this doesn't throw for Watermelon builtins (id, _changed, _status...) 24 | 25 | // const safeNameCharacters = /^[a-zA-Z_]\w*$/ 26 | // const knownSafeNames: Set = new Set() 27 | 28 | export default function checkName | ColumnName>(name: T): T 29 | -------------------------------------------------------------------------------- /utils/fp/filterObj/index.d.ts: -------------------------------------------------------------------------------- 1 | import { $Shape } from '../../../types' 2 | 3 | type FilterObj2 = boolean>( 4 | fn: Fn, 5 | obj: Obj, 6 | ) => $Shape 7 | type FilterObjCur = boolean>( 8 | fn: Fn, 9 | ) => (_: Obj) => $Shape 10 | 11 | type FilterObj = FilterObj2 & FilterObjCur 12 | 13 | declare function filterObj(): FilterObj 14 | 15 | export default filterObj 16 | -------------------------------------------------------------------------------- /utils/fp/filterObj/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | /* eslint-disable no-restricted-syntax */ 6 | /* eslint-disable guard-for-in */ 7 | function filterObj(predicate, obj) { 8 | if (1 === arguments.length) { 9 | // $FlowFixMe 10 | return function (_obj) { 11 | return filterObj(predicate, _obj); 12 | }; 13 | } 14 | var result = {}; 15 | var value; 16 | for (var prop in obj) { 17 | value = obj[prop]; 18 | if (predicate(value, prop, obj)) { 19 | result[prop] = value; 20 | } 21 | } 22 | return result; 23 | } 24 | var _default = filterObj; 25 | exports.default = _default; -------------------------------------------------------------------------------- /utils/fp/fromPairs/index.d.ts: -------------------------------------------------------------------------------- 1 | // inspired by ramda and rambda 2 | 3 | type KeyValueIn = { [k: string]: O } 4 | type KeyValueOut = [string, O][] 5 | 6 | export default function fromPairs(pairs: KeyValueIn): KeyValueOut 7 | -------------------------------------------------------------------------------- /utils/fp/fromPairs/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = fromPairs; 5 | // inspired by ramda and rambda 6 | /* eslint-disable */ 7 | 8 | function fromPairs(pairs) { 9 | var result = {}; 10 | for (var i = 0, l = pairs.length; i < l; i++) { 11 | result[pairs[i][0]] = pairs[i][1]; 12 | } 13 | return result; 14 | } -------------------------------------------------------------------------------- /utils/fp/groupBy/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function groupBy( 2 | predicate: (_: Val) => Key, 3 | ): (list: Val[]) => { [k: string]: Val[] } 4 | -------------------------------------------------------------------------------- /utils/fp/groupBy/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = groupBy; 5 | function groupBy(predicate) { 6 | return function (list) { 7 | var groupped = {}; 8 | var item; 9 | var key; 10 | var group; 11 | for (var i = 0, len = list.length; i < len; i++) { 12 | item = list[i]; 13 | key = predicate(item); 14 | group = groupped[key]; 15 | if (group) { 16 | group.push(item); 17 | } else { 18 | groupped[key] = [item]; 19 | } 20 | } 21 | return groupped; 22 | }; 23 | } -------------------------------------------------------------------------------- /utils/fp/identicalArrays/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function identicalArrays(left: V, right: V): boolean 4 | -------------------------------------------------------------------------------- /utils/fp/identicalArrays/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = identicalArrays; 5 | function identicalArrays(left, right) { 6 | if (left.length !== right.length) { 7 | return false; 8 | } 9 | for (var i = 0, len = left.length; i < len; i += 1) { 10 | if (left[i] !== right[i]) { 11 | return false; 12 | } 13 | } 14 | return true; 15 | } -------------------------------------------------------------------------------- /utils/fp/identity/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | // inspired by rambda and ramda 3 | 4 | export default function identity(value: T): T 5 | -------------------------------------------------------------------------------- /utils/fp/identity/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = identity; 5 | // inspired by rambda and ramda 6 | function identity(value) { 7 | return value; 8 | } -------------------------------------------------------------------------------- /utils/fp/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as groupBy } from './groupBy' 4 | export { default as allPromises } from './allPromises' 5 | export { default as identicalArrays } from './identicalArrays' 6 | export { default as isObj } from './isObj' 7 | export { default as noop } from './noop' 8 | export type { ArrayDiff } from './arrayDifference' 9 | export { default as fromPairs } from './fromPairs' 10 | export { default as toPairs } from './toPairs' 11 | export { default as unnest } from './unnest' 12 | export { default as identity } from './identity' 13 | export { default as unique } from './unique' 14 | export { default as keys } from './keys' 15 | export { default as values } from './values' 16 | export { default as pipe } from './pipe' 17 | export { default as mapObj } from './mapObj' 18 | export { default as filterObj } from './filterObj' 19 | export type { ArrayOrSpreadFn } from './arrayOrSpread' 20 | // export { default as fromArrayOrSpread } from './arrayOrSpread' 21 | -------------------------------------------------------------------------------- /utils/fp/isObj/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function isObj(maybeObject: T): boolean 4 | -------------------------------------------------------------------------------- /utils/fp/isObj/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = isObj; 5 | function isObj(maybeObject) { 6 | return null !== maybeObject && 'object' === typeof maybeObject && !Array.isArray(maybeObject); 7 | } -------------------------------------------------------------------------------- /utils/fp/keys/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { $Keys, Array } from '../../../types' 4 | 5 | export default function keys(obj: T): Array<$Keys> 6 | -------------------------------------------------------------------------------- /utils/fp/keys/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = keys; 5 | function keys(obj) { 6 | // $FlowFixMe 7 | return Object.keys(obj); 8 | } -------------------------------------------------------------------------------- /utils/fp/likeToRegexp/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function likeToRegexp(likeQuery: string): RegExp 2 | -------------------------------------------------------------------------------- /utils/fp/likeToRegexp/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = likeToRegexp; 5 | function likeToRegexp(likeQuery) { 6 | var regexp = "^".concat(likeQuery, "$").replace(/%/g, '.*').replace(/_/g, '.'); 7 | return new RegExp(regexp, 'is'); 8 | } -------------------------------------------------------------------------------- /utils/fp/mapObj/index.d.ts: -------------------------------------------------------------------------------- 1 | import { $ObjMap } from '../../../types' 2 | 3 | type MapObj2 = U>( 4 | fn: Fn, 5 | obj: Obj, 6 | ) => $ObjMap U> 7 | type MapObjCur = U>( 8 | fn: Fn, 9 | ) => (_: Obj) => $ObjMap U> 10 | type MapObj = MapObj2 & MapObjCur 11 | 12 | declare function mapObj(fn: (a: any, b: string, c: any) => any, obj: {}): MapObj 13 | 14 | export default mapObj 15 | -------------------------------------------------------------------------------- /utils/fp/mapObj/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | /* eslint-disable no-restricted-syntax */ 6 | /* eslint-disable guard-for-in */ 7 | function mapObj(fn, obj) { 8 | if (1 === arguments.length) { 9 | // $FlowFixMe 10 | return function (_obj) { 11 | return mapObj(fn, _obj); 12 | }; 13 | } 14 | var result = {}; 15 | for (var prop in obj) { 16 | result[prop] = fn(obj[prop], prop, obj); 17 | } 18 | return result; 19 | } 20 | var _default = mapObj; 21 | exports.default = _default; -------------------------------------------------------------------------------- /utils/fp/noop/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Does nothing 4 | export default function noop(): void 5 | -------------------------------------------------------------------------------- /utils/fp/noop/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = noop; 5 | // Does nothing 6 | function noop() {} -------------------------------------------------------------------------------- /utils/fp/pipe/index.d.ts: -------------------------------------------------------------------------------- 1 | type Pipe = (( 2 | ab: (_: A) => B, 3 | bc: (_: B) => C, 4 | cd: (_: C) => D, 5 | de: (_: D) => E, 6 | ef: (_: E) => F, 7 | fg: (_: F) => G, 8 | ) => (_: A) => G) & 9 | (( 10 | ab: (_: A) => B, 11 | bc: (_: B) => C, 12 | cd: (_: C) => D, 13 | de: (_: D) => E, 14 | ef: (_: E) => F, 15 | ) => (_: A) => F) & 16 | ((ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D, de: (_: D) => E) => (_: A) => E) & 17 | ((ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D) => (_: A) => D) & 18 | ((ab: (_: A) => B, bc: (_: B) => C) => (_: A) => C) & 19 | ((ab: (_: A) => B) => (_: A) => B) 20 | 21 | declare function pipe(...fns: ((_: any) => any)[]): Pipe 22 | 23 | export default pipe 24 | -------------------------------------------------------------------------------- /utils/fp/pipe/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = void 0; 5 | function pipe(...fns) { 6 | var fnsLen = fns.length; 7 | return function (...args) { 8 | var result; 9 | if (fnsLen) { 10 | result = fns[0].apply(fns, args); 11 | for (var i = 1; i < fnsLen; i++) { 12 | result = fns[i](result); 13 | } 14 | } 15 | return result; 16 | }; 17 | } 18 | var _default = pipe; 19 | exports.default = _default; -------------------------------------------------------------------------------- /utils/fp/sortBy/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function sortBy(sorter: (_: T) => U, list: T[]): T[] 4 | -------------------------------------------------------------------------------- /utils/fp/sortBy/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = sortBy; 5 | function sortBy(sorter, list) { 6 | var clone = list.slice(); 7 | var a; 8 | var b; 9 | return clone.sort(function (left, right) { 10 | a = sorter(left); 11 | b = sorter(right); 12 | if (a === b) { 13 | return 0; 14 | } 15 | // $FlowFixMe 16 | return a < b ? -1 : 1; 17 | }); 18 | } -------------------------------------------------------------------------------- /utils/fp/splitEvery/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function splitEvery(n: number, list: T[]): T[][] 4 | -------------------------------------------------------------------------------- /utils/fp/splitEvery/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = splitEvery; 5 | function splitEvery(n, list) { 6 | var splitted = []; 7 | var position = 0; 8 | var { 9 | length: length 10 | } = list; 11 | while (position < length) { 12 | splitted.push(list.slice(position, position += n)); 13 | } 14 | return splitted; 15 | } -------------------------------------------------------------------------------- /utils/fp/toPairs/index.d.ts: -------------------------------------------------------------------------------- 1 | // inspired by ramda and rambda 2 | 3 | 4 | type KeyValueOut = { [k: string]: O } 5 | type KeyValueIn = [string, O][] 6 | 7 | export default function toPairs(obj: KeyValueIn): KeyValueOut 8 | -------------------------------------------------------------------------------- /utils/fp/toPairs/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = toPairs; 5 | // inspired by ramda and rambda 6 | /* eslint-disable */ 7 | 8 | function toPairs(obj) { 9 | var pairs = []; 10 | if (obj) { 11 | var keys = Object.keys(obj); 12 | for (var i = 0, len = keys.length; i < len; i++) { 13 | var prop = keys[i]; 14 | var value = obj[prop]; 15 | if (prop in obj) { 16 | pairs[i] = [prop, value]; 17 | } 18 | } 19 | } 20 | return pairs; 21 | } -------------------------------------------------------------------------------- /utils/fp/unique/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Returns a list of unique elements, compared by identity (===) 4 | // This is a replacement for rambdax uniq() which is based on slow equals() 5 | export default function unique(list: T[]): T[] 6 | -------------------------------------------------------------------------------- /utils/fp/unique/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = unique; 5 | // Returns a list of unique elements, compared by identity (===) 6 | // This is a replacement for rambdax uniq() which is based on slow equals() 7 | function unique(list) { 8 | var result = []; 9 | for (var i = 0, len = list.length; i < len; i += 1) { 10 | var value = list[i]; 11 | var isUnique = true; 12 | for (var j = 0, resultLen = result.length; j < resultLen; j += 1) { 13 | if (value === result[j]) { 14 | isUnique = false; 15 | break; 16 | } 17 | } 18 | if (isUnique) { 19 | result.push(value); 20 | } 21 | } 22 | return result; 23 | } -------------------------------------------------------------------------------- /utils/fp/unnest/index.d.ts: -------------------------------------------------------------------------------- 1 | // inspired by ramda and rambda 2 | /* eslint-disable */ 3 | 4 | export default function unnest(arr: any[]): any[] 5 | -------------------------------------------------------------------------------- /utils/fp/unnest/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = unnest; 5 | // inspired by ramda and rambda 6 | /* eslint-disable */ 7 | 8 | function unnest(arr) { 9 | var result = []; 10 | for (var i = 0, l = arr.length; i < l; i++) { 11 | var value = arr[i]; 12 | if (Array.isArray(value)) { 13 | result = result.concat(value); 14 | } else { 15 | result.push(value); 16 | } 17 | } 18 | return result; 19 | } -------------------------------------------------------------------------------- /utils/fp/values/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default function values(obj: O): T[] 4 | -------------------------------------------------------------------------------- /utils/fp/values/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = values; 5 | function values(obj) { 6 | // $FlowFixMe 7 | return Object.values(obj); 8 | } -------------------------------------------------------------------------------- /utils/rx/__wmelonRxShim/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // NOTE: All Rx imports in WatermelonDB MUST go through this file 4 | // this is a magic shim that can be replaced via babel onto another shim that imports Rx files 5 | // from different locations 6 | // This allows manual tree shaking on React Native 7 | 8 | export { 9 | // classes 10 | Observable, 11 | Subject, 12 | ReplaySubject, 13 | BehaviorSubject, 14 | // observables 15 | of, 16 | merge, 17 | defer, 18 | } from 'rxjs' 19 | export { 20 | // operators 21 | multicast, 22 | distinctUntilChanged, 23 | map, 24 | switchMap, 25 | throttleTime, 26 | startWith, 27 | } from 'rxjs/operators' 28 | export type { ConnectableObservable } from 'rxjs' 29 | -------------------------------------------------------------------------------- /utils/rx/__wmelonRxShim/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.throttleTime = exports.switchMap = exports.startWith = exports.of = exports.multicast = exports.merge = exports.map = exports.distinctUntilChanged = exports.defer = exports.Subject = exports.ReplaySubject = exports.Observable = exports.BehaviorSubject = void 0; 5 | var _rxjs = require("rxjs"); 6 | exports.Observable = _rxjs.Observable; 7 | exports.Subject = _rxjs.Subject; 8 | exports.ReplaySubject = _rxjs.ReplaySubject; 9 | exports.BehaviorSubject = _rxjs.BehaviorSubject; 10 | exports.of = _rxjs.of; 11 | exports.merge = _rxjs.merge; 12 | exports.defer = _rxjs.defer; 13 | var _operators = require("rxjs/operators"); 14 | exports.multicast = _operators.multicast; 15 | exports.distinctUntilChanged = _operators.distinctUntilChanged; 16 | exports.map = _operators.map; 17 | exports.switchMap = _operators.switchMap; 18 | exports.throttleTime = _operators.throttleTime; 19 | exports.startWith = _operators.startWith; -------------------------------------------------------------------------------- /utils/rx/__wmelonRxShimESM2015/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // classes 4 | export { Observable } from 'rxjs' 5 | export { Subject } from 'rxjs' 6 | export { ReplaySubject } from 'rxjs' 7 | export { BehaviorSubject } from 'rxjs' 8 | 9 | // observables 10 | export { of } from 'rxjs' 11 | export { merge } from 'rxjs' 12 | export { defer } from 'rxjs' 13 | 14 | // operators 15 | export { multicast } from 'rxjs' 16 | export { distinctUntilChanged } from 'rxjs' 17 | export { map } from 'rxjs' 18 | export { switchMap } from 'rxjs' 19 | export { throttleTime } from 'rxjs' 20 | export { startWith } from 'rxjs' 21 | -------------------------------------------------------------------------------- /utils/rx/cacheWhileConnected/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Observable } from '../__wmelonRxShim' 4 | 5 | // Equivalent to observable |> distinctUntilChanged |> publishReplayLatestWhileConnected |> refCount 6 | // 7 | // Creates an observable that shares the connection with and replays the latest value from the underlying 8 | // observable, and skips emissions that are the same as the previous one 9 | 10 | export default function cacheWhileConnected(source: Observable): Observable 11 | -------------------------------------------------------------------------------- /utils/rx/cacheWhileConnected/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.default = cacheWhileConnected; 6 | var _wmelonRxShim = require("../__wmelonRxShim"); 7 | var _publishReplayLatestWhileConnected = _interopRequireDefault(require("../publishReplayLatestWhileConnected")); 8 | // Equivalent to observable |> distinctUntilChanged |> publishReplayLatestWhileConnected |> refCount 9 | // 10 | // Creates an observable that shares the connection with and replays the latest value from the underlying 11 | // observable, and skips emissions that are the same as the previous one 12 | function cacheWhileConnected(source) { 13 | return source.pipe((0, _wmelonRxShim.distinctUntilChanged)(), _publishReplayLatestWhileConnected.default).refCount(); 14 | } -------------------------------------------------------------------------------- /utils/rx/doOnDispose/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Observable } from '../__wmelonRxShim' 4 | 5 | // Performs an action when Observable is disposed; analogous to `Observable.do` 6 | 7 | export default function doOnDispose( 8 | onDispose: () => void, 9 | ): (observable: Observable) => Observable 10 | -------------------------------------------------------------------------------- /utils/rx/doOnDispose/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = doOnDispose; 5 | var _wmelonRxShim = require("../__wmelonRxShim"); 6 | // Performs an action when Observable is disposed; analogous to `Observable.do` 7 | function doOnDispose(onDispose) { 8 | return function (source) { 9 | return _wmelonRxShim.Observable.create(function (observer) { 10 | // $FlowFixMe 11 | var subscription = source.subscribe(observer); 12 | return function () { 13 | subscription.unsubscribe(); 14 | onDispose(); 15 | }; 16 | }); 17 | }; 18 | } -------------------------------------------------------------------------------- /utils/rx/doOnSubscribe/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Observable } from '../__wmelonRxShim' 4 | 5 | // Performs an action when Observable is subscribed to; analogous to `Observable.do` 6 | 7 | export default function doOnSubscribe( 8 | onSubscribe: () => void, 9 | ): (observable: Observable) => Observable 10 | -------------------------------------------------------------------------------- /utils/rx/doOnSubscribe/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = doOnSubscribe; 5 | var _wmelonRxShim = require("../__wmelonRxShim"); 6 | // Performs an action when Observable is subscribed to; analogous to `Observable.do` 7 | function doOnSubscribe(onSubscribe) { 8 | return function (source) { 9 | return (0, _wmelonRxShim.defer)(function () { 10 | onSubscribe(); 11 | return source; 12 | }); 13 | }; 14 | } -------------------------------------------------------------------------------- /utils/rx/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as cacheWhileConnected } from './cacheWhileConnected' 4 | // export { default as doOnDispose } from './doOnDispose' 5 | // export { default as doOnSubscribe } from './doOnSubscribe' 6 | export { default as publishReplayLatestWhileConnected } from './publishReplayLatestWhileConnected' 7 | 8 | export { 9 | // classes 10 | Observable, 11 | Subject, 12 | ReplaySubject, 13 | BehaviorSubject, 14 | // observables 15 | of, 16 | merge, 17 | defer, 18 | // operators 19 | multicast, 20 | distinctUntilChanged, 21 | map, 22 | switchMap, 23 | throttleTime, 24 | startWith, 25 | } from './__wmelonRxShim' 26 | export type { ConnectableObservable } from './__wmelonRxShim' 27 | -------------------------------------------------------------------------------- /utils/rx/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.throttleTime = exports.switchMap = exports.startWith = exports.publishReplayLatestWhileConnected = exports.of = exports.multicast = exports.merge = exports.map = exports.distinctUntilChanged = exports.defer = exports.cacheWhileConnected = exports.Subject = exports.ReplaySubject = exports.Observable = exports.BehaviorSubject = void 0; 6 | var _cacheWhileConnected = _interopRequireDefault(require("./cacheWhileConnected")); 7 | exports.cacheWhileConnected = _cacheWhileConnected.default; 8 | var _publishReplayLatestWhileConnected = _interopRequireDefault(require("./publishReplayLatestWhileConnected")); 9 | exports.publishReplayLatestWhileConnected = _publishReplayLatestWhileConnected.default; 10 | var _wmelonRxShim = require("./__wmelonRxShim"); 11 | exports.Observable = _wmelonRxShim.Observable; 12 | exports.Subject = _wmelonRxShim.Subject; 13 | exports.ReplaySubject = _wmelonRxShim.ReplaySubject; 14 | exports.BehaviorSubject = _wmelonRxShim.BehaviorSubject; 15 | exports.of = _wmelonRxShim.of; 16 | exports.merge = _wmelonRxShim.merge; 17 | exports.defer = _wmelonRxShim.defer; 18 | exports.multicast = _wmelonRxShim.multicast; 19 | exports.distinctUntilChanged = _wmelonRxShim.distinctUntilChanged; 20 | exports.map = _wmelonRxShim.map; 21 | exports.switchMap = _wmelonRxShim.switchMap; 22 | exports.throttleTime = _wmelonRxShim.throttleTime; 23 | exports.startWith = _wmelonRxShim.startWith; -------------------------------------------------------------------------------- /utils/rx/publishReplayLatestWhileConnected/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ConnectableObservable, 3 | Observable, 4 | ReplaySubject, 5 | multicast, 6 | } from '../__wmelonRxShim' 7 | 8 | // Creates a Connectable observable, that, while connected, replays the latest emission 9 | // upon subscription. When disconnected, the replay cache is cleared. 10 | 11 | export default function publishReplayLatestWhileConnected( 12 | source: Observable, 13 | ): ConnectableObservable 14 | -------------------------------------------------------------------------------- /utils/rx/publishReplayLatestWhileConnected/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | exports.default = publishReplayLatestWhileConnected; 5 | var _wmelonRxShim = require("../__wmelonRxShim"); 6 | // Creates a Connectable observable, that, while connected, replays the latest emission 7 | // upon subscription. When disconnected, the replay cache is cleared. 8 | function publishReplayLatestWhileConnected(source) { 9 | return source.pipe((0, _wmelonRxShim.multicast)(function () { 10 | return new _wmelonRxShim.ReplaySubject(1); 11 | })); 12 | } -------------------------------------------------------------------------------- /utils/subscriptions/SharedSubscribable/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { Unsubscribe } from '../type' 2 | 3 | export default class SharedSubscribable { 4 | _source: (subscriber: (_: T) => void) => Unsubscribe 5 | 6 | _unsubscribeSource: Unsubscribe | null 7 | 8 | _subscribers: [(_: T) => void, any][] 9 | 10 | _didEmit: boolean 11 | 12 | _lastValue: T | null 13 | 14 | constructor(source: (subscriber: (_: T) => void) => Unsubscribe) 15 | 16 | subscribe(subscriber: (_: T) => void, debugInfo?: any): Unsubscribe 17 | 18 | _notify(value: T): void 19 | 20 | _unsubscribe(entry: [(_: T) => void, any]): void 21 | } 22 | -------------------------------------------------------------------------------- /utils/subscriptions/index.d.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default as SharedSubscribable } from './SharedSubscribable' 4 | export type { Unsubscribe } from './type' 5 | -------------------------------------------------------------------------------- /utils/subscriptions/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | exports.__esModule = true; 5 | exports.SharedSubscribable = void 0; 6 | var _SharedSubscribable = _interopRequireDefault(require("./SharedSubscribable")); 7 | exports.SharedSubscribable = _SharedSubscribable.default; -------------------------------------------------------------------------------- /utils/subscriptions/type.d.ts: -------------------------------------------------------------------------------- 1 | export type Unsubscribe = () => void 2 | -------------------------------------------------------------------------------- /utils/subscriptions/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; --------------------------------------------------------------------------------