@evolu/react-native 11.1.2 → 12.0.0-preview.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/expo-sqlite.d.ts +4 -0
- package/dist/expo-sqlite.d.ts.map +1 -0
- package/dist/expo-sqlite.js +31 -0
- package/dist/op-sqlite.d.ts +4 -0
- package/dist/op-sqlite.d.ts.map +1 -0
- package/dist/op-sqlite.js +36 -0
- package/dist/providers/ExpoSqliteDriver.d.ts +3 -0
- package/dist/providers/ExpoSqliteDriver.d.ts.map +1 -0
- package/dist/providers/ExpoSqliteDriver.js +39 -0
- package/dist/providers/OpSqliteDriver.d.ts +3 -0
- package/dist/providers/OpSqliteDriver.d.ts.map +1 -0
- package/dist/providers/OpSqliteDriver.js +46 -0
- package/dist/utils/Hermes.d.ts +3 -0
- package/dist/utils/Hermes.d.ts.map +1 -0
- package/dist/utils/Hermes.js +14 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.d.ts.map +1 -0
- package/package.json +56 -27
- package/src/expo-sqlite.ts +53 -0
- package/src/op-sqlite.ts +56 -0
- package/src/providers/ExpoSqliteDriver.ts +57 -0
- package/src/providers/OpSqliteDriver.ts +64 -0
- package/src/utils/Hermes.ts +14 -0
- package/dist/PlatformLive.d.ts +0 -5
- package/dist/PlatformLive.d.ts.map +0 -1
- package/dist/PlatformLive.js +0 -56
- package/dist/SqliteLive.d.ts +0 -4
- package/dist/SqliteLive.d.ts.map +0 -1
- package/dist/SqliteLive.js +0 -26
- package/dist/dbFactory.d.ts +0 -7
- package/dist/dbFactory.d.ts.map +0 -1
- package/dist/dbFactory.js +0 -21
- package/dist/index.native.d.ts +0 -34
- package/dist/index.native.d.ts.map +0 -1
- package/dist/index.native.js +0 -39
- package/dist/index.web.d.ts +0 -2
- package/dist/index.web.d.ts.map +0 -1
- package/src/PlatformLive.ts +0 -77
- package/src/SqliteLive.ts +0 -46
- package/src/dbFactory.ts +0 -58
- package/src/index.native.ts +0 -43
- /package/dist/{index.web.js → web.js} +0 -0
- /package/src/{index.web.ts → web.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Evolu for React Native
|
|
1
|
+
# Evolu for React Native and Expo
|
|
2
2
|
|
|
3
|
-
[Evolu](https://github.com/evoluhq/evolu) for [React Native](https://reactnative.dev/).
|
|
3
|
+
[Evolu](https://github.com/evoluhq/evolu) for [React Native](https://reactnative.dev/) and [Expo](https://expo.dev/).
|
|
4
4
|
|
|
5
5
|
## Documentation
|
|
6
6
|
|
|
@@ -12,4 +12,4 @@ The Evolu community is on [GitHub Discussions](https://github.com/evoluhq/evolu/
|
|
|
12
12
|
|
|
13
13
|
To chat with other community members, you can join the [Evolu Discord](https://discord.gg/2J8yyyyxtZ).
|
|
14
14
|
|
|
15
|
-
[](https://twitter.com/evoluhq)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expo-sqlite.d.ts","sourceRoot":"","sources":["../src/expo-sqlite.ts"],"names":[],"mappings":"AASA,OAAO,EACL,cAAc,EAId,SAAS,EACV,MAAM,qBAAqB,CAAC;AAS7B,eAAO,MAAM,cAAc,EAAE,cAI3B,CAAC;AAkBH,eAAO,MAAM,oBAAoB,EAAE,SAMlC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createConsole, createEnglishMnemonic, createNanoIdLib, createRandom, createRandomBytes, createTime, } from "@evolu/common";
|
|
2
|
+
import { createDbWorkerForPlatform, createWebSocketSync, } from "@evolu/common/evolu";
|
|
3
|
+
import * as Expo from "expo";
|
|
4
|
+
import { createExpoSqliteDriver } from "./providers/ExpoSqliteDriver.js";
|
|
5
|
+
import { polyfillHermes } from "./utils/Hermes.js";
|
|
6
|
+
polyfillHermes();
|
|
7
|
+
export const createAppState = () => ({
|
|
8
|
+
reset: () => {
|
|
9
|
+
void Expo.reloadAppAsync();
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
const nanoIdLib = createNanoIdLib();
|
|
13
|
+
const console = createConsole();
|
|
14
|
+
const time = createTime();
|
|
15
|
+
const createDbWorker = () => createDbWorkerForPlatform({
|
|
16
|
+
createSqliteDriver: createExpoSqliteDriver,
|
|
17
|
+
createSync: createWebSocketSync,
|
|
18
|
+
console,
|
|
19
|
+
time,
|
|
20
|
+
random: createRandom(),
|
|
21
|
+
nanoIdLib,
|
|
22
|
+
createMnemonic: createEnglishMnemonic,
|
|
23
|
+
createRandomBytes,
|
|
24
|
+
});
|
|
25
|
+
export const evoluReactNativeDeps = {
|
|
26
|
+
time,
|
|
27
|
+
nanoIdLib,
|
|
28
|
+
console,
|
|
29
|
+
createAppState,
|
|
30
|
+
createDbWorker,
|
|
31
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"op-sqlite.d.ts","sourceRoot":"","sources":["../src/op-sqlite.ts"],"names":[],"mappings":"AASA,OAAO,EACL,cAAc,EAId,SAAS,EACV,MAAM,qBAAqB,CAAC;AAQ7B,eAAO,MAAM,cAAc,EAAE,cAQ3B,CAAC;AAkBH,eAAO,MAAM,oBAAoB,EAAE,SAMlC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createConsole, createEnglishMnemonic, createNanoIdLib, createRandom, createRandomBytes, createTime, } from "@evolu/common";
|
|
2
|
+
import { createDbWorkerForPlatform, createWebSocketSync, } from "@evolu/common/evolu";
|
|
3
|
+
import { DevSettings } from "react-native";
|
|
4
|
+
import { createOpSqliteDriver } from "./providers/OpSqliteDriver.js";
|
|
5
|
+
import { polyfillHermes } from "./utils/Hermes.js";
|
|
6
|
+
polyfillHermes();
|
|
7
|
+
export const createAppState = () => ({
|
|
8
|
+
reset: () => {
|
|
9
|
+
if (process.env.NODE_ENV === "development") {
|
|
10
|
+
DevSettings.reload();
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
// TODO: reload not implemented for bare rn
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const nanoIdLib = createNanoIdLib();
|
|
18
|
+
const console = createConsole();
|
|
19
|
+
const time = createTime();
|
|
20
|
+
const createDbWorker = () => createDbWorkerForPlatform({
|
|
21
|
+
createSqliteDriver: createOpSqliteDriver,
|
|
22
|
+
createSync: createWebSocketSync,
|
|
23
|
+
console,
|
|
24
|
+
time,
|
|
25
|
+
random: createRandom(),
|
|
26
|
+
nanoIdLib,
|
|
27
|
+
createMnemonic: createEnglishMnemonic,
|
|
28
|
+
createRandomBytes,
|
|
29
|
+
});
|
|
30
|
+
export const evoluReactNativeDeps = {
|
|
31
|
+
time,
|
|
32
|
+
nanoIdLib,
|
|
33
|
+
console,
|
|
34
|
+
createAppState,
|
|
35
|
+
createDbWorker,
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoSqliteDriver.d.ts","sourceRoot":"","sources":["../../src/providers/ExpoSqliteDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAGnB,MAAM,eAAe,CAAC;AAIvB,eAAO,MAAM,sBAAsB,EAAE,kBA+CpC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createPreparedStatementsCache, } from "@evolu/common";
|
|
2
|
+
import { openDatabaseSync } from "expo-sqlite";
|
|
3
|
+
export const createExpoSqliteDriver = (name) => {
|
|
4
|
+
const db = openDatabaseSync(`evolu1-${name}.db`);
|
|
5
|
+
let isDisposed = false;
|
|
6
|
+
const cache = createPreparedStatementsCache((sql) => db.prepareSync(sql), (statement) => {
|
|
7
|
+
statement.finalizeSync();
|
|
8
|
+
});
|
|
9
|
+
const driver = {
|
|
10
|
+
exec: (query, isMutation) => {
|
|
11
|
+
const prepared = cache.get(query);
|
|
12
|
+
if (prepared) {
|
|
13
|
+
if (isMutation) {
|
|
14
|
+
const { changes } = prepared.executeSync(query.parameters);
|
|
15
|
+
return { rows: [], changes };
|
|
16
|
+
}
|
|
17
|
+
const result = prepared.executeSync(query.parameters);
|
|
18
|
+
const rows = result.getAllSync();
|
|
19
|
+
result.resetSync();
|
|
20
|
+
return { rows: rows, changes: 0 };
|
|
21
|
+
}
|
|
22
|
+
if (isMutation) {
|
|
23
|
+
const { changes } = db.runSync(query.sql, query.parameters);
|
|
24
|
+
return { rows: [], changes };
|
|
25
|
+
}
|
|
26
|
+
const rows = db.getAllSync(query.sql, query.parameters);
|
|
27
|
+
return { rows: rows, changes: 0 };
|
|
28
|
+
},
|
|
29
|
+
export: () => db.serializeSync(),
|
|
30
|
+
[Symbol.dispose]: () => {
|
|
31
|
+
if (isDisposed)
|
|
32
|
+
return;
|
|
33
|
+
isDisposed = true;
|
|
34
|
+
cache[Symbol.dispose]();
|
|
35
|
+
db.closeSync();
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
return Promise.resolve(driver);
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpSqliteDriver.d.ts","sourceRoot":"","sources":["../../src/providers/OpSqliteDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,kBAAkB,EAGnB,MAAM,eAAe,CAAC;AAIvB,eAAO,MAAM,oBAAoB,EAAE,kBAoDlC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { constVoid, createPreparedStatementsCache, isSqlMutation, } from "@evolu/common";
|
|
2
|
+
import { open } from "@op-engineering/op-sqlite";
|
|
3
|
+
export const createOpSqliteDriver = (name) => {
|
|
4
|
+
const db = open({ name: `evolu1-${name}.db` });
|
|
5
|
+
let isDisposed = false;
|
|
6
|
+
const cache = createPreparedStatementsCache((sql) => db.prepareStatement(sql),
|
|
7
|
+
// op-sqlite doesn't have API for that
|
|
8
|
+
constVoid);
|
|
9
|
+
const driver = {
|
|
10
|
+
exec: (query, isMutation) => {
|
|
11
|
+
const prepared = cache.get(query);
|
|
12
|
+
if (prepared) {
|
|
13
|
+
prepared.bindSync(query.parameters);
|
|
14
|
+
if (isSqlMutation(query.sql)) {
|
|
15
|
+
let changes = 0;
|
|
16
|
+
const { rowsAffected } = db.executeSync(query.sql, query.parameters);
|
|
17
|
+
changes += rowsAffected;
|
|
18
|
+
return { rows: [], changes };
|
|
19
|
+
}
|
|
20
|
+
const { rows } = db.executeSync(query.sql, query.parameters);
|
|
21
|
+
return { rows: rows, changes: 0 };
|
|
22
|
+
}
|
|
23
|
+
if (isMutation) {
|
|
24
|
+
let changes = 0;
|
|
25
|
+
const { rowsAffected } = db.executeSync(query.sql, query.parameters);
|
|
26
|
+
changes += rowsAffected;
|
|
27
|
+
return { rows: [], changes };
|
|
28
|
+
}
|
|
29
|
+
const { rows } = db.executeSync(query.sql, query.parameters);
|
|
30
|
+
return { rows: rows, changes: 0 };
|
|
31
|
+
},
|
|
32
|
+
// FIXME: op-sqlite does not expose binary, but a path to the database file
|
|
33
|
+
// another react native dependency would be needed to implement this
|
|
34
|
+
export: () => {
|
|
35
|
+
throw new Error("TODO: Not implemented yet");
|
|
36
|
+
},
|
|
37
|
+
[Symbol.dispose]: () => {
|
|
38
|
+
if (isDisposed)
|
|
39
|
+
return;
|
|
40
|
+
isDisposed = true;
|
|
41
|
+
cache[Symbol.dispose]();
|
|
42
|
+
db.close();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
return Promise.resolve(driver);
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Hermes.d.ts","sourceRoot":"","sources":["../../src/utils/Hermes.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,eAAO,MAAM,cAAc,QAAO,IAYjC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Polyfills `Promise.withResolvers`. */
|
|
2
|
+
export const polyfillHermes = () => {
|
|
3
|
+
if (typeof Promise.withResolvers === "undefined") {
|
|
4
|
+
// @ts-expect-error This is OK.
|
|
5
|
+
Promise.withResolvers = function () {
|
|
6
|
+
let resolve, reject;
|
|
7
|
+
const promise = new Promise((res, rej) => {
|
|
8
|
+
resolve = res;
|
|
9
|
+
reject = rej;
|
|
10
|
+
});
|
|
11
|
+
return { promise, resolve, reject };
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
};
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/react-native",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Evolu for React Native",
|
|
3
|
+
"version": "12.0.0-preview.0",
|
|
4
|
+
"description": "Evolu for React Native and Expo",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
7
|
+
"expo",
|
|
8
|
+
"expo-sqlite",
|
|
9
|
+
"op-sqlite",
|
|
7
10
|
"react-native",
|
|
8
11
|
"react-hooks"
|
|
9
12
|
],
|
|
@@ -15,50 +18,76 @@
|
|
|
15
18
|
},
|
|
16
19
|
"homepage": "https://evolu.dev",
|
|
17
20
|
"type": "module",
|
|
18
|
-
"types": "./dist/index.native.d.ts",
|
|
19
21
|
"exports": {
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
+
"./expo-sqlite": {
|
|
23
|
+
"types": "./dist/expo-sqlite.d.ts",
|
|
24
|
+
"default": "./dist/expo-sqlite.js",
|
|
25
|
+
"react-native": "./dist/expo-sqlite.js",
|
|
26
|
+
"browser": "./dist/web.js"
|
|
27
|
+
},
|
|
28
|
+
"./op-sqlite": {
|
|
29
|
+
"types": "./dist/op-sqlite.d.ts",
|
|
30
|
+
"default": "./dist/op-sqlite.js",
|
|
31
|
+
"react-native": "./dist/op-sqlite.js",
|
|
32
|
+
"browser": "./dist/web.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"typesVersions": {
|
|
36
|
+
"*": {
|
|
37
|
+
"expo-sqlite": [
|
|
38
|
+
"./dist/expo-sqlite.d.ts"
|
|
39
|
+
],
|
|
40
|
+
"op-sqlite": [
|
|
41
|
+
"./dist/op-sqlite.d.ts"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
22
44
|
},
|
|
23
45
|
"files": [
|
|
24
46
|
"dist/**",
|
|
25
47
|
"src/**",
|
|
26
48
|
"README.md"
|
|
27
49
|
],
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"expo-updates": "^0.25.11"
|
|
30
|
-
},
|
|
31
50
|
"devDependencies": {
|
|
32
|
-
"
|
|
33
|
-
"expo": "^
|
|
34
|
-
"expo-sqlite": "
|
|
35
|
-
"react-native": "^0.
|
|
36
|
-
"typescript": "^5.
|
|
37
|
-
"vitest": "^2.
|
|
38
|
-
"@evolu/common": "
|
|
39
|
-
"@evolu/
|
|
40
|
-
"@evolu/
|
|
41
|
-
"@evolu/tsconfig": "0.0.2",
|
|
42
|
-
"eslint-config-evolu": "1.0.0"
|
|
51
|
+
"@op-engineering/op-sqlite": "^14.0.3",
|
|
52
|
+
"expo": "^53.0.10",
|
|
53
|
+
"expo-sqlite": "~15.2.11",
|
|
54
|
+
"react-native": "^0.79.3",
|
|
55
|
+
"typescript": "^5.8.3",
|
|
56
|
+
"vitest": "^3.2.2",
|
|
57
|
+
"@evolu/common": "6.0.0-preview.0",
|
|
58
|
+
"@evolu/react": "9.0.0-preview.0",
|
|
59
|
+
"@evolu/tsconfig": "0.0.2"
|
|
43
60
|
},
|
|
44
61
|
"peerDependencies": {
|
|
45
|
-
"@evolu/common
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
62
|
+
"@evolu/common": "^6.0.0-preview.0",
|
|
63
|
+
"@evolu/react": "^9.0.0-preview.0",
|
|
64
|
+
"@op-engineering/op-sqlite": ">=12",
|
|
65
|
+
"expo": ">=52",
|
|
66
|
+
"expo-sqlite": ">=15",
|
|
67
|
+
"react-native": ">=0.76"
|
|
68
|
+
},
|
|
69
|
+
"peerDependenciesMeta": {
|
|
70
|
+
"@op-engineering/op-sqlite": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"expo": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"expo-sqlite": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
49
79
|
},
|
|
50
80
|
"publishConfig": {
|
|
51
81
|
"access": "public"
|
|
52
82
|
},
|
|
53
83
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
84
|
+
"node": ">=22.0.0"
|
|
55
85
|
},
|
|
56
86
|
"sideEffects": [],
|
|
57
87
|
"scripts": {
|
|
58
88
|
"dev": "tsc --watch",
|
|
59
|
-
"build": "rm -rf dist && tsc",
|
|
60
|
-
"
|
|
61
|
-
"clean": "rm -rf .turbo node_modules dist",
|
|
89
|
+
"build": "shx rm -rf dist && tsc",
|
|
90
|
+
"clean": "shx rm -rf .turbo node_modules dist",
|
|
62
91
|
"format": "prettier --write \"src/*.{ts,tsx,md}\""
|
|
63
92
|
}
|
|
64
93
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createConsole,
|
|
3
|
+
createEnglishMnemonic,
|
|
4
|
+
createNanoIdLib,
|
|
5
|
+
createRandom,
|
|
6
|
+
createRandomBytes,
|
|
7
|
+
createTime,
|
|
8
|
+
} from "@evolu/common";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
CreateAppState,
|
|
12
|
+
CreateDbWorker,
|
|
13
|
+
createDbWorkerForPlatform,
|
|
14
|
+
createWebSocketSync,
|
|
15
|
+
EvoluDeps,
|
|
16
|
+
} from "@evolu/common/evolu";
|
|
17
|
+
|
|
18
|
+
import * as Expo from "expo";
|
|
19
|
+
|
|
20
|
+
import { createExpoSqliteDriver } from "./providers/ExpoSqliteDriver.js";
|
|
21
|
+
import { polyfillHermes } from "./utils/Hermes.js";
|
|
22
|
+
|
|
23
|
+
polyfillHermes();
|
|
24
|
+
|
|
25
|
+
export const createAppState: CreateAppState = () => ({
|
|
26
|
+
reset: () => {
|
|
27
|
+
void Expo.reloadAppAsync();
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const nanoIdLib = createNanoIdLib();
|
|
32
|
+
const console = createConsole();
|
|
33
|
+
const time = createTime();
|
|
34
|
+
|
|
35
|
+
const createDbWorker: CreateDbWorker = () =>
|
|
36
|
+
createDbWorkerForPlatform({
|
|
37
|
+
createSqliteDriver: createExpoSqliteDriver,
|
|
38
|
+
createSync: createWebSocketSync,
|
|
39
|
+
console,
|
|
40
|
+
time,
|
|
41
|
+
random: createRandom(),
|
|
42
|
+
nanoIdLib,
|
|
43
|
+
createMnemonic: createEnglishMnemonic,
|
|
44
|
+
createRandomBytes,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const evoluReactNativeDeps: EvoluDeps = {
|
|
48
|
+
time,
|
|
49
|
+
nanoIdLib,
|
|
50
|
+
console,
|
|
51
|
+
createAppState,
|
|
52
|
+
createDbWorker,
|
|
53
|
+
};
|
package/src/op-sqlite.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createConsole,
|
|
3
|
+
createEnglishMnemonic,
|
|
4
|
+
createNanoIdLib,
|
|
5
|
+
createRandom,
|
|
6
|
+
createRandomBytes,
|
|
7
|
+
createTime,
|
|
8
|
+
} from "@evolu/common";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
CreateAppState,
|
|
12
|
+
CreateDbWorker,
|
|
13
|
+
createDbWorkerForPlatform,
|
|
14
|
+
createWebSocketSync,
|
|
15
|
+
EvoluDeps,
|
|
16
|
+
} from "@evolu/common/evolu";
|
|
17
|
+
|
|
18
|
+
import { DevSettings } from "react-native";
|
|
19
|
+
import { createOpSqliteDriver } from "./providers/OpSqliteDriver.js";
|
|
20
|
+
import { polyfillHermes } from "./utils/Hermes.js";
|
|
21
|
+
|
|
22
|
+
polyfillHermes();
|
|
23
|
+
|
|
24
|
+
export const createAppState: CreateAppState = () => ({
|
|
25
|
+
reset: () => {
|
|
26
|
+
if (process.env.NODE_ENV === "development") {
|
|
27
|
+
DevSettings.reload();
|
|
28
|
+
} else {
|
|
29
|
+
// TODO: reload not implemented for bare rn
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const nanoIdLib = createNanoIdLib();
|
|
35
|
+
const console = createConsole();
|
|
36
|
+
const time = createTime();
|
|
37
|
+
|
|
38
|
+
const createDbWorker: CreateDbWorker = () =>
|
|
39
|
+
createDbWorkerForPlatform({
|
|
40
|
+
createSqliteDriver: createOpSqliteDriver,
|
|
41
|
+
createSync: createWebSocketSync,
|
|
42
|
+
console,
|
|
43
|
+
time,
|
|
44
|
+
random: createRandom(),
|
|
45
|
+
nanoIdLib,
|
|
46
|
+
createMnemonic: createEnglishMnemonic,
|
|
47
|
+
createRandomBytes,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const evoluReactNativeDeps: EvoluDeps = {
|
|
51
|
+
time,
|
|
52
|
+
nanoIdLib,
|
|
53
|
+
console,
|
|
54
|
+
createAppState,
|
|
55
|
+
createDbWorker,
|
|
56
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPreparedStatementsCache,
|
|
3
|
+
CreateSqliteDriver,
|
|
4
|
+
SqliteDriver,
|
|
5
|
+
SqliteRow,
|
|
6
|
+
} from "@evolu/common";
|
|
7
|
+
|
|
8
|
+
import { openDatabaseSync, SQLiteStatement } from "expo-sqlite";
|
|
9
|
+
|
|
10
|
+
export const createExpoSqliteDriver: CreateSqliteDriver = (name) => {
|
|
11
|
+
const db = openDatabaseSync(`evolu1-${name}.db`);
|
|
12
|
+
let isDisposed = false;
|
|
13
|
+
|
|
14
|
+
const cache = createPreparedStatementsCache<SQLiteStatement>(
|
|
15
|
+
(sql) => db.prepareSync(sql),
|
|
16
|
+
(statement) => {
|
|
17
|
+
statement.finalizeSync();
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const driver: SqliteDriver = {
|
|
22
|
+
exec: (query, isMutation) => {
|
|
23
|
+
const prepared = cache.get(query);
|
|
24
|
+
|
|
25
|
+
if (prepared) {
|
|
26
|
+
if (isMutation) {
|
|
27
|
+
const { changes } = prepared.executeSync(query.parameters);
|
|
28
|
+
return { rows: [], changes };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const result = prepared.executeSync(query.parameters);
|
|
32
|
+
const rows = result.getAllSync();
|
|
33
|
+
result.resetSync();
|
|
34
|
+
return { rows: rows as Array<SqliteRow>, changes: 0 };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isMutation) {
|
|
38
|
+
const { changes } = db.runSync(query.sql, query.parameters);
|
|
39
|
+
return { rows: [], changes };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const rows = db.getAllSync(query.sql, query.parameters);
|
|
43
|
+
return { rows: rows as Array<SqliteRow>, changes: 0 };
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
export: () => db.serializeSync(),
|
|
47
|
+
|
|
48
|
+
[Symbol.dispose]: () => {
|
|
49
|
+
if (isDisposed) return;
|
|
50
|
+
isDisposed = true;
|
|
51
|
+
cache[Symbol.dispose]();
|
|
52
|
+
db.closeSync();
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return Promise.resolve(driver);
|
|
57
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
constVoid,
|
|
3
|
+
createPreparedStatementsCache,
|
|
4
|
+
isSqlMutation,
|
|
5
|
+
CreateSqliteDriver,
|
|
6
|
+
SqliteDriver,
|
|
7
|
+
SqliteRow,
|
|
8
|
+
} from "@evolu/common";
|
|
9
|
+
|
|
10
|
+
import { open, PreparedStatement } from "@op-engineering/op-sqlite";
|
|
11
|
+
|
|
12
|
+
export const createOpSqliteDriver: CreateSqliteDriver = (name) => {
|
|
13
|
+
const db = open({ name: `evolu1-${name}.db` });
|
|
14
|
+
let isDisposed = false;
|
|
15
|
+
|
|
16
|
+
const cache = createPreparedStatementsCache<PreparedStatement>(
|
|
17
|
+
(sql) => db.prepareStatement(sql),
|
|
18
|
+
// op-sqlite doesn't have API for that
|
|
19
|
+
constVoid,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const driver: SqliteDriver = {
|
|
23
|
+
exec: (query, isMutation) => {
|
|
24
|
+
const prepared = cache.get(query);
|
|
25
|
+
|
|
26
|
+
if (prepared) {
|
|
27
|
+
prepared.bindSync(query.parameters);
|
|
28
|
+
if (isSqlMutation(query.sql)) {
|
|
29
|
+
let changes = 0;
|
|
30
|
+
const { rowsAffected } = db.executeSync(query.sql, query.parameters);
|
|
31
|
+
changes += rowsAffected;
|
|
32
|
+
return { rows: [], changes };
|
|
33
|
+
}
|
|
34
|
+
const { rows } = db.executeSync(query.sql, query.parameters);
|
|
35
|
+
return { rows: rows as Array<SqliteRow>, changes: 0 };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isMutation) {
|
|
39
|
+
let changes = 0;
|
|
40
|
+
const { rowsAffected } = db.executeSync(query.sql, query.parameters);
|
|
41
|
+
changes += rowsAffected;
|
|
42
|
+
return { rows: [], changes };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { rows } = db.executeSync(query.sql, query.parameters);
|
|
46
|
+
return { rows: rows as Array<SqliteRow>, changes: 0 };
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// FIXME: op-sqlite does not expose binary, but a path to the database file
|
|
50
|
+
// another react native dependency would be needed to implement this
|
|
51
|
+
export: () => {
|
|
52
|
+
throw new Error("TODO: Not implemented yet");
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
[Symbol.dispose]: () => {
|
|
56
|
+
if (isDisposed) return;
|
|
57
|
+
isDisposed = true;
|
|
58
|
+
cache[Symbol.dispose]();
|
|
59
|
+
db.close();
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return Promise.resolve(driver);
|
|
64
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Polyfills `Promise.withResolvers`. */
|
|
2
|
+
export const polyfillHermes = (): void => {
|
|
3
|
+
if (typeof Promise.withResolvers === "undefined") {
|
|
4
|
+
// @ts-expect-error This is OK.
|
|
5
|
+
Promise.withResolvers = function () {
|
|
6
|
+
let resolve, reject;
|
|
7
|
+
const promise = new Promise((res, rej) => {
|
|
8
|
+
resolve = res;
|
|
9
|
+
reject = rej;
|
|
10
|
+
});
|
|
11
|
+
return { promise, resolve, reject };
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
};
|
package/dist/PlatformLive.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { AppState, SyncLock } from "@evolu/common";
|
|
2
|
-
import * as Layer from "effect/Layer";
|
|
3
|
-
export declare const AppStateLive: Layer.Layer<AppState, never, never>;
|
|
4
|
-
export declare const SyncLockLive: Layer.Layer<SyncLock, never, never>;
|
|
5
|
-
//# sourceMappingURL=PlatformLive.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PlatformLive.d.ts","sourceRoot":"","sources":["../src/PlatformLive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EAGT,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAItC,eAAO,MAAM,YAAY,qCAmCxB,CAAC;AAEF,eAAO,MAAM,YAAY,qCA2BxB,CAAC"}
|
package/dist/PlatformLive.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { AppState, SyncLock, SyncLockAlreadySyncingError, } from "@evolu/common";
|
|
2
|
-
import NetInfo from "@react-native-community/netinfo";
|
|
3
|
-
import * as Effect from "effect/Effect";
|
|
4
|
-
import * as Layer from "effect/Layer";
|
|
5
|
-
import { reloadAsync } from "expo-updates";
|
|
6
|
-
import { DevSettings, AppState as ReactNativeAppState } from "react-native";
|
|
7
|
-
export const AppStateLive = Layer.succeed(AppState, AppState.of({
|
|
8
|
-
init: ({ onRequestSync }) => Effect.sync(() => {
|
|
9
|
-
let appStateStatus = ReactNativeAppState.currentState;
|
|
10
|
-
ReactNativeAppState.addEventListener("change", (current) => {
|
|
11
|
-
if (appStateStatus.match(/inactive|background/) &&
|
|
12
|
-
current === "active")
|
|
13
|
-
onRequestSync();
|
|
14
|
-
appStateStatus = current;
|
|
15
|
-
});
|
|
16
|
-
let netInfoState = null;
|
|
17
|
-
NetInfo.addEventListener((current) => {
|
|
18
|
-
if (netInfoState?.isInternetReachable === false &&
|
|
19
|
-
current.isConnected &&
|
|
20
|
-
current.isInternetReachable)
|
|
21
|
-
onRequestSync();
|
|
22
|
-
netInfoState = current;
|
|
23
|
-
});
|
|
24
|
-
const reset = process.env.NODE_ENV === "development"
|
|
25
|
-
? Effect.sync(() => {
|
|
26
|
-
DevSettings.reload();
|
|
27
|
-
})
|
|
28
|
-
: Effect.promise(() => reloadAsync());
|
|
29
|
-
return { reset };
|
|
30
|
-
}),
|
|
31
|
-
}));
|
|
32
|
-
export const SyncLockLive = Layer.effect(SyncLock, Effect.sync(() => {
|
|
33
|
-
let hasSyncLock = false;
|
|
34
|
-
return SyncLock.of({
|
|
35
|
-
tryAcquire: Effect.gen(function* () {
|
|
36
|
-
yield* Effect.logTrace("SyncLock tryAcquire");
|
|
37
|
-
const acquire = Effect.gen(function* () {
|
|
38
|
-
if (hasSyncLock) {
|
|
39
|
-
yield* Effect.logTrace("SyncLock not acquired");
|
|
40
|
-
yield* Effect.fail(new SyncLockAlreadySyncingError());
|
|
41
|
-
}
|
|
42
|
-
yield* Effect.logTrace("SyncLock acquired");
|
|
43
|
-
hasSyncLock = true;
|
|
44
|
-
const syncLockRelease = {
|
|
45
|
-
release: Effect.gen(function* () {
|
|
46
|
-
yield* Effect.logTrace("SyncLock released");
|
|
47
|
-
hasSyncLock = false;
|
|
48
|
-
}),
|
|
49
|
-
};
|
|
50
|
-
return syncLockRelease;
|
|
51
|
-
});
|
|
52
|
-
const release = ({ release }) => release;
|
|
53
|
-
return yield* Effect.acquireRelease(acquire, release);
|
|
54
|
-
}),
|
|
55
|
-
});
|
|
56
|
-
}));
|
package/dist/SqliteLive.d.ts
DELETED
package/dist/SqliteLive.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteLive.d.ts","sourceRoot":"","sources":["../src/SqliteLive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EAKP,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,eAAO,MAAM,UAAU,oCAiCtB,CAAC"}
|
package/dist/SqliteLive.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Config, Sqlite, isSqlMutation, maybeLogSqliteQueryExecutionTime, valuesToSqliteValues, } from "@evolu/common";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Layer from "effect/Layer";
|
|
4
|
-
import * as ExpoSQLite from "expo-sqlite";
|
|
5
|
-
export const SqliteLive = Layer.effect(Sqlite, Effect.gen(function* () {
|
|
6
|
-
const config = yield* Config;
|
|
7
|
-
const db = ExpoSQLite.openDatabaseSync(`evolu1-${config.name}.db`);
|
|
8
|
-
const mutex = yield* Effect.makeSemaphore(1);
|
|
9
|
-
return Sqlite.of({
|
|
10
|
-
exec: (query) => Effect.gen(function* () {
|
|
11
|
-
const parameters = valuesToSqliteValues(query.parameters || []);
|
|
12
|
-
if (!isSqlMutation(query.sql)) {
|
|
13
|
-
const rows = (yield* Effect.promise(() => db.getAllAsync(query.sql, parameters)).pipe(maybeLogSqliteQueryExecutionTime(query)));
|
|
14
|
-
return { rows, changes: 0 };
|
|
15
|
-
}
|
|
16
|
-
const { changes } = yield* Effect.promise(() => db.runAsync(query.sql, parameters));
|
|
17
|
-
return { rows: [], changes };
|
|
18
|
-
}),
|
|
19
|
-
// RN doesn't need the "last" mode because there are no tabs.
|
|
20
|
-
transaction: () => mutex.withPermits(1),
|
|
21
|
-
export: () => Effect.sync(() => {
|
|
22
|
-
throw new Error("expo-sqlite/next does not support serializeSync");
|
|
23
|
-
return db.serializeSync();
|
|
24
|
-
}),
|
|
25
|
-
});
|
|
26
|
-
}));
|
package/dist/dbFactory.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { EvoluFactory, SqliteFactory, SyncFactory } from "@evolu/common";
|
|
2
|
-
import * as Layer from "effect/Layer";
|
|
3
|
-
export declare const NanoIdGeneratorLive: Layer.Layer<import("@evolu/common").NanoIdGenerator, never, never>;
|
|
4
|
-
export declare const SyncFactoryLive: Layer.Layer<SyncFactory, never, never>;
|
|
5
|
-
export declare const SqliteFactoryLive: Layer.Layer<SqliteFactory, never, never>;
|
|
6
|
-
export declare const EvoluFactoryReactNative: Layer.Layer<EvoluFactory, never, never>;
|
|
7
|
-
//# sourceMappingURL=dbFactory.d.ts.map
|
package/dist/dbFactory.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dbFactory.d.ts","sourceRoot":"","sources":["../src/dbFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EAGZ,aAAa,EACb,WAAW,EAKZ,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAMtC,eAAO,MAAM,mBAAmB,oEAK/B,CAAC;AAEF,eAAO,MAAM,eAAe,wCAE1B,CAAC;AAEH,eAAO,MAAM,iBAAiB,0CAE5B,CAAC;AAEH,eAAO,MAAM,uBAAuB,yCAsBnC,CAAC"}
|
package/dist/dbFactory.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DbFactory, EvoluFactory, SecretBox, Sqlite, SqliteFactory, SyncFactory, Time, createDb, createNanoIdGeneratorLive, createSync, } from "@evolu/common";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Layer from "effect/Layer";
|
|
4
|
-
// @ts-expect-error https://github.com/ai/nanoid/issues/468
|
|
5
|
-
import { customAlphabet, nanoid } from "nanoid/index.browser.js";
|
|
6
|
-
import { AppStateLive, SyncLockLive } from "./PlatformLive.js";
|
|
7
|
-
import { SqliteLive } from "./SqliteLive.js";
|
|
8
|
-
export const NanoIdGeneratorLive = createNanoIdGeneratorLive(
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
10
|
-
customAlphabet,
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
12
|
-
nanoid);
|
|
13
|
-
export const SyncFactoryLive = Layer.succeed(SyncFactory, {
|
|
14
|
-
createSync: Effect.provide(createSync, SecretBox.Live),
|
|
15
|
-
});
|
|
16
|
-
export const SqliteFactoryLive = Layer.succeed(SqliteFactory, {
|
|
17
|
-
createSqlite: Sqlite.pipe(Effect.provide(SqliteLive)),
|
|
18
|
-
});
|
|
19
|
-
export const EvoluFactoryReactNative = Layer.provide(EvoluFactory.Common, Layer.mergeAll(Layer.succeed(DbFactory, {
|
|
20
|
-
createDb: createDb.pipe(Effect.provide(Layer.mergeAll(SqliteFactory.Common.pipe(Layer.provide(SqliteFactoryLive), Layer.provide(NanoIdGeneratorLive)), NanoIdGeneratorLive, Time.Live, SyncFactoryLive, SyncLockLive))),
|
|
21
|
-
}), NanoIdGeneratorLive, AppStateLive));
|
package/dist/index.native.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export * from "@evolu/common-react";
|
|
2
|
-
export * from "@evolu/common/public";
|
|
3
|
-
export declare const
|
|
4
|
-
/**
|
|
5
|
-
* Create Evolu from the database schema.
|
|
6
|
-
*
|
|
7
|
-
* Tables with a name prefixed with `_` are local-only, which means they are
|
|
8
|
-
* never synced. It's useful for device-specific or temporal data.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* import * as S from "@effect/schema/Schema";
|
|
12
|
-
* import * as E from "@evolu/react-native";
|
|
13
|
-
*
|
|
14
|
-
* const TodoId = E.id("Todo");
|
|
15
|
-
* type TodoId = typeof TodoId.Type;
|
|
16
|
-
*
|
|
17
|
-
* const TodoTable = E.table({
|
|
18
|
-
* id: TodoId,
|
|
19
|
-
* title: E.NonEmptyString1000,
|
|
20
|
-
* });
|
|
21
|
-
* type TodoTable = typeof TodoTable.Type;
|
|
22
|
-
*
|
|
23
|
-
* const Database = E.database({
|
|
24
|
-
* todo: TodoTable,
|
|
25
|
-
*
|
|
26
|
-
* // Prefix `_` makes the table local-only (it will not sync)
|
|
27
|
-
* _todo: TodoTable,
|
|
28
|
-
* });
|
|
29
|
-
* type Database = typeof Database.Type;
|
|
30
|
-
*
|
|
31
|
-
* const evolu = E.createEvolu(Database);
|
|
32
|
-
*/
|
|
33
|
-
createEvolu: <T extends import("@evolu/common").EvoluSchema, I>(schema: import("@effect/schema/Schema").Schema<T, I>, config?: Partial<import("@evolu/common").EvoluConfig<T>>) => import("@evolu/common").Evolu<T>;
|
|
34
|
-
//# sourceMappingURL=index.native.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../src/index.native.ts"],"names":[],"mappings":"AAEA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AAQrC,eAAO;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,WAAW,wMACiE,CAAC"}
|
package/dist/index.native.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { EvoluFactory } from "@evolu/common";
|
|
2
|
-
export * from "@evolu/common-react";
|
|
3
|
-
export * from "@evolu/common/public";
|
|
4
|
-
import * as Effect from "effect/Effect";
|
|
5
|
-
import { EvoluFactoryReactNative } from "./dbFactory.js";
|
|
6
|
-
// JSDoc doesn't support destructured parameters, so we must copy-paste
|
|
7
|
-
// createEvolu docs from `evolu-common/src/Evolu.ts`.
|
|
8
|
-
// https://github.com/microsoft/TypeScript/issues/11859
|
|
9
|
-
export const {
|
|
10
|
-
/**
|
|
11
|
-
* Create Evolu from the database schema.
|
|
12
|
-
*
|
|
13
|
-
* Tables with a name prefixed with `_` are local-only, which means they are
|
|
14
|
-
* never synced. It's useful for device-specific or temporal data.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* import * as S from "@effect/schema/Schema";
|
|
18
|
-
* import * as E from "@evolu/react-native";
|
|
19
|
-
*
|
|
20
|
-
* const TodoId = E.id("Todo");
|
|
21
|
-
* type TodoId = typeof TodoId.Type;
|
|
22
|
-
*
|
|
23
|
-
* const TodoTable = E.table({
|
|
24
|
-
* id: TodoId,
|
|
25
|
-
* title: E.NonEmptyString1000,
|
|
26
|
-
* });
|
|
27
|
-
* type TodoTable = typeof TodoTable.Type;
|
|
28
|
-
*
|
|
29
|
-
* const Database = E.database({
|
|
30
|
-
* todo: TodoTable,
|
|
31
|
-
*
|
|
32
|
-
* // Prefix `_` makes the table local-only (it will not sync)
|
|
33
|
-
* _todo: TodoTable,
|
|
34
|
-
* });
|
|
35
|
-
* type Database = typeof Database.Type;
|
|
36
|
-
*
|
|
37
|
-
* const evolu = E.createEvolu(Database);
|
|
38
|
-
*/
|
|
39
|
-
createEvolu, } = EvoluFactory.pipe(Effect.provide(EvoluFactoryReactNative), Effect.runSync);
|
package/dist/index.web.d.ts
DELETED
package/dist/index.web.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../src/index.web.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
|
package/src/PlatformLive.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AppState,
|
|
3
|
-
SyncLock,
|
|
4
|
-
SyncLockAlreadySyncingError,
|
|
5
|
-
SyncLockRelease,
|
|
6
|
-
} from "@evolu/common";
|
|
7
|
-
import NetInfo, { NetInfoState } from "@react-native-community/netinfo";
|
|
8
|
-
import * as Effect from "effect/Effect";
|
|
9
|
-
import * as Layer from "effect/Layer";
|
|
10
|
-
import { reloadAsync } from "expo-updates";
|
|
11
|
-
import { DevSettings, AppState as ReactNativeAppState } from "react-native";
|
|
12
|
-
|
|
13
|
-
export const AppStateLive = Layer.succeed(
|
|
14
|
-
AppState,
|
|
15
|
-
AppState.of({
|
|
16
|
-
init: ({ onRequestSync }) =>
|
|
17
|
-
Effect.sync(() => {
|
|
18
|
-
let appStateStatus = ReactNativeAppState.currentState;
|
|
19
|
-
ReactNativeAppState.addEventListener("change", (current) => {
|
|
20
|
-
if (
|
|
21
|
-
appStateStatus.match(/inactive|background/) &&
|
|
22
|
-
current === "active"
|
|
23
|
-
)
|
|
24
|
-
onRequestSync();
|
|
25
|
-
appStateStatus = current;
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
let netInfoState: NetInfoState | null = null;
|
|
29
|
-
NetInfo.addEventListener((current) => {
|
|
30
|
-
if (
|
|
31
|
-
netInfoState?.isInternetReachable === false &&
|
|
32
|
-
current.isConnected &&
|
|
33
|
-
current.isInternetReachable
|
|
34
|
-
)
|
|
35
|
-
onRequestSync();
|
|
36
|
-
netInfoState = current;
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const reset =
|
|
40
|
-
process.env.NODE_ENV === "development"
|
|
41
|
-
? Effect.sync(() => {
|
|
42
|
-
DevSettings.reload();
|
|
43
|
-
})
|
|
44
|
-
: Effect.promise(() => reloadAsync());
|
|
45
|
-
return { reset };
|
|
46
|
-
}),
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
export const SyncLockLive = Layer.effect(
|
|
51
|
-
SyncLock,
|
|
52
|
-
Effect.sync(() => {
|
|
53
|
-
let hasSyncLock = false;
|
|
54
|
-
return SyncLock.of({
|
|
55
|
-
tryAcquire: Effect.gen(function* () {
|
|
56
|
-
yield* Effect.logTrace("SyncLock tryAcquire");
|
|
57
|
-
const acquire = Effect.gen(function* () {
|
|
58
|
-
if (hasSyncLock) {
|
|
59
|
-
yield* Effect.logTrace("SyncLock not acquired");
|
|
60
|
-
yield* Effect.fail(new SyncLockAlreadySyncingError());
|
|
61
|
-
}
|
|
62
|
-
yield* Effect.logTrace("SyncLock acquired");
|
|
63
|
-
hasSyncLock = true;
|
|
64
|
-
const syncLockRelease: SyncLockRelease = {
|
|
65
|
-
release: Effect.gen(function* () {
|
|
66
|
-
yield* Effect.logTrace("SyncLock released");
|
|
67
|
-
hasSyncLock = false;
|
|
68
|
-
}),
|
|
69
|
-
};
|
|
70
|
-
return syncLockRelease;
|
|
71
|
-
});
|
|
72
|
-
const release = ({ release }: SyncLockRelease) => release;
|
|
73
|
-
return yield* Effect.acquireRelease(acquire, release);
|
|
74
|
-
}),
|
|
75
|
-
});
|
|
76
|
-
}),
|
|
77
|
-
);
|
package/src/SqliteLive.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Config,
|
|
3
|
-
Sqlite,
|
|
4
|
-
SqliteRow,
|
|
5
|
-
isSqlMutation,
|
|
6
|
-
maybeLogSqliteQueryExecutionTime,
|
|
7
|
-
valuesToSqliteValues,
|
|
8
|
-
} from "@evolu/common";
|
|
9
|
-
import * as Effect from "effect/Effect";
|
|
10
|
-
import * as Layer from "effect/Layer";
|
|
11
|
-
import * as ExpoSQLite from "expo-sqlite";
|
|
12
|
-
|
|
13
|
-
export const SqliteLive = Layer.effect(
|
|
14
|
-
Sqlite,
|
|
15
|
-
Effect.gen(function* () {
|
|
16
|
-
const config = yield* Config;
|
|
17
|
-
const db = ExpoSQLite.openDatabaseSync(`evolu1-${config.name}.db`);
|
|
18
|
-
const mutex = yield* Effect.makeSemaphore(1);
|
|
19
|
-
|
|
20
|
-
return Sqlite.of({
|
|
21
|
-
exec: (query) =>
|
|
22
|
-
Effect.gen(function* () {
|
|
23
|
-
const parameters = valuesToSqliteValues(query.parameters || []);
|
|
24
|
-
if (!isSqlMutation(query.sql)) {
|
|
25
|
-
const rows = (yield* Effect.promise(() =>
|
|
26
|
-
db.getAllAsync(query.sql, parameters),
|
|
27
|
-
).pipe(maybeLogSqliteQueryExecutionTime(query))) as SqliteRow[];
|
|
28
|
-
return { rows, changes: 0 };
|
|
29
|
-
}
|
|
30
|
-
const { changes } = yield* Effect.promise(() =>
|
|
31
|
-
db.runAsync(query.sql, parameters),
|
|
32
|
-
);
|
|
33
|
-
return { rows: [], changes };
|
|
34
|
-
}),
|
|
35
|
-
|
|
36
|
-
// RN doesn't need the "last" mode because there are no tabs.
|
|
37
|
-
transaction: () => mutex.withPermits(1),
|
|
38
|
-
|
|
39
|
-
export: () =>
|
|
40
|
-
Effect.sync(() => {
|
|
41
|
-
throw new Error("expo-sqlite/next does not support serializeSync");
|
|
42
|
-
return db.serializeSync();
|
|
43
|
-
}),
|
|
44
|
-
});
|
|
45
|
-
}),
|
|
46
|
-
);
|
package/src/dbFactory.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DbFactory,
|
|
3
|
-
EvoluFactory,
|
|
4
|
-
SecretBox,
|
|
5
|
-
Sqlite,
|
|
6
|
-
SqliteFactory,
|
|
7
|
-
SyncFactory,
|
|
8
|
-
Time,
|
|
9
|
-
createDb,
|
|
10
|
-
createNanoIdGeneratorLive,
|
|
11
|
-
createSync,
|
|
12
|
-
} from "@evolu/common";
|
|
13
|
-
|
|
14
|
-
import * as Effect from "effect/Effect";
|
|
15
|
-
import * as Layer from "effect/Layer";
|
|
16
|
-
// @ts-expect-error https://github.com/ai/nanoid/issues/468
|
|
17
|
-
import { customAlphabet, nanoid } from "nanoid/index.browser.js";
|
|
18
|
-
import { AppStateLive, SyncLockLive } from "./PlatformLive.js";
|
|
19
|
-
import { SqliteLive } from "./SqliteLive.js";
|
|
20
|
-
|
|
21
|
-
export const NanoIdGeneratorLive = createNanoIdGeneratorLive(
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
23
|
-
customAlphabet,
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
25
|
-
nanoid,
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
export const SyncFactoryLive = Layer.succeed(SyncFactory, {
|
|
29
|
-
createSync: Effect.provide(createSync, SecretBox.Live),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
export const SqliteFactoryLive = Layer.succeed(SqliteFactory, {
|
|
33
|
-
createSqlite: Sqlite.pipe(Effect.provide(SqliteLive)),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
export const EvoluFactoryReactNative = Layer.provide(
|
|
37
|
-
EvoluFactory.Common,
|
|
38
|
-
Layer.mergeAll(
|
|
39
|
-
Layer.succeed(DbFactory, {
|
|
40
|
-
createDb: createDb.pipe(
|
|
41
|
-
Effect.provide(
|
|
42
|
-
Layer.mergeAll(
|
|
43
|
-
SqliteFactory.Common.pipe(
|
|
44
|
-
Layer.provide(SqliteFactoryLive),
|
|
45
|
-
Layer.provide(NanoIdGeneratorLive),
|
|
46
|
-
),
|
|
47
|
-
NanoIdGeneratorLive,
|
|
48
|
-
Time.Live,
|
|
49
|
-
SyncFactoryLive,
|
|
50
|
-
SyncLockLive,
|
|
51
|
-
),
|
|
52
|
-
),
|
|
53
|
-
),
|
|
54
|
-
}),
|
|
55
|
-
NanoIdGeneratorLive,
|
|
56
|
-
AppStateLive,
|
|
57
|
-
),
|
|
58
|
-
);
|
package/src/index.native.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { EvoluFactory } from "@evolu/common";
|
|
2
|
-
|
|
3
|
-
export * from "@evolu/common-react";
|
|
4
|
-
export * from "@evolu/common/public";
|
|
5
|
-
|
|
6
|
-
import * as Effect from "effect/Effect";
|
|
7
|
-
import { EvoluFactoryReactNative } from "./dbFactory.js";
|
|
8
|
-
|
|
9
|
-
// JSDoc doesn't support destructured parameters, so we must copy-paste
|
|
10
|
-
// createEvolu docs from `evolu-common/src/Evolu.ts`.
|
|
11
|
-
// https://github.com/microsoft/TypeScript/issues/11859
|
|
12
|
-
export const {
|
|
13
|
-
/**
|
|
14
|
-
* Create Evolu from the database schema.
|
|
15
|
-
*
|
|
16
|
-
* Tables with a name prefixed with `_` are local-only, which means they are
|
|
17
|
-
* never synced. It's useful for device-specific or temporal data.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* import * as S from "@effect/schema/Schema";
|
|
21
|
-
* import * as E from "@evolu/react-native";
|
|
22
|
-
*
|
|
23
|
-
* const TodoId = E.id("Todo");
|
|
24
|
-
* type TodoId = typeof TodoId.Type;
|
|
25
|
-
*
|
|
26
|
-
* const TodoTable = E.table({
|
|
27
|
-
* id: TodoId,
|
|
28
|
-
* title: E.NonEmptyString1000,
|
|
29
|
-
* });
|
|
30
|
-
* type TodoTable = typeof TodoTable.Type;
|
|
31
|
-
*
|
|
32
|
-
* const Database = E.database({
|
|
33
|
-
* todo: TodoTable,
|
|
34
|
-
*
|
|
35
|
-
* // Prefix `_` makes the table local-only (it will not sync)
|
|
36
|
-
* _todo: TodoTable,
|
|
37
|
-
* });
|
|
38
|
-
* type Database = typeof Database.Type;
|
|
39
|
-
*
|
|
40
|
-
* const evolu = E.createEvolu(Database);
|
|
41
|
-
*/
|
|
42
|
-
createEvolu,
|
|
43
|
-
} = EvoluFactory.pipe(Effect.provide(EvoluFactoryReactNative), Effect.runSync);
|
|
File without changes
|
|
File without changes
|