From 5a07c1ac29c415eb6eaf722303763f7fb20bb257 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Tue, 11 Jul 2023 12:45:08 -0400 Subject: [PATCH] 14 day data cleanup and vacuum (#7438) --- app/actions/local/systems.ts | 86 ++- package-lock.json | 143 ++--- patches/@nozbe+watermelondb+0.26.0.patch | 686 ++++++++++++++++++++++- 3 files changed, 788 insertions(+), 127 deletions(-) diff --git a/app/actions/local/systems.ts b/app/actions/local/systems.ts index a64a7f3a3..3dd0a20da 100644 --- a/app/actions/local/systems.ts +++ b/app/actions/local/systems.ts @@ -9,12 +9,12 @@ import DatabaseManager from '@database/manager'; import {getServerCredentials} from '@init/credentials'; import {queryAllChannelsForTeam} from '@queries/servers/channel'; import {getConfig, getLicense, getGlobalDataRetentionPolicy, getGranularDataRetentionPolicies, getLastGlobalDataRetentionRun, getIsDataRetentionEnabled} from '@queries/servers/system'; +import PostModel from '@typings/database/models/servers/post'; import {logError} from '@utils/log'; import {deletePosts} from './post'; import type {DataRetentionPoliciesRequest} from '@actions/remote/systems'; -import type PostModel from '@typings/database/models/servers/post'; const {SERVER: {POST}} = MM_TABLES; @@ -127,11 +127,6 @@ export async function dataRetentionCleanup(serverUrl: string) { try { const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const isDataRetentionEnabled = await getIsDataRetentionEnabled(database); - if (!isDataRetentionEnabled) { - return {error: undefined}; - } - const lastRunAt = await getLastGlobalDataRetentionRun(database); const lastCleanedToday = new Date(lastRunAt).toDateString() === new Date().toDateString(); @@ -140,6 +135,25 @@ export async function dataRetentionCleanup(serverUrl: string) { return {error: undefined}; } + const isDataRetentionEnabled = await getIsDataRetentionEnabled(database); + const result = await (isDataRetentionEnabled ? dataRetentionPolicyCleanup(serverUrl) : dataRetentionWithoutPolicyCleanup(serverUrl)); + + if (!result.error) { + await updateLastDataRetentionRun(serverUrl); + } + + await database.unsafeVacuum(); + + return result; + } catch (error) { + logError('An error occurred while performing data retention cleanup', error); + return {error}; + } +} + +async function dataRetentionPolicyCleanup(serverUrl: string) { + try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); const globalPolicy = await getGlobalDataRetentionPolicy(database); const granularPoliciesData = await getGranularDataRetentionPolicies(database); @@ -196,32 +210,50 @@ export async function dataRetentionCleanup(serverUrl: string) { Q.unsafeSqlQuery(`SELECT * FROM ${POST} where ${conditions.join(' OR ')}`), ).fetchIds(); - if (postIds.length) { - const batchSize = 1000; - const deletePromises = []; - for (let i = 0; i < postIds.length; i += batchSize) { - const batch = postIds.slice(i, batchSize); - deletePromises.push( - deletePosts(serverUrl, batch), - ); - } - const deleteResult = await Promise.all(deletePromises); - for (const {error} of deleteResult) { - if (error) { - return {error}; - } - } - } - - await updateLastDataRetentionRun(serverUrl); - - return {error: undefined}; + return dataRetentionCleanPosts(serverUrl, postIds); } catch (error) { - logError('An error occurred while performing data retention cleanup', error); + logError('An error occurred while performing data retention policy cleanup', error); return {error}; } } +async function dataRetentionWithoutPolicyCleanup(serverUrl: string) { + try { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const cutoff = getDataRetentionPolicyCutoff(14); // 14 days + + const postIds = await database.get(POST).query( + Q.where('create_at', Q.lt(cutoff)), + ).fetchIds(); + + return dataRetentionCleanPosts(serverUrl, postIds); + } catch (error) { + logError('An error occurred while performing data retention without policy cleanup', error); + return {error}; + } +} + +async function dataRetentionCleanPosts(serverUrl: string, postIds: string[]) { + if (postIds.length) { + const batchSize = 1000; + const deletePromises = []; + for (let i = 0; i < postIds.length; i += batchSize) { + const batch = postIds.slice(i, batchSize); + deletePromises.push( + deletePosts(serverUrl, batch), + ); + } + const deleteResult = await Promise.all(deletePromises); + for (const {error} of deleteResult) { + if (error) { + return {error}; + } + } + } + + return {error: undefined}; +} + // Returns cutoff time based on the policy's post_duration function getDataRetentionPolicyCutoff(postDuration: number) { const periodDate = new Date(); diff --git a/package-lock.json b/package-lock.json index 0700b4696..421d3ba03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "mattermost-mobile", - "version": "2.5.1", + "version": "2.6.0", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { @@ -22261,8 +22261,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "requires": { - } + "requires": {} }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.18.6", @@ -24099,8 +24098,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@mattermost/react-native-emm/-/react-native-emm-1.3.5.tgz", "integrity": "sha512-REdUEsm/RA6lI1Rt4b009jvWn28f7H+e27gd4hlNk6zesIh/dlfiHwYfInW/vwbNFBdSPpvHy7Qi2mdcvrNqhg==", - "requires": { - } + "requires": {} }, "@mattermost/react-native-network-client": { "version": "1.3.4", @@ -24146,8 +24144,7 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/@mattermost/react-native-turbo-log/-/react-native-turbo-log-0.2.3.tgz", "integrity": "sha512-usWyD8zVAHzrYqgPH1ne5I14gCOkhS2mefK58g5v4DewZfCm0/Uc0w8MRuPS/9jyOPPq1rUZj8U1AqKgEne9tQ==", - "requires": { - } + "requires": {} }, "@msgpack/msgpack": { "version": "2.8.0", @@ -24243,15 +24240,13 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-5.6.0.tgz", "integrity": "sha512-a/GYwnBTxj1yKWB9m/qy8GzjowSocML8NbLT81wdMh0JzZYXCLze51BR2cb8JNDgRPzA9xe7KpD3j9qQOSOjag==", - "requires": { - } + "requires": {} }, "@react-native-clipboard/clipboard": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.2.tgz", "integrity": "sha512-bHyZVW62TuleiZsXNHS1Pv16fWc0fh8O9WvBzl4h2fykqZRW9a+Pv/RGTH56E3X2PqzHP38K5go8zmCZUoIsoQ==", - "requires": { - } + "requires": {} }, "@react-native-community/cli": { "version": "10.2.4", @@ -25408,8 +25403,7 @@ "version": "9.3.10", "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-9.3.10.tgz", "integrity": "sha512-OwnqoJUp/4sa9e3ju+wQavAa8l0fiA3DheeLMKzKxtKeAe0CA7bNxWRM752JvRQ6A/igPnt1V0zSlu5owvQEuA==", - "requires": { - } + "requires": {} }, "@react-native-cookies/cookies": { "version": "6.2.1", @@ -25485,8 +25479,7 @@ "version": "1.3.17", "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.17.tgz", "integrity": "sha512-sui8AzHm6TxeEvWT/NEXlz3egYvCUog4tlXA4Xlb2Vxvy3purVXDq/XsM56lJl344U5Aj/jDzkVanOTMWyk4UA==", - "requires": { - } + "requires": {} }, "@react-navigation/native": { "version": "6.1.6", @@ -25770,72 +25763,63 @@ "version": "0.10.3", "resolved": "https://registry.npmjs.org/@stream-io/flat-list-mvcp/-/flat-list-mvcp-0.10.3.tgz", "integrity": "sha512-2ZK8piYlEfKIPZrH8BpZz9uj8HZcUvMCV0X7qSLSAc/vhLOANBfR0SSn0OaWPbqb2mFGAd4FxmLSPp1zKEYuaw==", - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz", "integrity": "sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-remove-jsx-attribute": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz", "integrity": "sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-remove-jsx-empty-expression": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz", "integrity": "sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-replace-jsx-attribute-value": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz", "integrity": "sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-svg-dynamic-title": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz", "integrity": "sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-svg-em-dimensions": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz", "integrity": "sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-transform-react-native-svg": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz", "integrity": "sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-plugin-transform-svg-component": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.1.0.tgz", "integrity": "sha512-1zacrn08K5RyV2NtXahOZ5Im/+aB1Y0LVh6QpzwgQV05sY7H5Npq+OcW/UqXbfB2Ua/WnHsFossFQqigCjarYg==", "dev": true, - "requires": { - } + "requires": {} }, "@svgr/babel-preset": { "version": "6.1.0", @@ -26618,8 +26602,7 @@ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", "dev": true, - "requires": { - } + "requires": {} }, "@webpack-cli/info": { "version": "1.4.0", @@ -26635,8 +26618,7 @@ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", "dev": true, - "requires": { - } + "requires": {} }, "@xtuc/ieee754": { "version": "1.2.0", @@ -26691,16 +26673,14 @@ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "dev": true, "peer": true, - "requires": { - } + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "requires": { - } + "requires": {} }, "adm-zip": { "version": "0.5.9", @@ -26762,8 +26742,7 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "peer": true, - "requires": { - } + "requires": {} }, "anser": { "version": "1.4.10", @@ -26999,8 +26978,7 @@ "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "requires": { - } + "requires": {} }, "babel-jest": { "version": "29.5.0", @@ -29024,8 +29002,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", "dev": true, - "requires": { - } + "requires": {} }, "eslint-import-resolver-node": { "version": "0.3.7", @@ -29113,8 +29090,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz", "integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==", "dev": true, - "requires": { - } + "requires": {} }, "eslint-plugin-import": { "version": "2.27.5", @@ -29250,8 +29226,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "dev": true, - "requires": { - } + "requires": {} }, "eslint-plugin-react-native": { "version": "4.0.0", @@ -31626,8 +31601,7 @@ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "requires": { - } + "requires": {} }, "jest-regex-util": { "version": "29.4.3", @@ -35007,8 +34981,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz", "integrity": "sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==", - "requires": { - } + "requires": {} }, "react-intl": { "version": "6.4.4", @@ -35186,8 +35159,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz", "integrity": "sha512-TE4Kiy7jUyv+hugxDxitzu38sW1NqjCk4uE5IgU2WevLv7sZacaBc6PZKOShNRPGirLl1NWkaG3LDEkdb9Um5g==", - "requires": { - } + "requires": {} }, "react-native-button": { "version": "3.1.0", @@ -35240,15 +35212,13 @@ "version": "1.6.4", "resolved": "https://registry.npmjs.org/react-native-create-thumbnail/-/react-native-create-thumbnail-1.6.4.tgz", "integrity": "sha512-JWuKXswDXtqUPfuqh6rjCVMvTSSG3kUtwvSK/YdaNU0i+nZKxeqHmt/CO2+TyI/WSUFynGVmWT1xOHhCZAFsRQ==", - "requires": { - } + "requires": {} }, "react-native-device-info": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-10.6.0.tgz", "integrity": "sha512-/MmINdojWdw2/9rwYpH/dX+1gFP0o78p8yYPjwxiPhoySSL2rZaNi+Mq9VwC+zFi/yQmJUvHntkKSw2KUc7rFw==", - "requires": { - } + "requires": {} }, "react-native-document-picker": { "version": "9.0.1", @@ -35285,22 +35255,19 @@ "version": "2.10.10", "resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.10.10.tgz", "integrity": "sha512-otAXGoZDl1689OoUJWN/rXxVbdoZ3xcmyF1uq/CsizdLwwyZqVGd6d+p/vbYvnF996FfEyAEBnHrdFxulTn51w==", - "requires": { - } + "requires": {} }, "react-native-fast-image": { "version": "8.6.3", "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz", "integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==", - "requires": { - } + "requires": {} }, "react-native-file-viewer": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/react-native-file-viewer/-/react-native-file-viewer-2.1.5.tgz", "integrity": "sha512-MGC6sx9jsqHdefhVQ6o0akdsPGpkXgiIbpygb2Sg4g4bh7v6K1cardLV1NwGB9A6u1yICOSDT/MOC//9Ez6EUg==", - "requires": { - } + "requires": {} }, "react-native-fs": { "version": "2.20.0", @@ -35339,22 +35306,19 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.0.3.tgz", "integrity": "sha512-7+qvcxXZts/hA+HOOIFyM1x9m9fn/TJVSTgXaoQ8uT4gLc97IMvqHQ559tDmnlth+hHMzd3HRMpmRLWoKPL0DA==", - "requires": { - } + "requires": {} }, "react-native-hw-keyboard-event": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/react-native-hw-keyboard-event/-/react-native-hw-keyboard-event-0.0.4.tgz", "integrity": "sha512-G8qp0nm17PHigLb/axgdF9xg51BKCG2p1AGeq//J/luLp5zNczIcQJh+nm02R1MeEUE3e53wqO4LMe0MV3raZg==", - "requires": { - } + "requires": {} }, "react-native-image-picker": { "version": "5.4.2", "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-5.4.2.tgz", "integrity": "sha512-C/k3cYAh8fBImoGEwmiChNwHx9fJGqAIu2E4BUJdI1XlL17tSYjfTDx/bsuF4amZwa7hxZdQnZmpk0EnwIEUaw==", - "requires": { - } + "requires": {} }, "react-native-in-app-review": { "version": "4.3.3", @@ -35364,15 +35328,13 @@ "react-native-incall-manager": { "version": "git+ssh://git@github.com/react-native-webrtc/react-native-incall-manager.git#6d927ef24c6e47c6134177a4bb14a71054f85b65", "from": "react-native-incall-manager@4.0.1", - "requires": { - } + "requires": {} }, "react-native-iphone-x-helper": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz", "integrity": "sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==", - "requires": { - } + "requires": {} }, "react-native-keyboard-aware-scroll-view": { "version": "0.9.5", @@ -35387,8 +35349,7 @@ "version": "5.7.0", "resolved": "https://registry.npmjs.org/react-native-keyboard-tracking-view/-/react-native-keyboard-tracking-view-5.7.0.tgz", "integrity": "sha512-MDeEwAbn9LJDOfHq0QLCGaZirVLk2X/tHqkAqz3y6uxryTRdSl9PwleOVar5Jx2oAPEg4J9BXbUD1wwOOi+5Kg==", - "requires": { - } + "requires": {} }, "react-native-keychain": { "version": "8.1.1", @@ -35399,15 +35360,13 @@ "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.7.2.tgz", "integrity": "sha512-c1mNtmQrD+nzlIePQ5XZR/6IuEaNcYvE+N7MnOHDSOkXmxfLVbyicT15KEYnXQqbTLwTrFNJPZot241lWRVC3g==", - "requires": { - } + "requires": {} }, "react-native-localize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.0.0.tgz", "integrity": "sha512-B8taYRLuLIYDzBTKIglA3K6ntjaEwbk6mwQ72ogZYl5ovM00NnpbiZ3sRq8KRVe/V1NGczxT33uVqG6BUWGWhg==", - "requires": { - } + "requires": {} }, "react-native-math-view": { "version": "3.9.5", @@ -35443,8 +35402,7 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-4.3.5.tgz", "integrity": "sha512-tCKkPaauN8/RIwPTeWdyezHXt5i4iDJViZGZ7/EBBuHB9kl9Oq3UjKFZeDfnM4DmJt+m7K2FQ7NaAuh3Kg1FCA==", - "requires": { - } + "requires": {} }, "react-native-permissions": { "version": "3.8.0", @@ -35485,8 +35443,7 @@ "version": "4.5.3", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.5.3.tgz", "integrity": "sha512-ihYeGDEBSkYH+1aWnadNhVtclhppVgd/c0tm4mj0+HV11FoiWJ8N6ocnnZnRLvM5Fxc+hUqxR9bm5AXU3rXiyA==", - "requires": { - } + "requires": {} }, "react-native-screens": { "version": "3.21.0", @@ -35519,8 +35476,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz", "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==", - "requires": { - } + "requires": {} }, "react-native-svg": { "version": "13.9.0", @@ -37468,15 +37424,13 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz", "integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==", - "requires": { - } + "requires": {} }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "requires": { - } + "requires": {} }, "utf8": { "version": "3.0.0", @@ -37797,8 +37751,7 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": { - } + "requires": {} }, "xdate": { "version": "0.8.2", diff --git a/patches/@nozbe+watermelondb+0.26.0.patch b/patches/@nozbe+watermelondb+0.26.0.patch index 620fee4b1..550e8d88a 100644 --- a/patches/@nozbe+watermelondb+0.26.0.patch +++ b/patches/@nozbe+watermelondb+0.26.0.patch @@ -1,5 +1,18 @@ +diff --git a/node_modules/@nozbe/watermelondb/Database/index.d.ts b/node_modules/@nozbe/watermelondb/Database/index.d.ts +index 861ed06..d2d6527 100644 +--- a/node_modules/@nozbe/watermelondb/Database/index.d.ts ++++ b/node_modules/@nozbe/watermelondb/Database/index.d.ts +@@ -88,6 +88,8 @@ export default class Database { + // Yes, this sucks and there should be some safety mechanisms or warnings. Please contribute! + unsafeResetDatabase(): Promise + ++ unsafeVacuum(): Promise ++ + _ensureInWriter(diagnosticMethodName: string): void + + // (experimental) puts Database in a broken state diff --git a/node_modules/@nozbe/watermelondb/Database/index.js b/node_modules/@nozbe/watermelondb/Database/index.js -index fa82516..97ba290 100644 +index fa82516..aa88fe9 100644 --- a/node_modules/@nozbe/watermelondb/Database/index.js +++ b/node_modules/@nozbe/watermelondb/Database/index.js @@ -91,7 +91,9 @@ var Database = /*#__PURE__*/function () { @@ -24,6 +37,66 @@ index fa82516..97ba290 100644 } if (!changeNotifications[table]) { changeNotifications[table] = []; +@@ -379,6 +385,59 @@ var Database = /*#__PURE__*/function () { + } + }.bind(this)); + }; ++ ++ _proto.unsafeVacuum = function unsafeVacuum() { ++ return new Promise(function ($return, $error) { ++ var $Try_3_Finally = function ($Try_3_Exit) { ++ return function ($Try_3_Value) { ++ try { ++ // this._isBeingReset = false; ++ return $Try_3_Exit && $Try_3_Exit.call(this, $Try_3_Value); ++ } catch ($boundEx) { ++ return $error($boundEx); ++ } ++ }.bind(this); ++ }.bind(this); ++ var adapter, ErrorAdapter; ++ var $Try_3_Post = function () { ++ try { ++ return $return(); ++ } catch ($boundEx) { ++ return $error($boundEx); ++ } ++ }; ++ var $Try_3_Catch = function ($exception_4) { ++ try { ++ throw $exception_4; ++ } catch ($boundEx) { ++ return $Try_3_Finally($error)($boundEx); ++ } ++ }; ++ try { ++ // Kill ability to call adapter methods during reset (to catch bugs if someone does this) ++ ({ ++ adapter: adapter ++ } = this); ++ ErrorAdapter = require('../adapters/error').default; ++ this.adapter = new ErrorAdapter(); ++ ++ // Vacuum the database ++ return Promise.resolve(adapter.unsafeVacuum()).then(function () { ++ try { ++ // Restore working Database ++ this._resetCount += 1; ++ this.adapter = adapter; ++ return $Try_3_Finally($Try_3_Post)(); ++ } catch ($boundEx) { ++ return $Try_3_Catch($boundEx); ++ } ++ }.bind(this), $Try_3_Catch); ++ } catch ($exception_4) { ++ $Try_3_Catch($exception_4) ++ } ++ }.bind(this)); ++ }; ++ + _proto._ensureInWriter = function _ensureInWriter(diagnosticMethodName) { + (0, _common.invariant)(this._workQueue.isWriterRunning, "".concat(diagnosticMethodName, " can only be called from inside of a Writer. See docs for more details.")); + } diff --git a/node_modules/@nozbe/watermelondb/Model/index.d.ts b/node_modules/@nozbe/watermelondb/Model/index.d.ts index 96114ec..ecfe3c1 100644 --- a/node_modules/@nozbe/watermelondb/Model/index.d.ts @@ -94,8 +167,211 @@ index 10ee0a5..3d0bf77 100644 this.__ensureNotDisposable("Model.prepareDestroyPermanently()"); this._raw._status = 'deleted'; this._preparedState = 'destroyPermanently'; +diff --git a/node_modules/@nozbe/watermelondb/adapters/compat.d.ts b/node_modules/@nozbe/watermelondb/adapters/compat.d.ts +index 51879c7..435ad72 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/compat.d.ts ++++ b/node_modules/@nozbe/watermelondb/adapters/compat.d.ts +@@ -44,6 +44,8 @@ export default class DatabaseAdapterCompat { + + unsafeResetDatabase(): Promise + ++ unsafeVacuum(): Promise ++ + unsafeExecute(work: UnsafeExecuteOperations): Promise + + getLocal(key: string): Promise +diff --git a/node_modules/@nozbe/watermelondb/adapters/compat.js b/node_modules/@nozbe/watermelondb/adapters/compat.js +index 4a0b5ef..0980828 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/compat.js ++++ b/node_modules/@nozbe/watermelondb/adapters/compat.js +@@ -76,6 +76,12 @@ var DatabaseAdapterCompat = /*#__PURE__*/function () { + return _this11.underlyingAdapter.unsafeResetDatabase(callback); + }); + }; ++ _proto.unsafeVacuum = function unsafeVacuum() { ++ var _this16 = this; ++ return (0, _Result.toPromise)(function (callback) { ++ return _this16.underlyingAdapter.unsafeVacuum(callback); ++ }); ++ } + _proto.unsafeExecute = function unsafeExecute(work) { + var _this12 = this; + return (0, _Result.toPromise)(function (callback) { +diff --git a/node_modules/@nozbe/watermelondb/adapters/error.js b/node_modules/@nozbe/watermelondb/adapters/error.js +index 70a4c11..453dca2 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/error.js ++++ b/node_modules/@nozbe/watermelondb/adapters/error.js +@@ -13,7 +13,7 @@ var throwError = function (name) { + var ErrorAdapter = /*#__PURE__*/function () { + function ErrorAdapter() { + var _this = this; +- ['find', 'query', 'queryIds', 'count', 'batch', 'getDeletedRecords', 'destroyDeletedRecords', 'unsafeResetDatabase', 'getLocal', 'setLocal', 'removeLocal', 'testClone'].forEach(function (name) { ++ ['find', 'query', 'queryIds', 'count', 'batch', 'getDeletedRecords', 'destroyDeletedRecords', 'unsafeResetDatabase', 'unsafeVacuum', 'getLocal', 'setLocal', 'removeLocal', 'testClone'].forEach(function (name) { + // $FlowFixMe + _this[name] = function () { + return throwError(name); +diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts b/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts +index 5528dc1..0408198 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts ++++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts +@@ -13,6 +13,7 @@ export type WorkerExecutorType = + | 'batch' + | 'getDeletedRecords' + | 'unsafeResetDatabase' ++ | 'unsafeVacuum' + | 'unsafeExecute' + | 'getLocal' + | 'setLocal' +diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts +index e09fb14..51d42d9 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts ++++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts +@@ -114,6 +114,8 @@ export default class LokiJSAdapter implements DatabaseAdapter { + + unsafeResetDatabase(callback: ResultCallback): void + ++ unsafeVacuum(callback: ResultCallback): void ++ + unsafeExecute(operations: UnsafeExecuteOperations, callback: ResultCallback): void + + getLocal(key: string, callback: ResultCallback): void +diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js +index 9d5ea84..aa8397d 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js ++++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js +@@ -105,6 +105,9 @@ var LokiJSAdapter = /*#__PURE__*/function () { + _proto.unsafeResetDatabase = function unsafeResetDatabase(callback) { + this._dispatcher.call('unsafeResetDatabase', [], callback); + }; ++ _proto.unsafeVacuum = function unssafeVacuum(callback) { ++ this._dispatcher.call('unsafeVacuum', [], callback); ++ }; + _proto.unsafeExecute = function unsafeExecute(operations, callback) { + this._dispatcher.call('unsafeExecute', [operations], callback); + }; +diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js +index ca35a79..7079572 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js ++++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js +@@ -33,7 +33,7 @@ var DatabaseBridge = /*#__PURE__*/function () { + type: type, + payload: payload + } = action; +- if ('setUp' === type || 'unsafeResetDatabase' === type) { ++ if ('setUp' === type || 'unsafeResetDatabase' === type || 'unsafeVacuum' === type) { + this.processActionAsync(action); + } else { + var response = this._driverAction(type).apply(void 0, (0, _toConsumableArray2.default)(payload)); +diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js +index ee89cff..9c1f6ce 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js ++++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js +@@ -215,6 +215,11 @@ var DatabaseDriver = /*#__PURE__*/function () { + }.bind(this), $error); + }.bind(this)); + } ++ _proto.unsafeVacuum = function unsafeVacuum() { ++ return new Promise(function ($return, $error) { ++ return Promise.resolve(0); ++ }.bind(this)); ++ } + + // *** LocalStorage *** + ; +diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts +index 88c43a0..cef8232 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts ++++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts +@@ -85,6 +85,8 @@ export default class SQLiteAdapter implements DatabaseAdapter { + + unsafeResetDatabase(callback: ResultCallback): void + ++ unsafeVacuum(callback: ResultCallback): void ++ + unsafeExecute(operations: UnsafeExecuteOperations, callback: ResultCallback): void + + getLocal(key: string, callback: ResultCallback): void +diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js +index bb80355..897f4d5 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js ++++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js +@@ -238,6 +238,14 @@ var SQLiteAdapter = /*#__PURE__*/function () { + callback(result); + }); + }; ++ _proto.unsafeVacuum = function unsafeVacuum(callback) { ++ this._dispatcher.call('unsafeVacuum', [], function (result) { ++ if (result.value) { ++ _common.logger.log('[SQLite] Database is now vacuumed'); ++ } ++ callback(result); ++ }); ++ }; + _proto.unsafeExecute = function unsafeExecute(operations, callback) { + if ('production' !== process.env.NODE_ENV) { + (0, _common.invariant)(operations && 'object' === typeof operations && 1 === Object.keys(operations).length && (Array.isArray(operations.sqls) || 'string' === typeof operations.sqlString), "unsafeExecute expects an { sqls: [ [sql, [args..]], ... ] } or { sqlString: 'foo; bar' } object"); +diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js +index a93b0a7..df63896 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js ++++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js +@@ -113,6 +113,14 @@ var DatabaseBridge = /*#__PURE__*/function () { + }); + }); + }; ++ _proto.unsafeVacuum = function unsafeVacuum(tag, schema, schemaVersion, resolve, reject) { ++ this.withDriver(tag, resolve, reject, 'unsafeVacuum', function (driver) { ++ return driver.unsafeVacuum({ ++ version: schemaVersion, ++ sql: schema ++ }); ++ }); ++ }; + _proto.getLocal = function getLocal(tag, key, resolve, reject) { + this.withDriver(tag, resolve, reject, 'getLocal', function (driver) { + return driver.getLocal(key); +diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js +index 483884c..c325299 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js ++++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js +@@ -198,6 +198,10 @@ var DatabaseDriver = /*#__PURE__*/function () { + _this5.database.userVersion = schema.version; + }); + }; ++ _proto.unsafeVacuum = function unsafeVacuum() { ++ var _this5 = this; ++ this.database.unsafeVacuum(); ++ }; + _proto.migrate = function migrate(migrations) { + var _this6 = this; + var databaseVersion = this.database.userVersion; +diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts b/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts +index 8131fa1..6053628 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts ++++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts +@@ -49,6 +49,7 @@ export type SqliteDispatcherMethod = + | 'unsafeLoadFromSync' + | 'provideSyncJson' + | 'unsafeResetDatabase' ++ | 'unsafeVacuum' + | 'getLocal' + | 'unsafeExecuteMultiple' + +diff --git a/node_modules/@nozbe/watermelondb/adapters/type.d.ts b/node_modules/@nozbe/watermelondb/adapters/type.d.ts +index 027c366..35b5a29 100644 +--- a/node_modules/@nozbe/watermelondb/adapters/type.d.ts ++++ b/node_modules/@nozbe/watermelondb/adapters/type.d.ts +@@ -66,6 +66,9 @@ export interface DatabaseAdapter { + // Destroys the whole database, its schema, indexes, everything. + unsafeResetDatabase(callback: ResultCallback): void + ++ // Vacuum the whole database. ++ unsafeVacuum(callback: ResultCallback): void ++ + // Performs work on the underlying database - see concrete DatabaseAdapter implementation for more details + unsafeExecute(work: UnsafeExecuteOperations, callback: ResultCallback): void + diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt -index b7f750d..56dd558 100644 +index b7f750d..052f5a4 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt @@ -19,8 +19,7 @@ class Database private constructor(private val db: SQLiteDatabase) { @@ -140,8 +416,48 @@ index b7f750d..56dd558 100644 } else { // On some systems there is some kind of lock on `/databases` folder ¯\_(ツ)_/¯ context.getDatabasePath("$name.db").path.replace("/databases", "") +@@ -131,6 +144,10 @@ class Database private constructor(private val db: SQLiteDatabase) { + execute("pragma writable_schema=0") + } + ++ fun unsafeVacuum() { ++ execute("vacuum") ++ } ++ + private fun getAllTables(): ArrayList { + val allTables: ArrayList = arrayListOf() + rawQuery(Queries.select_tables).use { +diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt +index 62d9d92..2415956 100644 +--- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt ++++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt +@@ -153,6 +153,9 @@ class DatabaseBridge(private val reactContext: ReactApplicationContext) : + promise: Promise + ) = withDriver(tag, promise) { it.unsafeResetDatabase(Schema(schemaVersion, schema)) } + ++ @ReactMethod ++ fun unsafeVacuum(tag: ConnectionTag, promise: Promise) = withDriver(tag, promise) { it.unsafeVacuum() } ++ + @ReactMethod + fun getLocal(tag: ConnectionTag, key: String, promise: Promise) = + withDriver(tag, promise) { it.getLocal(key) } +diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt +index 36909ae..eeacc6e 100644 +--- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt ++++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt +@@ -177,6 +177,10 @@ class DatabaseDriver(context: Context, dbName: String, unsafeNativeReuse: Boolea + } + } + ++ fun unsafeVacuum() { ++ database.unsafeVacuum() ++ } ++ + fun close() = database.close() + + private fun markAsCached(table: TableName, id: RecordID) { diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java -index 2f170e0..01e7450 100644 +index 2f170e0..bac0352 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java @@ -11,6 +11,8 @@ import java.util.Arrays; @@ -185,8 +501,38 @@ index 2f170e0..01e7450 100644 } else { // On some systems there is some kind of lock on `/databases` folder ¯\_(ツ)_/¯ path = context.getDatabasePath("" + name + ".db").getPath().replace("/databases", ""); +@@ -172,6 +190,10 @@ public class WMDatabase { + }); + } + ++ public void unsafeVacuum() { ++ execute("vacuum"); ++ } ++ + interface TransactionFunction { + void applyTransactionFunction(); + } +diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java +index be92429..847746d 100644 +--- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java ++++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java +@@ -122,6 +122,14 @@ public class WMDatabaseBridge extends ReactContextBaseJavaModule { + }, "unsafeResetDatabase"); + } + ++ @ReactMethod ++ public void unsafeVacuum(int tag, Promise promise) { ++ withDriver(tag, promise, (driver) -> { ++ driver.unsafeVacuum(); ++ return null; ++ }, "unsafeVacuum"); ++ } ++ + @ReactMethod + public void getLocal(int tag, String key, Promise promise) { + withDriver(tag, promise, (driver) -> driver.getLocal(key), "getLocal"); diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java -index 1534830..d9a5217 100644 +index 1534830..5c5313c 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java @@ -55,11 +55,9 @@ public class WMDatabaseDriver { @@ -203,8 +549,150 @@ index 1534830..d9a5217 100644 if (BuildConfig.DEBUG) { this.log = Logger.getLogger("DB_Driver"); } else { +@@ -230,6 +228,13 @@ public class WMDatabaseDriver { + }); + } + ++ public void unsafeVacuum() { ++ if (log != null) { ++ log.info("Unsafe database vacuum"); ++ } ++ database.unsafeVacuum(); ++ } ++ + private static class SchemaCompatibility { + static class Compatible extends SchemaCompatibility { + } +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h +index f199fdc..01ba3b7 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h +@@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN + + - (BOOL) inTransaction:(BOOL (^)(NSError**))transactionBlock error:(NSError**)errorPtr; + - (BOOL) unsafeDestroyEverything:(NSError**)errorPtr; ++- (BOOL) unsafeVacuum:(NSError**)errorPtr; + + @end + +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m +index 04fc2e5..e6d1a5b 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m +@@ -125,6 +125,15 @@ + } + } + ++- (BOOL) unsafeVacuum:(NSError**)errorPtr ++{ ++ if (![self executeStatements:@"vacuum" error:errorPtr]) { ++ return NO; ++ } ++ ++ return YES; ++} ++ + - (BOOL) unsafeDestroyEverything:(NSError**)errorPtr + { + // NOTE: Deleting files by default because it seems simpler, more reliable +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m +index 8a3ab35..9e0550f 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m +@@ -183,6 +183,16 @@ BRIDGE_METHOD(unsafeResetDatabase, + }) + } + ++RCT_EXPORT_METHOD(unsafeVacuum:(nonnull NSNumber *)tag ++ resolve:(RCTPromiseResolveBlock)resolve ++ reject:(RCTPromiseRejectBlock)reject ++) { ++ WITH_DRIVER({ ++ [driver unsafeVacuum:errorPtr]; ++ return @YES; ++ }) ++} ++ + BRIDGE_METHOD(getLocal, + key:(nonnull NSString *)key + ) +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h +index 0224cc3..085a10c 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h +@@ -34,6 +34,7 @@ typedef NS_ENUM(NSInteger, WMDatabaseCompatibility) { + - (BOOL) batch:(NSArray *)operations error:(NSError **)errorPtr; + - (NSString *) getLocal:(NSString *)key error:(NSError **)errorPtr; + - (BOOL) unsafeResetDatabaseWithSchema:(NSString *)sql schemaVersion:(long)version error:(NSError **)errorPtr; ++- (BOOL) unsafeVacuum:(NSError **)errorPtr; + + @end + +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift +index b4d7151..6a71821 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift +@@ -136,6 +136,10 @@ class Database { + } + } + ++ func unsafeVacuum() throws { ++ try executeStatements("vacuum") ++ } ++ + private var isInMemoryDatabase: Bool { + return path == ":memory:" || path == "file::memory:" || path.contains("?mode=memory") + } +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m +index eb039db..6837d3a 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m +@@ -63,6 +63,8 @@ WMELON_BRIDGE_METHOD(unsafeResetDatabase, + schemaVersion:(nonnull NSNumber *)version + ) + ++WMELON_BRIDGE_METHOD(unsafeVacuum, null) ++ + WMELON_BRIDGE_METHOD(getLocal, + key:(nonnull NSString *)key + ) +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift +index bca17d8..634818e 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift +@@ -184,6 +184,15 @@ extension DatabaseBridge { + } + } + ++ @objc(unsafeVacuum:resolve:reject:) ++ func unsafeVacuum(tag: ConnectionTag, ++ resolve: @escaping RCTPromiseResolveBlock, ++ reject: @escaping RCTPromiseRejectBlock) { ++ withDriver(tag, resolve, reject) { ++ try $0.database.unsafeVacuum() ++ } ++ } ++ + @objc(getLocal:key:resolve:reject:) + func getLocal(tag: ConnectionTag, key: String, + resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { +diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift +index fd8ba18..6f2b3fe 100644 +--- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift ++++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift +@@ -207,6 +207,10 @@ class DatabaseDriver { + } + } + ++ func unsafeVaccum() throws { ++ try database.unsafeVacuum() ++ } ++ + private func migrate(with migrations: MigrationSet) throws { + precondition( + database.userVersion == migrations.from, diff --git a/node_modules/@nozbe/watermelondb/native/shared/Database.cpp b/node_modules/@nozbe/watermelondb/native/shared/Database.cpp -index 8a4e9b4..06acfb1 100644 +index 8a4e9b4..dc567c6 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/Database.cpp +++ b/node_modules/@nozbe/watermelondb/native/shared/Database.cpp @@ -20,7 +20,7 @@ Database::Database(jsi::Runtime *runtime, std::string path, bool usesExclusiveLo @@ -216,6 +704,52 @@ index 8a4e9b4..06acfb1 100644 // set timeout before SQLITE_BUSY error is returned initSql += "pragma busy_timeout = 5000;"; +@@ -102,6 +102,10 @@ void Database::unsafeResetDatabase(jsi::String &schema, int schemaVersion) { + } + } + ++void Database::unsafeVacuum() { ++ executeMultiple("vacuum"); ++} ++ + void Database::migrate(jsi::String &migrationSql, int fromVersion, int toVersion) { + auto &rt = getRt(); + const std::lock_guard lock(mutex_); +diff --git a/node_modules/@nozbe/watermelondb/native/shared/Database.h b/node_modules/@nozbe/watermelondb/native/shared/Database.h +index cf53686..97f5a4b 100644 +--- a/node_modules/@nozbe/watermelondb/native/shared/Database.h ++++ b/node_modules/@nozbe/watermelondb/native/shared/Database.h +@@ -38,6 +38,7 @@ public: + void batchJSON(jsi::String &&operationsJson); + jsi::Value unsafeLoadFromSync(int jsonId, jsi::Object &schema, std::string preamble, std::string postamble); + void unsafeResetDatabase(jsi::String &schema, int schemaVersion); ++ void unsafeVacuum(); + jsi::Value getLocal(jsi::String &key); + void executeMultiple(std::string sql); + +diff --git a/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp b/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp +index ef0f23b..cb49ecd 100644 +--- a/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp ++++ b/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp +@@ -213,6 +213,18 @@ void Database::install(jsi::Runtime *runtime) { + std::abort(); + } + }); ++ createMethod(rt, adapter, "unsafeVacuum", 0, [database](jsi::Runtime &rt, const jsi::Value *args) { ++ assert(database->initialized_); ++ ++ try { ++ database->unsafeVacuum(); ++ return jsi::Value::undefined(); ++ } catch (const std::exception &ex) { ++ consoleError("Failed to vacuum database correctly - " + std::string(ex.what())); ++ // Partially reset database is likely corrupted, so it's probably less bad to crash ++ std::abort(); ++ } ++ }); + createMethod(rt, adapter, "unsafeClose", 0, [database](jsi::Runtime &rt, const jsi::Value *args) { + assert(database->initialized_); + database->destroy(); diff --git a/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp b/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp index e08153b..2fca075 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp @@ -243,3 +777,145 @@ index 22cffa7..4b74a7f 100644 sqlite3 *sqlite; +diff --git a/node_modules/@nozbe/watermelondb/src/Database/index.js b/node_modules/@nozbe/watermelondb/src/Database/index.js +index d4ca4e7..75c1f8e 100644 +--- a/node_modules/@nozbe/watermelondb/src/Database/index.js ++++ b/node_modules/@nozbe/watermelondb/src/Database/index.js +@@ -361,6 +361,24 @@ export default class Database { + } + } + ++ async unsafeVacuum(): Promise { ++ try { ++ // Kill ability to call adapter methods during reset (to catch bugs if someone does this) ++ const { adapter } = this ++ const ErrorAdapter = require('../adapters/error').default ++ this.adapter = (new ErrorAdapter(): any) ++ ++ // Vacuum the database ++ await adapter.unsafeVacuum() ++ ++ // Restore working Database ++ this._resetCount += 1 ++ this.adapter = adapter ++ } finally { ++ this._isBeingReset = false ++ } ++ } ++ + _ensureInWriter(diagnosticMethodName: string): void { + invariant( + this._workQueue.isWriterRunning, +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/compat.js b/node_modules/@nozbe/watermelondb/src/adapters/compat.js +index c5175c0..c8c0c17 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/compat.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/compat.js +@@ -81,6 +81,10 @@ export default class DatabaseAdapterCompat { + return toPromise((callback) => this.underlyingAdapter.unsafeResetDatabase(callback)) + } + ++ unsafeVacuum(): Promise { ++ return toPromise((callback) => this.underlyingAdapter.unsafeVacuum(callback)) ++ } ++ + unsafeExecute(work: UnsafeExecuteOperations): Promise { + return toPromise((callback) => this.underlyingAdapter.unsafeExecute(work, callback)) + } +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/error.js b/node_modules/@nozbe/watermelondb/src/adapters/error.js +index 9f1e847..cdd3af4 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/error.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/error.js +@@ -19,6 +19,7 @@ export default class ErrorAdapter { + 'getDeletedRecords', + 'destroyDeletedRecords', + 'unsafeResetDatabase', ++ 'unsafeVacuum', + 'getLocal', + 'setLocal', + 'removeLocal', +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js +index 0111c87..2f9456a 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js +@@ -340,6 +340,20 @@ export default class SQLiteAdapter implements DatabaseAdapter { + ) + } + ++ unsafeVacuum(callback: ResultCallback { ++ if (result.value) { ++ logger.log('[SQLite] Database is vacuumed') ++ } ++ callback(result) ++ }, ++ ) ++ } ++ + unsafeExecute(operations: UnsafeExecuteOperations, callback: ResultCallback): void { + if (process.env.NODE_ENV !== 'production') { + invariant( +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js +index 480ff4d..ee3935b 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js +@@ -160,6 +160,16 @@ class DatabaseBridge { + ) + } + ++ unsafeVacuum( ++ tag: number, ++ resolve: (any) => void, ++ reject: (string) => void, ++ ): void { ++ this.withDriver(tag, resolve, reject, 'unsafeVacuum', (driver) => ++ driver.unsafeVacuum(), ++ ) ++ } ++ + getLocal(tag: number, key: string, resolve: (any) => void, reject: (string) => void): void { + this.withDriver(tag, resolve, reject, 'getLocal', (driver) => driver.getLocal(key)) + } +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js +index fc1351a..5cb7e68 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js +@@ -222,6 +222,10 @@ class DatabaseDriver { + }) + } + ++ unsafeVacuum(): void { ++ this.database.unsafeVacuum(); ++ } ++ + migrate(migrations: Migrations): void { + const databaseVersion = this.database.userVersion + +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js +index 8cb606b..4ed59bd 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js +@@ -85,6 +85,7 @@ export type SqliteDispatcherMethod = + | 'unsafeLoadFromSync' + | 'provideSyncJson' + | 'unsafeResetDatabase' ++ | 'unsafeVacuum' + | 'getLocal' + | 'unsafeExecuteMultiple' + +diff --git a/node_modules/@nozbe/watermelondb/src/adapters/type.js b/node_modules/@nozbe/watermelondb/src/adapters/type.js +index 8929541..bc3f95d 100644 +--- a/node_modules/@nozbe/watermelondb/src/adapters/type.js ++++ b/node_modules/@nozbe/watermelondb/src/adapters/type.js +@@ -68,6 +68,8 @@ export interface DatabaseAdapter { + // Destroys the whole database, its schema, indexes, everything. + unsafeResetDatabase(callback: ResultCallback): void; + ++ unsafeVacuum(callback: ResultCallback): void; ++ + // Performs work on the underlying database - see concrete DatabaseAdapter implementation for more details + unsafeExecute(work: UnsafeExecuteOperations, callback: ResultCallback): void; +