@lika85456/s3qlite 0.1.1 → 0.2.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 +121 -2
- package/cjs/src/connection.js +13 -6
- package/cjs/src/connection.js.map +1 -1
- package/cjs/src/fork.js +42 -0
- package/cjs/src/fork.js.map +1 -0
- package/cjs/src/index.js +1 -0
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/push.js.map +1 -1
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/wrapDatabase.js +10 -2
- package/cjs/src/wrapDatabase.js.map +1 -1
- package/dts/src/fork.d.ts +30 -0
- package/dts/src/index.d.ts +1 -0
- package/dts/src/push.d.ts +2 -1
- package/dts/src/types.d.ts +5 -3
- package/esm/src/connection.js +14 -7
- package/esm/src/connection.js.map +1 -1
- package/esm/src/fork.js +35 -0
- package/esm/src/fork.js.map +1 -0
- package/esm/src/index.js +1 -0
- package/esm/src/index.js.map +1 -1
- package/esm/src/push.js.map +1 -1
- package/esm/src/types.js.map +1 -1
- package/esm/src/wrapDatabase.js +10 -2
- package/esm/src/wrapDatabase.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,126 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./apps/docs/public/s3qlite_logo.png" width="400" alt="S3Qlite" />
|
|
3
|
+
</div>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
In-process SQL database with sub-millisecond latency persisted on S3 for durability and easy distribution.
|
|
6
|
+
|
|
7
|
+
This library provides a wrapper over [Turso database](https://turso.tech/) extending it with additional methods for synchronization.
|
|
8
|
+
|
|
9
|
+
Tired of not paying a single cent with the generous free tier of Turso Cloud? Run your own!
|
|
4
10
|
|
|
5
11
|
# Installation
|
|
6
12
|
|
|
7
13
|
`bun add @lika85456/s3qlite`
|
|
14
|
+
|
|
15
|
+
# Usage
|
|
16
|
+
|
|
17
|
+
## Promise API
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { connect } from "@lika85456/s3qlite";
|
|
21
|
+
|
|
22
|
+
const db = await connect(
|
|
23
|
+
"agent-123", // non-existing database will be created on first sync
|
|
24
|
+
{
|
|
25
|
+
bucket: "s3qlite", // single bucket can store multiple databases
|
|
26
|
+
localDirectory: "./.s3qlite", // the local database, batches and other files need to be stored in localDirectory
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
await db.exec(`
|
|
31
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
32
|
+
id TEXT PRIMARY KEY,
|
|
33
|
+
name TEXT NOT NULL
|
|
34
|
+
)
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
await db.run("INSERT INTO users (id, name) VALUES (?, ?)", "1", "alice");
|
|
38
|
+
|
|
39
|
+
// Downloads remote changes and uploads local changes.
|
|
40
|
+
await db.sync();
|
|
41
|
+
|
|
42
|
+
const users = await db.all("SELECT * FROM users ORDER BY id");
|
|
43
|
+
console.log(users);
|
|
44
|
+
|
|
45
|
+
await db.close();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Effect API
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { connect } from "@lika85456/s3qlite/effect";
|
|
52
|
+
import { Effect } from "effect";
|
|
53
|
+
|
|
54
|
+
const program = Effect.gen(function* () {
|
|
55
|
+
const db = yield* connect("user-123", {
|
|
56
|
+
bucket: "s3qlite",
|
|
57
|
+
localDirectory: "./.s3qlite",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
yield* Effect.tryPromise(() =>
|
|
61
|
+
db.exec(`
|
|
62
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
63
|
+
id TEXT PRIMARY KEY,
|
|
64
|
+
name TEXT NOT NULL
|
|
65
|
+
)
|
|
66
|
+
`),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
yield* Effect.tryPromise(() =>
|
|
70
|
+
db.run("INSERT INTO users (id, name) VALUES (?, ?)", "1", "alice"),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
yield* db.sync();
|
|
74
|
+
|
|
75
|
+
const users = yield* Effect.tryPromise(
|
|
76
|
+
() => db.all("SELECT * FROM users ORDER BY id") as Promise<unknown[]>,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
console.log(users);
|
|
80
|
+
|
|
81
|
+
yield* Effect.tryPromise(() => db.close());
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await Effect.runPromise(program);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Explicit S3 Config
|
|
88
|
+
|
|
89
|
+
S3Qlite uses default AWS SDK configuration (env variables), but you can also provide explicit S3 configuration:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const db = await connect("app-123", {
|
|
93
|
+
bucket: "s3qlite",
|
|
94
|
+
localDirectory: "./.s3qlite",
|
|
95
|
+
s3: {
|
|
96
|
+
region: "us-east-1",
|
|
97
|
+
credentials: {
|
|
98
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
99
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
# Operations
|
|
106
|
+
|
|
107
|
+
## .sync(): Promise<void>
|
|
108
|
+
|
|
109
|
+
Pulls changes from remote and applies them to the local database. Then it uploads local changes to the remote. This operation tries to resolve any issues that might arise during pull or push operations. Prefer using sync instead of pull and push separately, unless you have specific reasons to do so.
|
|
110
|
+
|
|
111
|
+
## .pull(): Promise<void>
|
|
112
|
+
|
|
113
|
+
Pulls changes from remote and applies them to the local database without pushing local changes back. If you have unpushed local changes, pull does a rollback-and-replay: it temporarily rolls back to your last synced state, applies the remote changes, then replays your local changes on top. This is atomic - your database stays untouched if anything goes wrong.
|
|
114
|
+
|
|
115
|
+
## .push(): Promise<void>
|
|
116
|
+
|
|
117
|
+
Uploads local changes to the remote. If the remote has any changes that are not present locally (the local database is not pulled to the latest remote state), this operation will fail - this might also happen when other instances push their changes between your pull & push operations - use sync to not worry about this.
|
|
118
|
+
|
|
119
|
+
# How does it work?
|
|
120
|
+
|
|
121
|
+
S3Qlite uses Turso CDC to store locally created changes. During sync these changes are batched and synced to/from the provided S3 bucket and applied, so multiple instances can do work at the same time on the same database with minimal S3 requests and minimal transfered data. Thanks to this approach a point in-time state of the database can be reconstructed as well as easily forking the database without additional overhead.
|
|
122
|
+
|
|
123
|
+
# Limitations
|
|
124
|
+
|
|
125
|
+
- Due to the last push wins strategy, a unwanted "corruption" of data might occur. Have in mind that the synchronization is just application of CDC rows - if two instances update the same row, the last pushed edit wins. Try to avoid autoincrement and prefer other databases for more transactional workloads. S3Qlite however does support fully transactional model - sync before & after every operation.
|
|
126
|
+
- Currently fork or checkpointing is not implemented, it is planned for future.
|
package/cjs/src/connection.js
CHANGED
|
@@ -6,6 +6,7 @@ const batches_1 = require("./batches");
|
|
|
6
6
|
const extract_1 = require("./cdc/extract");
|
|
7
7
|
const truncate_1 = require("./cdc/truncate");
|
|
8
8
|
const contexts_1 = require("./contexts");
|
|
9
|
+
const fork_1 = require("./fork");
|
|
9
10
|
const fileKV_1 = require("./kv/fileKV");
|
|
10
11
|
const s3KV_1 = require("./kv/s3KV");
|
|
11
12
|
const syncFiles_1 = require("./kv/syncFiles");
|
|
@@ -113,14 +114,20 @@ const connect = (dbName, options) => effect_1.Effect.gen(function* () {
|
|
|
113
114
|
}).pipe(effect_1.Effect.flatMap(() => effect_1.Ref.set(connectionRef, conn))),
|
|
114
115
|
};
|
|
115
116
|
const ctx = effect_1.Context.empty().pipe(effect_1.Context.add(contexts_1.ConnectionConfig, connectionConfigValue), effect_1.Context.add(contexts_1.ConnectionState, connectionStateValue), effect_1.Context.add(storage_1.LocalKV, local), effect_1.Context.add(storage_1.RemoteKV, remote));
|
|
116
|
-
const
|
|
117
|
-
wrapper
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
const mutex = yield* effect_1.Effect.makeSemaphore(1);
|
|
118
|
+
const wrapper = (0, wrapDatabase_1.wrapDatabase)(() => currentConnection, (call, method) => {
|
|
119
|
+
if (method === "iterate" || method === "transaction") {
|
|
120
|
+
return call();
|
|
121
|
+
}
|
|
122
|
+
return effect_1.Effect.runPromise(mutex.withPermits(1)(effect_1.Effect.promise(() => Promise.resolve().then(call))));
|
|
123
|
+
});
|
|
124
|
+
wrapper.pull = (() => mutex.withPermits(1)((0, pull_1.pull)().pipe(effect_1.Effect.provide(ctx), effect_1.Effect.asVoid)));
|
|
125
|
+
wrapper.push = () => mutex.withPermits(1)((0, push_1.push)().pipe(effect_1.Effect.provide(ctx), effect_1.Effect.asVoid));
|
|
126
|
+
wrapper.sync = (() => mutex.withPermits(1)(effect_1.Effect.gen(function* () {
|
|
120
127
|
yield* (0, pull_1.pull)();
|
|
121
128
|
yield* (0, push_1.push)();
|
|
122
|
-
}).pipe(effect_1.Effect.provide(ctx)));
|
|
123
|
-
wrapper.fork = () => effect_1.Effect.
|
|
129
|
+
}).pipe(effect_1.Effect.retry(effect_1.Schedule.recurs(2)), effect_1.Effect.provide(ctx))));
|
|
130
|
+
wrapper.fork = (nextDbName) => mutex.withPermits(1)((0, fork_1.fork)(nextDbName).pipe(effect_1.Effect.provide(ctx)));
|
|
124
131
|
wrapper.checkpoint = () => effect_1.Effect.die(new Error("Not implemented"));
|
|
125
132
|
return wrapper;
|
|
126
133
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/connection.ts"],"names":[],"mappings":";;;AAAA,mCAAsD;AAEtD,uCAAuC;AACvC,2CAAkD;AAClD,6CAA0C;AAC1C,yCAA+D;AAC/D,wCAAyC;AACzC,oCAAqC;AACrC,8CAA2C;AAC3C,iCAA8B;AAC9B,iCAA8B;AAC9B,uCAUmB;AAEnB,iDAA8C;AAEvC,MAAM,kBAAkB,GAAG,CAAC,iBAAoC,EAAE,EAAE,CAC1E,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAA,eAAQ,EAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,IAAA,mBAAU,EAAC,iBAAiB,CAAC,cAAc,IAAI,aAAa,CAAC,CAAC;IACpF,OAAO,gBAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,gBAAO,CAAC,GAAG,CAAC,kBAAQ,EAAE,IAAI,CAAC,EAAE,gBAAO,CAAC,GAAG,CAAC,iBAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACxF,CAAC,CAAC,CAAC;AALS,QAAA,kBAAkB,sBAK3B;AAEG,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,OAA0B,EAAE,EAAE,CACrE,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,kBAAQ,CAAC;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,iBAAO,CAAC;IAE7B,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC,eAAM,CAAC,GAAG,CAAC;QACnD,SAAS,EAAE,IAAA,iBAAO,EAAa,KAAK,EAAE,IAAA,iBAAO,EAAC,MAAM,CAAC,CAAC;QACtD,UAAU,EAAE,IAAA,iBAAO,EAAO,MAAM,EAAE,IAAA,iBAAO,EAAC,MAAM,CAAC,CAAC;QAClD,qCAAqC;KACrC,CAAC,CAAC;IAEH,IAAI,eAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAEvC,yDAAyD;QACzD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAA,eAAK,EAAC,MAAM,CAAC,CAAC,CAAC;QACvD,KAAK,CAAC,CAAC,IAAA,mBAAQ,EAAC,UAAU,CAAC,CAAC;QAC5B,KAAK,CAAC,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;QAEhD,MAAM,IAAI,GAAS;YAClB,SAAS,EAAE;gBACV;oBACC,EAAE,EAAE,UAAU;oBACd,eAAe,EAAE,EAAE;iBACnB;aACD;YACD,OAAO,EAAE,EAAE;SACX,CAAC;QAEF,gGAAgG;QAChG,MAAM,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,CAAC,eAAM,CAAC,GAAG,CAAC;YAC1C,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,IAAA,iBAAO,EAAC,MAAM,CAAC,EAAE,IAAA,oBAAU,EAAC,IAAI,CAAC,CAAC;YAC3D,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,IAAA,eAAK,EAAC,MAAM,CAAC,EAAE,IAAA,qBAAW,EAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACjE,eAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,IAAA,qBAAW,EAAC,UAAU,CAAC,CAAC,CAAC,EAC/D,eAAM,CAAC,OAAO,CACb,eAAM,CAAC,KAAK,CAAC;gBACZ,MAAM,EAAE,GAAG,EAAE,CAAC,eAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;gBACrE,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,IAAA,qBAAW,EAAC,UAAU,CAAC,EAAE,MAAM,CAAC;aACvE,CAAC,CACF,CACD;SACD,CAAC,CAAC;QAEH,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CACf,IAAA,iBAAO,EAAC,MAAM,CAAC,EACf,IAAA,oBAAU,EAAa;YACtB,IAAI;YACJ,UAAU,EAAE,YAAY,CAAC,IAAI;YAC7B,uBAAuB,EAAE,CAAC;SAC1B,CAAC,CACF,CAAC;IACH,CAAC;IAED,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,eAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7C,IAAI,CAAC,cAAc,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,kBAAkB,GAAG,cAAc,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,eAAe,GAAG,kBAAkB;YACzC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,kBAAkB,CAAC,GAAG,CAAC;YAChE,CAAC,CAAC,CAAC,CAAC;QACL,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAE9D,KAAK,CAAC,CAAC,IAAA,qBAAS,EAAC;YAChB,IAAA,qBAAW,EAAC,cAAc,CAAC,EAAE,CAAC;YAC9B,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,kBAAQ,EAAC,KAAK,CAAC,EAAE,CAAC,CAAC;SACvD,CAAC,CAAC;QAEH,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAA,qBAAW,EAAC,cAAc,CAAC,EAAE,CAAC,EAAE,IAAA,eAAK,EAAC,MAAM,CAAC,CAAC,CAAC;QAElE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAA,eAAK,EAAC,MAAM,CAAC,CAAC,CAAC;QAC/C,KAAK,MAAM,KAAK,IAAI,iBAAiB,EAAE,CAAC;YACvC,KAAK,CAAC,CAAC,IAAA,oBAAU,EAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,uBAAuB,GAAG,KAAK,CAAC,CAAC,IAAA,2BAAiB,EAAC,EAAE,CAAC,CAAC;QAC7D,KAAK,CAAC,CAAC,IAAA,mBAAQ,EAAC,EAAE,CAAC,CAAC;QACpB,KAAK,CAAC,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;QAExC,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACjD,IAAI,WAAW,EAAE,CAAC;YACjB,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAA,eAAK,EAAC,MAAM,CAAC,EAAE,IAAA,iBAAO,EAAC,cAAc,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CACf,IAAA,iBAAO,EAAC,MAAM,CAAC,EACf,IAAA,oBAAU,EAAa;YACtB,IAAI;YACJ,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI;YACjC,uBAAuB;SACvB,CAAC,CACF,CAAC;IACH,CAAC;IAED,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,eAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,GAAG,CACvB,IAAI,KAAK,CACR,0EAA0E,CAC1E,CACD,CAAC;IACH,CAAC;IAED,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,eAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3D,cAAc;IACf,CAAC;IAED,MAAM,qBAAqB,GAAG;QAC7B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,QAAQ,EAAE,IAAA,eAAK,EAAC,MAAM,CAAC;QACvB,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,aAAa;QACvD,aAAa,EAAE,IAAA,iBAAO,EAAC,MAAM,CAAC;QAC9B,OAAO;KACP,CAAC;IAEF,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAA,eAAK,EAAC,MAAM,CAAC,CAAC,CAAC;IAEvD,IAAI,iBAAiB,GAAG,UAAU,CAAC;IAEnC,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,YAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAElD,MAAM,oBAAoB,GAAG;QAC5B,aAAa,EAAE,YAAG,CAAC,GAAG,CAAC,aAAa,CAAC;QACrC,aAAa,EAAE,CAAC,IAAuB,EAAE,EAAE,CAC1C,eAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAChB,iBAAiB,GAAG,IAAI,CAAC;QAC1B,CAAC,CAAC,CAAC,IAAI,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAG,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;KAC5D,CAAC;IAEF,MAAM,GAAG,GAAG,gBAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAC/B,gBAAO,CAAC,GAAG,CAAC,2BAAgB,EAAE,qBAAqB,CAAC,EACpD,gBAAO,CAAC,GAAG,CAAC,0BAAe,EAAE,oBAAoB,CAAC,EAClD,gBAAO,CAAC,GAAG,CAAC,iBAAO,EAAE,KAAK,CAAC,EAC3B,gBAAO,CAAC,GAAG,CAAC,kBAAQ,EAAE,MAAM,CAAC,CAC7B,CAAC;IAEF,MAAM,OAAO,GAAG,IAAA,2BAAY,EAC3B,GAAG,EAAE,CAAC,iBAAiB,EACvB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CACc,CAAC;IAEhC,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,CACpB,IAAA,WAAI,GAAE,CAAC,IAAI,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,eAAM,CAAC,MAAM,CAAC,CAAwB,CAAC;IACzE,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,IAAA,WAAI,GAAE,CAAC,IAAI,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,eAAM,CAAC,MAAM,CAAC,CAAC;IACrE,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,CACpB,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,KAAK,CAAC,CAAC,IAAA,WAAI,GAAE,CAAC;QACd,KAAK,CAAC,CAAC,IAAA,WAAI,GAAE,CAAC;IACf,CAAC,CAAC,CAAC,IAAI,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAwB,CAAC;IACtD,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,eAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,eAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAEpE,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC,CAAC;AA5JS,QAAA,OAAO,WA4JhB","sourcesContent":["import { Context, Effect, Option, Ref } from \"effect\";\n\nimport { applyBatch } from \"./batches\";\nimport { getLatestChangeId } from \"./cdc/extract\";\nimport { truncate } from \"./cdc/truncate\";\nimport { ConnectionConfig, ConnectionState } from \"./contexts\";\nimport { makeFileKV } from \"./kv/fileKV\";\nimport { makeS3KV } from \"./kv/s3KV\";\nimport { pullFiles } from \"./kv/syncFiles\";\nimport { pull } from \"./pull\";\nimport { push } from \"./push\";\nimport {\n\tLocalKV,\n\tRemoteKV,\n\tbaseKey,\n\tbatchKey,\n\tdbKey,\n\tencodeJson,\n\tgetJson,\n\theadKey,\n\tsnapshotKey,\n} from \"./storage\";\nimport type { ConnectionOptions, Head, S3qliteDatabase, StoredHead } from \"./types\";\nimport { wrapDatabase } from \"./wrapDatabase\";\n\nexport const initializeContexts = (connectionOptions: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst s3kv = yield* makeS3KV(connectionOptions.bucket);\n\t\tconst filekv = yield* makeFileKV(connectionOptions.localDirectory ?? \"./.s3qlite/\");\n\t\treturn Context.empty().pipe(Context.add(RemoteKV, s3kv), Context.add(LocalKV, filekv));\n\t});\n\nexport const connect = (dbName: string, options: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst remote = yield* RemoteKV;\n\t\tconst local = yield* LocalKV;\n\n\t\tconst { localHead, remoteHead } = yield* Effect.all({\n\t\t\tlocalHead: getJson<StoredHead>(local, headKey(dbName)),\n\t\t\tremoteHead: getJson<Head>(remote, headKey(dbName)),\n\t\t\t// localDb: local.get(dbKey(dbName)),\n\t\t});\n\n\t\tif (Option.isNone(localHead) && Option.isNone(remoteHead)) {\n\t\t\tconst snapshotId = crypto.randomUUID();\n\n\t\t\t// connection is scoped, will die when the effect is done\n\t\t\tconst connection = yield* local.connect(dbKey(dbName));\n\t\t\tyield* truncate(connection);\n\t\t\tyield* Effect.promise(() => connection.close());\n\n\t\t\tconst head: Head = {\n\t\t\t\tsnapshots: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: snapshotId,\n\t\t\t\t\t\tbatchIdsApplied: [],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tbatches: [],\n\t\t\t};\n\n\t\t\t// TODO: this should be atomic, and the head(s) probably updated only after snapshot is written.\n\t\t\tconst { remoteResult } = yield* Effect.all({\n\t\t\t\tremoteResult: remote.set(headKey(dbName), encodeJson(head)),\n\t\t\t\tsnapshot: local.clone(dbKey(dbName), snapshotKey(snapshotId)).pipe(\n\t\t\t\t\tEffect.flatMap(() => local.readStream(snapshotKey(snapshotId))),\n\t\t\t\t\tEffect.flatMap(\n\t\t\t\t\t\tOption.match({\n\t\t\t\t\t\t\tonNone: () => Effect.die(new Error(\"Snapshot not found after clone\")),\n\t\t\t\t\t\t\tonSome: (stream) => remote.writeStream(snapshotKey(snapshotId), stream),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t});\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteResult.etag,\n\t\t\t\t\tlastSyncedLocalChangeId: 0,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isNone(localHead)) {\n\t\t\tconst head = remoteHead.value.value;\n\t\t\tconst latestSnapshot = head.snapshots.at(-1);\n\n\t\t\tif (!latestSnapshot) {\n\t\t\t\treturn yield* Effect.die(new Error(\"Remote head has no snapshots\"));\n\t\t\t}\n\n\t\t\tconst lastAppliedBatchId = latestSnapshot.batchIdsApplied.at(-1);\n\t\t\tconst batchStartIndex = lastAppliedBatchId\n\t\t\t\t? head.batches.findIndex((b) => b.id === lastAppliedBatchId) + 1\n\t\t\t\t: 0;\n\t\t\tconst batchesToDownload = head.batches.slice(batchStartIndex);\n\n\t\t\tyield* pullFiles([\n\t\t\t\tsnapshotKey(latestSnapshot.id),\n\t\t\t\t...batchesToDownload.map((batch) => batchKey(batch.id)),\n\t\t\t]);\n\n\t\t\tyield* local.clone(snapshotKey(latestSnapshot.id), dbKey(dbName));\n\n\t\t\tconst db = yield* local.connect(dbKey(dbName));\n\t\t\tfor (const batch of batchesToDownload) {\n\t\t\t\tyield* applyBatch(db, batch);\n\t\t\t}\n\t\t\tconst lastSyncedLocalChangeId = yield* getLatestChangeId(db);\n\t\t\tyield* truncate(db);\n\t\t\tyield* Effect.promise(() => db.close());\n\n\t\t\tconst lastBatchId = batchesToDownload.at(-1)?.id;\n\t\t\tif (lastBatchId) {\n\t\t\t\tyield* local.clone(dbKey(dbName), baseKey(latestSnapshot.id, lastBatchId));\n\t\t\t}\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteHead.value.etag,\n\t\t\t\t\tlastSyncedLocalChangeId,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isNone(remoteHead) && Option.isSome(localHead)) {\n\t\t\treturn yield* Effect.die(\n\t\t\t\tnew Error(\n\t\t\t\t\t\"Local head exists but remote head does not. This is an unexpected state.\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isSome(localHead)) {\n\t\t\t// do nothing?\n\t\t}\n\n\t\tconst connectionConfigValue = {\n\t\t\tbucket: options.bucket,\n\t\t\tdbName,\n\t\t\tlivePath: dbKey(dbName),\n\t\t\tlocalDirectory: options.localDirectory ?? \"./.s3qlite/\",\n\t\t\tlocalHeadPath: headKey(dbName),\n\t\t\toptions,\n\t\t};\n\n\t\tconst connection = yield* local.connect(dbKey(dbName));\n\n\t\tlet currentConnection = connection;\n\n\t\tconst connectionRef = yield* Ref.make(connection);\n\n\t\tconst connectionStateValue = {\n\t\t\tgetConnection: Ref.get(connectionRef),\n\t\t\tsetConnection: (conn: typeof connection) =>\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tcurrentConnection = conn;\n\t\t\t\t}).pipe(Effect.flatMap(() => Ref.set(connectionRef, conn))),\n\t\t};\n\n\t\tconst ctx = Context.empty().pipe(\n\t\t\tContext.add(ConnectionConfig, connectionConfigValue),\n\t\t\tContext.add(ConnectionState, connectionStateValue),\n\t\t\tContext.add(LocalKV, local),\n\t\t\tContext.add(RemoteKV, remote),\n\t\t);\n\n\t\tconst wrapper = wrapDatabase(\n\t\t\t() => currentConnection,\n\t\t\t(call) => call(),\n\t\t) as unknown as S3qliteDatabase;\n\n\t\twrapper.pull = (() =>\n\t\t\tpull().pipe(Effect.provide(ctx), Effect.asVoid)) as typeof wrapper.pull;\n\t\twrapper.push = () => push().pipe(Effect.provide(ctx), Effect.asVoid);\n\t\twrapper.sync = (() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tyield* pull();\n\t\t\t\tyield* push();\n\t\t\t}).pipe(Effect.provide(ctx))) as typeof wrapper.sync;\n\t\twrapper.fork = () => Effect.die(new Error(\"Not implemented\"));\n\t\twrapper.checkpoint = () => Effect.die(new Error(\"Not implemented\"));\n\n\t\treturn wrapper;\n\t});"]}
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { Context, Effect, Option, Ref, Schedule } from \"effect\";\n\nimport { applyBatch } from \"./batches\";\nimport { getLatestChangeId } from \"./cdc/extract\";\nimport { truncate } from \"./cdc/truncate\";\nimport { ConnectionConfig, ConnectionState } from \"./contexts\";\nimport { fork } from \"./fork\";\nimport { makeFileKV } from \"./kv/fileKV\";\nimport { makeS3KV } from \"./kv/s3KV\";\nimport { pullFiles } from \"./kv/syncFiles\";\nimport { pull } from \"./pull\";\nimport { push } from \"./push\";\nimport {\n\tLocalKV,\n\tRemoteKV,\n\tbaseKey,\n\tbatchKey,\n\tdbKey,\n\tencodeJson,\n\tgetJson,\n\theadKey,\n\tsnapshotKey,\n} from \"./storage\";\nimport type { ConnectionOptions, Head, S3qliteDatabase, StoredHead } from \"./types\";\nimport { wrapDatabase } from \"./wrapDatabase\";\n\nexport const initializeContexts = (connectionOptions: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst s3kv = yield* makeS3KV(connectionOptions.bucket);\n\t\tconst filekv = yield* makeFileKV(connectionOptions.localDirectory ?? \"./.s3qlite/\");\n\t\treturn Context.empty().pipe(Context.add(RemoteKV, s3kv), Context.add(LocalKV, filekv));\n\t});\n\nexport const connect = (dbName: string, options: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst remote = yield* RemoteKV;\n\t\tconst local = yield* LocalKV;\n\n\t\tconst { localHead, remoteHead } = yield* Effect.all({\n\t\t\tlocalHead: getJson<StoredHead>(local, headKey(dbName)),\n\t\t\tremoteHead: getJson<Head>(remote, headKey(dbName)),\n\t\t\t// localDb: local.get(dbKey(dbName)),\n\t\t});\n\n\t\tif (Option.isNone(localHead) && Option.isNone(remoteHead)) {\n\t\t\tconst snapshotId = crypto.randomUUID();\n\n\t\t\t// connection is scoped, will die when the effect is done\n\t\t\tconst connection = yield* local.connect(dbKey(dbName));\n\t\t\tyield* truncate(connection);\n\t\t\tyield* Effect.promise(() => connection.close());\n\n\t\t\tconst head: Head = {\n\t\t\t\tsnapshots: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: snapshotId,\n\t\t\t\t\t\tbatchIdsApplied: [],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tbatches: [],\n\t\t\t};\n\n\t\t\t// TODO: this should be atomic, and the head(s) probably updated only after snapshot is written.\n\t\t\tconst { remoteResult } = yield* Effect.all({\n\t\t\t\tremoteResult: remote.set(headKey(dbName), encodeJson(head)),\n\t\t\t\tsnapshot: local.clone(dbKey(dbName), snapshotKey(snapshotId)).pipe(\n\t\t\t\t\tEffect.flatMap(() => local.readStream(snapshotKey(snapshotId))),\n\t\t\t\t\tEffect.flatMap(\n\t\t\t\t\t\tOption.match({\n\t\t\t\t\t\t\tonNone: () => Effect.die(new Error(\"Snapshot not found after clone\")),\n\t\t\t\t\t\t\tonSome: (stream) => remote.writeStream(snapshotKey(snapshotId), stream),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t});\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteResult.etag,\n\t\t\t\t\tlastSyncedLocalChangeId: 0,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isNone(localHead)) {\n\t\t\tconst head = remoteHead.value.value;\n\t\t\tconst latestSnapshot = head.snapshots.at(-1);\n\n\t\t\tif (!latestSnapshot) {\n\t\t\t\treturn yield* Effect.die(new Error(\"Remote head has no snapshots\"));\n\t\t\t}\n\n\t\t\tconst lastAppliedBatchId = latestSnapshot.batchIdsApplied.at(-1);\n\t\t\tconst batchStartIndex = lastAppliedBatchId\n\t\t\t\t? head.batches.findIndex((b) => b.id === lastAppliedBatchId) + 1\n\t\t\t\t: 0;\n\t\t\tconst batchesToDownload = head.batches.slice(batchStartIndex);\n\n\t\t\tyield* pullFiles([\n\t\t\t\tsnapshotKey(latestSnapshot.id),\n\t\t\t\t...batchesToDownload.map((batch) => batchKey(batch.id)),\n\t\t\t]);\n\n\t\t\tyield* local.clone(snapshotKey(latestSnapshot.id), dbKey(dbName));\n\n\t\t\tconst db = yield* local.connect(dbKey(dbName));\n\t\t\tfor (const batch of batchesToDownload) {\n\t\t\t\tyield* applyBatch(db, batch);\n\t\t\t}\n\t\t\tconst lastSyncedLocalChangeId = yield* getLatestChangeId(db);\n\t\t\tyield* truncate(db);\n\t\t\tyield* Effect.promise(() => db.close());\n\n\t\t\tconst lastBatchId = batchesToDownload.at(-1)?.id;\n\t\t\tif (lastBatchId) {\n\t\t\t\tyield* local.clone(dbKey(dbName), baseKey(latestSnapshot.id, lastBatchId));\n\t\t\t}\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteHead.value.etag,\n\t\t\t\t\tlastSyncedLocalChangeId,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isNone(remoteHead) && Option.isSome(localHead)) {\n\t\t\treturn yield* Effect.die(\n\t\t\t\tnew Error(\n\t\t\t\t\t\"Local head exists but remote head does not. This is an unexpected state.\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isSome(localHead)) {\n\t\t\t// do nothing?\n\t\t}\n\n\t\tconst connectionConfigValue = {\n\t\t\tbucket: options.bucket,\n\t\t\tdbName,\n\t\t\tlivePath: dbKey(dbName),\n\t\t\tlocalDirectory: options.localDirectory ?? \"./.s3qlite/\",\n\t\t\tlocalHeadPath: headKey(dbName),\n\t\t\toptions,\n\t\t};\n\n\t\tconst connection = yield* local.connect(dbKey(dbName));\n\n\t\tlet currentConnection = connection;\n\n\t\tconst connectionRef = yield* Ref.make(connection);\n\n\t\tconst connectionStateValue = {\n\t\t\tgetConnection: Ref.get(connectionRef),\n\t\t\tsetConnection: (conn: typeof connection) =>\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tcurrentConnection = conn;\n\t\t\t\t}).pipe(Effect.flatMap(() => Ref.set(connectionRef, conn))),\n\t\t};\n\n\t\tconst ctx = Context.empty().pipe(\n\t\t\tContext.add(ConnectionConfig, connectionConfigValue),\n\t\t\tContext.add(ConnectionState, connectionStateValue),\n\t\t\tContext.add(LocalKV, local),\n\t\t\tContext.add(RemoteKV, remote),\n\t\t);\n\n\t\tconst mutex = yield* Effect.makeSemaphore(1);\n\n\t\tconst wrapper = wrapDatabase(\n\t\t\t() => currentConnection,\n\t\t\t(call, method) => {\n\t\t\t\tif (method === \"iterate\" || method === \"transaction\") {\n\t\t\t\t\treturn call();\n\t\t\t\t}\n\n\t\t\t\treturn Effect.runPromise(\n\t\t\t\t\tmutex.withPermits(1)(\n\t\t\t\t\t\tEffect.promise(() =>\n\t\t\t\t\t\t\tPromise.resolve().then(call as () => Promise<unknown>),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t) as never;\n\t\t\t},\n\t\t) as unknown as S3qliteDatabase;\n\n\t\twrapper.pull = (() =>\n\t\t\tmutex.withPermits(1)(\n\t\t\t\tpull().pipe(Effect.provide(ctx), Effect.asVoid),\n\t\t\t)) as typeof wrapper.pull;\n\t\twrapper.push = () => mutex.withPermits(1)(push().pipe(Effect.provide(ctx), Effect.asVoid));\n\t\twrapper.sync = (() =>\n\t\t\tmutex.withPermits(1)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* pull();\n\t\t\t\t\tyield* push();\n\t\t\t\t}).pipe(Effect.retry(Schedule.recurs(2)), Effect.provide(ctx)),\n\t\t\t)) as typeof wrapper.sync;\n\t\twrapper.fork = (nextDbName) =>\n\t\t\tmutex.withPermits(1)(fork(nextDbName).pipe(Effect.provide(ctx)));\n\t\twrapper.checkpoint = () => Effect.die(new Error(\"Not implemented\"));\n\n\t\treturn wrapper;\n\t});"]}
|
package/cjs/src/fork.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fork = exports.SourceDoesNotExistError = exports.AlreadyExistsError = exports.SameNameError = void 0;
|
|
4
|
+
const effect_1 = require("effect");
|
|
5
|
+
const contexts_1 = require("./contexts");
|
|
6
|
+
const storage_1 = require("./storage");
|
|
7
|
+
class SameNameError extends effect_1.Data.TaggedError("SameNameError") {
|
|
8
|
+
}
|
|
9
|
+
exports.SameNameError = SameNameError;
|
|
10
|
+
class AlreadyExistsError extends effect_1.Data.TaggedError("AlreadyExistsError") {
|
|
11
|
+
}
|
|
12
|
+
exports.AlreadyExistsError = AlreadyExistsError;
|
|
13
|
+
class SourceDoesNotExistError extends effect_1.Data.TaggedError("SourceDoesNotExistError") {
|
|
14
|
+
}
|
|
15
|
+
exports.SourceDoesNotExistError = SourceDoesNotExistError;
|
|
16
|
+
const fork = (nextDbName) => effect_1.Effect.gen(function* () {
|
|
17
|
+
const remoteKV = yield* storage_1.RemoteKV;
|
|
18
|
+
const { dbName } = yield* contexts_1.ConnectionConfig;
|
|
19
|
+
if (nextDbName === dbName) {
|
|
20
|
+
return yield* effect_1.Effect.fail(new SameNameError({
|
|
21
|
+
dbName,
|
|
22
|
+
message: `Cannot fork database "${dbName}" into itself`,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
const existingFork = yield* remoteKV.get((0, storage_1.headKey)(nextDbName));
|
|
26
|
+
if (effect_1.Option.isSome(existingFork)) {
|
|
27
|
+
return yield* effect_1.Effect.fail(new AlreadyExistsError({
|
|
28
|
+
dbName: nextDbName,
|
|
29
|
+
message: `Database "${nextDbName}" already exists`,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
const sourceHead = yield* remoteKV.get((0, storage_1.headKey)(dbName));
|
|
33
|
+
if (effect_1.Option.isNone(sourceHead)) {
|
|
34
|
+
return yield* effect_1.Effect.fail(new SourceDoesNotExistError({
|
|
35
|
+
dbName,
|
|
36
|
+
message: `Source database "${dbName}" does not exist`,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
yield* remoteKV.set((0, storage_1.headKey)(nextDbName), sourceHead.value.value);
|
|
40
|
+
});
|
|
41
|
+
exports.fork = fork;
|
|
42
|
+
//# sourceMappingURL=fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.js","sourceRoot":"","sources":["../../../src/fork.ts"],"names":[],"mappings":";;;AAAA,mCAA8C;AAE9C,yCAA8C;AAC9C,uCAA8C;AAE9C,MAAa,aAAc,SAAQ,aAAI,CAAC,WAAW,CAAC,eAAe,CAGjE;CAAG;AAHL,sCAGK;AAEL,MAAa,kBAAmB,SAAQ,aAAI,CAAC,WAAW,CAAC,oBAAoB,CAG3E;CAAG;AAHL,gDAGK;AAEL,MAAa,uBAAwB,SAAQ,aAAI,CAAC,WAAW,CAAC,yBAAyB,CAGrF;CAAG;AAHL,0DAGK;AAIE,MAAM,IAAI,GAAG,CACnB,UAAkB,EAC4C,EAAE,CAChE,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,kBAAQ,CAAC;IACjC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,2BAAgB,CAAC;IAE3C,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,IAAI,CACxB,IAAI,aAAa,CAAC;YACjB,MAAM;YACN,OAAO,EAAE,yBAAyB,MAAM,eAAe;SACvD,CAAC,CACF,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAA,iBAAO,EAAC,UAAU,CAAC,CAAC,CAAC;IAC9D,IAAI,eAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,IAAI,CACxB,IAAI,kBAAkB,CAAC;YACtB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,aAAa,UAAU,kBAAkB;SAClD,CAAC,CACF,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAA,iBAAO,EAAC,MAAM,CAAC,CAAC,CAAC;IACxD,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,IAAI,CACxB,IAAI,uBAAuB,CAAC;YAC3B,MAAM;YACN,OAAO,EAAE,oBAAoB,MAAM,kBAAkB;SACrD,CAAC,CACF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAA,iBAAO,EAAC,UAAU,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AArCS,QAAA,IAAI,QAqCb","sourcesContent":["import { Data, Effect, Option } from \"effect\";\n\nimport { ConnectionConfig } from \"./contexts\";\nimport { RemoteKV, headKey } from \"./storage\";\n\nexport class SameNameError extends Data.TaggedError(\"SameNameError\")<{\n\treadonly dbName: string;\n\treadonly message: string;\n}> {}\n\nexport class AlreadyExistsError extends Data.TaggedError(\"AlreadyExistsError\")<{\n\treadonly dbName: string;\n\treadonly message: string;\n}> {}\n\nexport class SourceDoesNotExistError extends Data.TaggedError(\"SourceDoesNotExistError\")<{\n\treadonly dbName: string;\n\treadonly message: string;\n}> {}\n\nexport type ForkError = SameNameError | AlreadyExistsError | SourceDoesNotExistError;\n\nexport const fork = (\n\tnextDbName: string,\n): Effect.Effect<void, ForkError, ConnectionConfig | RemoteKV> =>\n\tEffect.gen(function* () {\n\t\tconst remoteKV = yield* RemoteKV;\n\t\tconst { dbName } = yield* ConnectionConfig;\n\n\t\tif (nextDbName === dbName) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew SameNameError({\n\t\t\t\t\tdbName,\n\t\t\t\t\tmessage: `Cannot fork database \"${dbName}\" into itself`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst existingFork = yield* remoteKV.get(headKey(nextDbName));\n\t\tif (Option.isSome(existingFork)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew AlreadyExistsError({\n\t\t\t\t\tdbName: nextDbName,\n\t\t\t\t\tmessage: `Database \"${nextDbName}\" already exists`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst sourceHead = yield* remoteKV.get(headKey(dbName));\n\t\tif (Option.isNone(sourceHead)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew SourceDoesNotExistError({\n\t\t\t\t\tdbName,\n\t\t\t\t\tmessage: `Source database \"${dbName}\" does not exist`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tyield* remoteKV.set(headKey(nextDbName), sourceHead.value.value);\n\t});\n"]}
|
package/cjs/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ Object.defineProperty(exports, "initContexts", { enumerable: true, get: function
|
|
|
21
21
|
__exportStar(require("./cdc/apply"), exports);
|
|
22
22
|
__exportStar(require("./cdc/extract"), exports);
|
|
23
23
|
__exportStar(require("./cdc/types"), exports);
|
|
24
|
+
__exportStar(require("./fork"), exports);
|
|
24
25
|
__exportStar(require("./kv/fileKV"), exports);
|
|
25
26
|
__exportStar(require("./kv/kv"), exports);
|
|
26
27
|
__exportStar(require("./kv/memoryKV"), exports);
|
package/cjs/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mCAAgC;AAEhC,6CAA8E;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mCAAgC;AAEhC,6CAA8E;AAY/C,6FAZM,+BAAkB,OAYZ;AAT3C,8CAA4B;AAC5B,gDAA8B;AAC9B,8CAA4B;AAC5B,yCAAuB;AACvB,8CAA4B;AAC5B,0CAAwB;AACxB,gDAA8B;AAC9B,4CAA0B;AAInB,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,iBAAoC,EAAE,EAAE,CAC/E,IAAA,+BAAkB,EAAC,iBAAiB,CAAC,CAAC,IAAI,CACzC,eAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACtB,IAAA,oBAAe,EAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,IAAI,CAAC,eAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CACpE,CACD,CAAC;AALU,QAAA,OAAO,WAKjB","sourcesContent":["import { Effect } from \"effect\";\n\nimport { connect as connectInternal, initializeContexts } from \"./connection\";\nimport type { ConnectionOptions } from \"./types\";\n\nexport * from \"./cdc/apply\";\nexport * from \"./cdc/extract\";\nexport * from \"./cdc/types\";\nexport * from \"./fork\";\nexport * from \"./kv/fileKV\";\nexport * from \"./kv/kv\";\nexport * from \"./kv/memoryKV\";\nexport * from \"./kv/s3KV\";\n\nexport { initializeContexts as initContexts };\n\nexport const connect = (dbName: string, connectionOptions: ConnectionOptions) =>\n\tinitializeContexts(connectionOptions).pipe(\n\t\tEffect.flatMap((ctx) =>\n\t\t\tconnectInternal(dbName, connectionOptions).pipe(Effect.provide(ctx)),\n\t\t),\n\t);"]}
|
package/cjs/src/push.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/push.ts"],"names":[],"mappings":";;;AAAA,mCAAwC;AAExC,uCAAyC;AACzC,2CAAkD;AAClD,6CAA0C;AAC1C,yCAA+D;
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/push.ts"],"names":[],"mappings":"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","sourcesContent":["import { Effect, Option } from \"effect\";\n\nimport { extractBatch } from \"./batches\";\nimport { getLatestChangeId } from \"./cdc/extract\";\nimport { truncate } from \"./cdc/truncate\";\nimport { ConnectionConfig, ConnectionState } from \"./contexts\";\nimport type { ConflictError } from \"./kv/kv\";\nimport { pushFiles } from \"./kv/syncFiles\";\nimport {\n\tLocalKV,\n\tRemoteKV,\n\tbaseKey,\n\tbatchKey,\n\tdbKey,\n\tencodeJson,\n\tgetJson,\n\theadKey,\n} from \"./storage\";\nimport type { Batch, Head, StoredHead } from \"./types\";\n\nexport const push = (): Effect.Effect<\n\tvoid,\n\tConflictError,\n\tConnectionConfig | ConnectionState | LocalKV | RemoteKV\n> =>\n\tEffect.gen(function* () {\n\t\tconst localKV = yield* LocalKV;\n\t\tconst remoteKV = yield* RemoteKV;\n\t\tconst { dbName } = yield* ConnectionConfig;\n\t\tconst state = yield* ConnectionState;\n\t\tconst tursoConnection = { current: yield* state.getConnection };\n\n\t\tconst localHead = yield* getJson<StoredHead>(localKV, headKey(dbName)).pipe(\n\t\t\tEffect.orDie,\n\t\t\tEffect.flatMap(\n\t\t\t\tOption.match({\n\t\t\t\t\tonNone: () => Effect.die(new Error(\"Expected local stored head to exist\")),\n\t\t\t\t\tonSome: Effect.succeed,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\n\t\tconst pendingBatches: { batch: Batch; lastChangeId: number }[] = [];\n\n\t\tif (localHead.value.pendingBatches?.length) {\n\t\t\tpendingBatches.push(\n\t\t\t\t...localHead.value.pendingBatches.map((pb) => ({\n\t\t\t\t\tbatch: { id: pb.id },\n\t\t\t\t\tlastChangeId: pb.lastChangeId,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\n\t\tconst extracted = yield* extractBatch(\n\t\t\ttursoConnection.current,\n\t\t\tlocalHead.value.lastSyncedLocalChangeId,\n\t\t);\n\n\t\tif (Option.isSome(extracted)) {\n\t\t\tpendingBatches.push({\n\t\t\t\tbatch: extracted.value.batch,\n\t\t\t\tlastChangeId: extracted.value.lastLocalChangeId,\n\t\t\t});\n\t\t}\n\n\t\tif (pendingBatches.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tyield* pushFiles(pendingBatches.map((pb) => batchKey(pb.batch.id))).pipe(Effect.orDie);\n\n\t\tconst newHead: Head = {\n\t\t\tsnapshots: localHead.value.head.snapshots,\n\t\t\tbatches: localHead.value.head.batches.concat(pendingBatches.map((pb) => pb.batch)),\n\t\t};\n\n\t\tconst result = yield* remoteKV.cas(\n\t\t\theadKey(dbName),\n\t\t\tencodeJson(newHead),\n\t\t\tlocalHead.value.remoteEtag,\n\t\t);\n\n\t\tconst lastSyncedLocalChangeId = yield* getLatestChangeId(tursoConnection.current).pipe(\n\t\t\tEffect.orDie,\n\t\t);\n\n\t\tyield* localKV.set(\n\t\t\theadKey(dbName),\n\t\t\tencodeJson<StoredHead>({\n\t\t\t\thead: newHead,\n\t\t\t\tremoteEtag: result.etag,\n\t\t\t\tlastSyncedLocalChangeId,\n\t\t\t}),\n\t\t);\n\n\t\tconst lastSnapshotId = newHead.snapshots.at(-1)?.id;\n\t\tconst lastBatchId = newHead.batches.at(-1)?.id;\n\t\tif (lastSnapshotId && lastBatchId) {\n\t\t\tyield* truncate(tursoConnection.current);\n\t\t\tyield* localKV.clone(dbKey(dbName), baseKey(lastSnapshotId, lastBatchId));\n\t\t}\n\t});"]}
|
package/cjs/src/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Database } from \"@tursodatabase/database\";\nimport type { Effect } from \"effect\";\n\nexport type Snapshot = {\n\tid: string;\n\n\t// thanks to this we can do full history\n\tbaseSnapshotId?: string;\n\tbatchIdsApplied: string[];\n};\n\nexport type Batch = {\n\tid: string;\n};\n\nexport type Head = {\n\tsnapshots: Snapshot[];\n\tbatches: Batch[];\n};\n\nexport type PendingBatch = {\n\tid: string;\n\tlastChangeId: number;\n};\n\nexport type StoredHead = {\n\thead: Head;\n\n\t/**\n\t * The latest etag of the remote head this instance has been synced to.\n\t */\n\tremoteEtag: string;\n\n\tlastSyncedLocalChangeId: number;\n\n\tpendingBatches?: PendingBatch[];\n};\n\n// export type ConflictResolutionMode = \"fail\" | \"preferRemote\" | \"preferLocal\";\n//\n// export type ConflictOperation = \"pull\" | \"push\" | \"sync\";\n//\n// export type ConflictResolutionContext = {\n// \treadonly cause: Error;\n// \treadonly localHead: StoredHead;\n// \treadonly operation: ConflictOperation;\n// \treadonly remoteHead: Head;\n// };\n//\n// export type ConflictResolver = (\n// \tcontext: ConflictResolutionContext,\n// ) => Effect.Effect<ConflictResolutionMode, Error>;\n//\n// type StrongConsistencyOptions = {\n// \tmode: \"strong\";\n// };\n//\n// type EventualConsistencyOptions = {\n// \tmode: \"eventual\";\n// \tsync?: \"manual\" | { everyMs: number };\n// };\n\nexport type ConnectionOptions = {\n\tbucket: string;\n\t// conflictResolution?: ConflictResolutionMode | ConflictResolver;\n\tlocalDirectory?: string;\n};\n// } & (StrongConsistencyOptions | EventualConsistencyOptions);\n\nexport type S3qliteDatabase = Database & {\n\tpull: () => Effect.Effect<void, Error>;\n\tpush: () => Effect.Effect<void,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Database } from \"@tursodatabase/database\";\nimport type { Effect } from \"effect\";\n\nimport type { AlreadyExistsError, SameNameError, SourceDoesNotExistError } from \"./fork\";\nimport type { ConflictError } from \"./kv/kv\";\n\nexport type Snapshot = {\n\tid: string;\n\n\t// thanks to this we can do full history\n\tbaseSnapshotId?: string;\n\tbatchIdsApplied: string[];\n};\n\nexport type Batch = {\n\tid: string;\n};\n\nexport type Head = {\n\tsnapshots: Snapshot[];\n\tbatches: Batch[];\n};\n\nexport type PendingBatch = {\n\tid: string;\n\tlastChangeId: number;\n};\n\nexport type StoredHead = {\n\thead: Head;\n\n\t/**\n\t * The latest etag of the remote head this instance has been synced to.\n\t */\n\tremoteEtag: string;\n\n\tlastSyncedLocalChangeId: number;\n\n\tpendingBatches?: PendingBatch[];\n};\n\n// export type ConflictResolutionMode = \"fail\" | \"preferRemote\" | \"preferLocal\";\n//\n// export type ConflictOperation = \"pull\" | \"push\" | \"sync\";\n//\n// export type ConflictResolutionContext = {\n// \treadonly cause: Error;\n// \treadonly localHead: StoredHead;\n// \treadonly operation: ConflictOperation;\n// \treadonly remoteHead: Head;\n// };\n//\n// export type ConflictResolver = (\n// \tcontext: ConflictResolutionContext,\n// ) => Effect.Effect<ConflictResolutionMode, Error>;\n//\n// type StrongConsistencyOptions = {\n// \tmode: \"strong\";\n// };\n//\n// type EventualConsistencyOptions = {\n// \tmode: \"eventual\";\n// \tsync?: \"manual\" | { everyMs: number };\n// };\n\nexport type ConnectionOptions = {\n\tbucket: string;\n\t// conflictResolution?: ConflictResolutionMode | ConflictResolver;\n\tlocalDirectory?: string;\n};\n// } & (StrongConsistencyOptions | EventualConsistencyOptions);\n\nexport type S3qliteDatabase = Database & {\n\tpull: () => Effect.Effect<void, Error>;\n\tpush: () => Effect.Effect<void, ConflictError>;\n\tsync: () => Effect.Effect<void, Error | ConflictError>;\n\tfork: (\n\t\tdbName: string,\n\t) => Effect.Effect<void, SameNameError | AlreadyExistsError | SourceDoesNotExistError>;\n\tcheckpoint: () => Effect.Effect<void, Error>;\n};"]}
|
package/cjs/src/wrapDatabase.js
CHANGED
|
@@ -70,9 +70,17 @@ const wrapDatabase = (getTarget, wrap) => {
|
|
|
70
70
|
return (...args) => wrapTransaction(Reflect.apply(value, getTarget(), args), wrap);
|
|
71
71
|
}
|
|
72
72
|
if (asyncDatabaseMethods.has(property)) {
|
|
73
|
-
return (...args) => wrap(() =>
|
|
73
|
+
return (...args) => wrap(() => {
|
|
74
|
+
const target = getTarget();
|
|
75
|
+
const current = Reflect.get(target, property, target);
|
|
76
|
+
return Reflect.apply(current, target, args);
|
|
77
|
+
}, property, args);
|
|
74
78
|
}
|
|
75
|
-
return (...args) =>
|
|
79
|
+
return (...args) => {
|
|
80
|
+
const target = getTarget();
|
|
81
|
+
const current = Reflect.get(target, property, target);
|
|
82
|
+
return Reflect.apply(current, target, args);
|
|
83
|
+
};
|
|
76
84
|
},
|
|
77
85
|
});
|
|
78
86
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapDatabase.js","sourceRoot":"","sources":["../../../src/wrapDatabase.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"wrapDatabase.js","sourceRoot":"","sources":["../../../src/wrapDatabase.ts"],"names":[],"mappings":"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","sourcesContent":["const asyncDatabaseMethods = new Set<PropertyKey>([\n\t\"all\",\n\t\"close\",\n\t\"exec\",\n\t\"get\",\n\t\"iterate\",\n\t\"pragma\",\n\t\"run\",\n]);\n\nconst asyncStatementMethods = new Set<PropertyKey>([\"all\", \"close\", \"get\", \"run\"]);\n\nconst syncStatementMethods = new Set<PropertyKey>([\"bind\", \"pluck\", \"raw\", \"safeIntegers\"]);\n\nconst wrapTransaction = <T extends (...args: readonly unknown[]) => unknown>(\n\ttransaction: T,\n\twrap: <R>(call: () => R, method: PropertyKey, args: readonly unknown[]) => R,\n): T =>\n\tnew Proxy(transaction, {\n\t\tapply: (target, thisArg, args) =>\n\t\t\twrap(() => Reflect.apply(target, thisArg, args), \"transaction\", args),\n\t\tget: (target, property, receiver) => {\n\t\t\tconst value = Reflect.get(target, property, receiver);\n\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\treturn wrapTransaction(value as (...args: readonly unknown[]) => unknown, wrap);\n\t\t},\n\t}) as T;\n\nconst wrapStatement = <T extends object>(\n\tstatement: T,\n\twrap: <R>(call: () => R, method: PropertyKey, args: readonly unknown[]) => R,\n): T => {\n\tlet proxy: T;\n\n\tproxy = new Proxy(statement, {\n\t\tget: (target, property, receiver) => {\n\t\t\tconst value = Reflect.get(target, property, receiver);\n\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\tif (syncStatementMethods.has(property)) {\n\t\t\t\treturn (...args: unknown[]) => {\n\t\t\t\t\tReflect.apply(value, target, args);\n\t\t\t\t\treturn proxy;\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (property === \"iterate\") {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\t(async function* () {\n\t\t\t\t\t\tconst iterable = wrap(\n\t\t\t\t\t\t\t() => Reflect.apply(value, target, args),\n\t\t\t\t\t\t\tproperty,\n\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t) as AsyncIterable<unknown>;\n\n\t\t\t\t\t\tfor await (const item of iterable) {\n\t\t\t\t\t\t\tyield item;\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t}\n\n\t\t\tif (asyncStatementMethods.has(property)) {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrap(() => Reflect.apply(value, target, args), property, args);\n\t\t\t}\n\n\t\t\treturn (...args: unknown[]) => Reflect.apply(value, target, args);\n\t\t},\n\t});\n\n\treturn proxy;\n};\n\nexport const wrapDatabase = <T extends object>(\n\tgetTarget: () => T,\n\twrap: <R>(call: () => R, method: PropertyKey, args: readonly unknown[]) => R,\n): T => {\n\treturn new Proxy({} as T, {\n\t\tget: (wrapper, property, receiver) => {\n\t\t\tif (Reflect.has(wrapper, property)) {\n\t\t\t\treturn Reflect.get(wrapper, property, receiver);\n\t\t\t}\n\n\t\t\tconst original = getTarget();\n\t\t\tconst value = Reflect.get(original, property, original);\n\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\tif (property === \"prepare\") {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrapStatement(Reflect.apply(value, getTarget(), args) as object, wrap);\n\t\t\t}\n\n\t\t\tif (property === \"transaction\") {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrapTransaction(\n\t\t\t\t\t\tReflect.apply(value, getTarget(), args) as (\n\t\t\t\t\t\t\t...args: readonly unknown[]\n\t\t\t\t\t\t) => unknown,\n\t\t\t\t\t\twrap,\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (asyncDatabaseMethods.has(property)) {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrap(\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tconst target = getTarget();\n\t\t\t\t\t\t\tconst current = Reflect.get(target, property, target) as (\n\t\t\t\t\t\t\t\t...args: readonly unknown[]\n\t\t\t\t\t\t\t) => unknown;\n\t\t\t\t\t\t\treturn Reflect.apply(current, target, args);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\targs,\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn (...args: unknown[]) => {\n\t\t\t\tconst target = getTarget();\n\t\t\t\tconst current = Reflect.get(target, property, target) as (\n\t\t\t\t\t...args: readonly unknown[]\n\t\t\t\t) => unknown;\n\t\t\t\treturn Reflect.apply(current, target, args);\n\t\t\t};\n\t\t},\n\t});\n};"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { ConnectionConfig } from "./contexts.js";
|
|
3
|
+
import { RemoteKV } from "./storage.js";
|
|
4
|
+
declare const SameNameError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
5
|
+
readonly _tag: "SameNameError";
|
|
6
|
+
} & Readonly<A>;
|
|
7
|
+
export declare class SameNameError extends SameNameError_base<{
|
|
8
|
+
readonly dbName: string;
|
|
9
|
+
readonly message: string;
|
|
10
|
+
}> {
|
|
11
|
+
}
|
|
12
|
+
declare const AlreadyExistsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
13
|
+
readonly _tag: "AlreadyExistsError";
|
|
14
|
+
} & Readonly<A>;
|
|
15
|
+
export declare class AlreadyExistsError extends AlreadyExistsError_base<{
|
|
16
|
+
readonly dbName: string;
|
|
17
|
+
readonly message: string;
|
|
18
|
+
}> {
|
|
19
|
+
}
|
|
20
|
+
declare const SourceDoesNotExistError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
21
|
+
readonly _tag: "SourceDoesNotExistError";
|
|
22
|
+
} & Readonly<A>;
|
|
23
|
+
export declare class SourceDoesNotExistError extends SourceDoesNotExistError_base<{
|
|
24
|
+
readonly dbName: string;
|
|
25
|
+
readonly message: string;
|
|
26
|
+
}> {
|
|
27
|
+
}
|
|
28
|
+
export type ForkError = SameNameError | AlreadyExistsError | SourceDoesNotExistError;
|
|
29
|
+
export declare const fork: (nextDbName: string) => Effect.Effect<void, ForkError, ConnectionConfig | RemoteKV>;
|
|
30
|
+
export {};
|
package/dts/src/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ConnectionOptions } from "./types.js";
|
|
|
4
4
|
export * from "./cdc/apply.js";
|
|
5
5
|
export * from "./cdc/extract.js";
|
|
6
6
|
export * from "./cdc/types.js";
|
|
7
|
+
export * from "./fork.js";
|
|
7
8
|
export * from "./kv/fileKV.js";
|
|
8
9
|
export * from "./kv/kv.js";
|
|
9
10
|
export * from "./kv/memoryKV.js";
|
package/dts/src/push.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { ConnectionConfig, ConnectionState } from "./contexts.js";
|
|
3
|
+
import type { ConflictError } from "./kv/kv.js";
|
|
3
4
|
import { LocalKV, RemoteKV } from "./storage.js";
|
|
4
|
-
export declare const push: () => Effect.Effect<void,
|
|
5
|
+
export declare const push: () => Effect.Effect<void, ConflictError, ConnectionConfig | ConnectionState | LocalKV | RemoteKV>;
|
package/dts/src/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Database } from "@tursodatabase/database";
|
|
2
2
|
import type { Effect } from "effect";
|
|
3
|
+
import type { AlreadyExistsError, SameNameError, SourceDoesNotExistError } from "./fork.js";
|
|
4
|
+
import type { ConflictError } from "./kv/kv.js";
|
|
3
5
|
export type Snapshot = {
|
|
4
6
|
id: string;
|
|
5
7
|
baseSnapshotId?: string;
|
|
@@ -31,8 +33,8 @@ export type ConnectionOptions = {
|
|
|
31
33
|
};
|
|
32
34
|
export type S3qliteDatabase = Database & {
|
|
33
35
|
pull: () => Effect.Effect<void, Error>;
|
|
34
|
-
push: () => Effect.Effect<void,
|
|
35
|
-
sync: () => Effect.Effect<void, Error>;
|
|
36
|
-
fork: (dbName: string) => Effect.Effect<void,
|
|
36
|
+
push: () => Effect.Effect<void, ConflictError>;
|
|
37
|
+
sync: () => Effect.Effect<void, Error | ConflictError>;
|
|
38
|
+
fork: (dbName: string) => Effect.Effect<void, SameNameError | AlreadyExistsError | SourceDoesNotExistError>;
|
|
37
39
|
checkpoint: () => Effect.Effect<void, Error>;
|
|
38
40
|
};
|
package/esm/src/connection.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Context, Effect, Option, Ref } from "effect";
|
|
1
|
+
import { Context, Effect, Option, Ref, Schedule } from "effect";
|
|
2
2
|
import { applyBatch } from "./batches.js";
|
|
3
3
|
import { getLatestChangeId } from "./cdc/extract.js";
|
|
4
4
|
import { truncate } from "./cdc/truncate.js";
|
|
5
5
|
import { ConnectionConfig, ConnectionState } from "./contexts.js";
|
|
6
|
+
import { fork } from "./fork.js";
|
|
6
7
|
import { makeFileKV } from "./kv/fileKV.js";
|
|
7
8
|
import { makeS3KV } from "./kv/s3KV.js";
|
|
8
9
|
import { pullFiles } from "./kv/syncFiles.js";
|
|
@@ -109,14 +110,20 @@ export const connect = (dbName, options) => Effect.gen(function* () {
|
|
|
109
110
|
}).pipe(Effect.flatMap(() => Ref.set(connectionRef, conn))),
|
|
110
111
|
};
|
|
111
112
|
const ctx = Context.empty().pipe(Context.add(ConnectionConfig, connectionConfigValue), Context.add(ConnectionState, connectionStateValue), Context.add(LocalKV, local), Context.add(RemoteKV, remote));
|
|
112
|
-
const
|
|
113
|
-
wrapper
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
const mutex = yield* Effect.makeSemaphore(1);
|
|
114
|
+
const wrapper = wrapDatabase(() => currentConnection, (call, method) => {
|
|
115
|
+
if (method === "iterate" || method === "transaction") {
|
|
116
|
+
return call();
|
|
117
|
+
}
|
|
118
|
+
return Effect.runPromise(mutex.withPermits(1)(Effect.promise(() => Promise.resolve().then(call))));
|
|
119
|
+
});
|
|
120
|
+
wrapper.pull = (() => mutex.withPermits(1)(pull().pipe(Effect.provide(ctx), Effect.asVoid)));
|
|
121
|
+
wrapper.push = () => mutex.withPermits(1)(push().pipe(Effect.provide(ctx), Effect.asVoid));
|
|
122
|
+
wrapper.sync = (() => mutex.withPermits(1)(Effect.gen(function* () {
|
|
116
123
|
yield* pull();
|
|
117
124
|
yield* push();
|
|
118
|
-
}).pipe(Effect.provide(ctx)));
|
|
119
|
-
wrapper.fork = () =>
|
|
125
|
+
}).pipe(Effect.retry(Schedule.recurs(2)), Effect.provide(ctx))));
|
|
126
|
+
wrapper.fork = (nextDbName) => mutex.withPermits(1)(fork(nextDbName).pipe(Effect.provide(ctx)));
|
|
120
127
|
wrapper.checkpoint = () => Effect.die(new Error("Not implemented"));
|
|
121
128
|
return wrapper;
|
|
122
129
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { Context, Effect, Option, Ref } from \"effect\";\n\nimport { applyBatch } from \"./batches\";\nimport { getLatestChangeId } from \"./cdc/extract\";\nimport { truncate } from \"./cdc/truncate\";\nimport { ConnectionConfig, ConnectionState } from \"./contexts\";\nimport { makeFileKV } from \"./kv/fileKV\";\nimport { makeS3KV } from \"./kv/s3KV\";\nimport { pullFiles } from \"./kv/syncFiles\";\nimport { pull } from \"./pull\";\nimport { push } from \"./push\";\nimport {\n\tLocalKV,\n\tRemoteKV,\n\tbaseKey,\n\tbatchKey,\n\tdbKey,\n\tencodeJson,\n\tgetJson,\n\theadKey,\n\tsnapshotKey,\n} from \"./storage\";\nimport type { ConnectionOptions, Head, S3qliteDatabase, StoredHead } from \"./types\";\nimport { wrapDatabase } from \"./wrapDatabase\";\n\nexport const initializeContexts = (connectionOptions: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst s3kv = yield* makeS3KV(connectionOptions.bucket);\n\t\tconst filekv = yield* makeFileKV(connectionOptions.localDirectory ?? \"./.s3qlite/\");\n\t\treturn Context.empty().pipe(Context.add(RemoteKV, s3kv), Context.add(LocalKV, filekv));\n\t});\n\nexport const connect = (dbName: string, options: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst remote = yield* RemoteKV;\n\t\tconst local = yield* LocalKV;\n\n\t\tconst { localHead, remoteHead } = yield* Effect.all({\n\t\t\tlocalHead: getJson<StoredHead>(local, headKey(dbName)),\n\t\t\tremoteHead: getJson<Head>(remote, headKey(dbName)),\n\t\t\t// localDb: local.get(dbKey(dbName)),\n\t\t});\n\n\t\tif (Option.isNone(localHead) && Option.isNone(remoteHead)) {\n\t\t\tconst snapshotId = crypto.randomUUID();\n\n\t\t\t// connection is scoped, will die when the effect is done\n\t\t\tconst connection = yield* local.connect(dbKey(dbName));\n\t\t\tyield* truncate(connection);\n\t\t\tyield* Effect.promise(() => connection.close());\n\n\t\t\tconst head: Head = {\n\t\t\t\tsnapshots: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: snapshotId,\n\t\t\t\t\t\tbatchIdsApplied: [],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tbatches: [],\n\t\t\t};\n\n\t\t\t// TODO: this should be atomic, and the head(s) probably updated only after snapshot is written.\n\t\t\tconst { remoteResult } = yield* Effect.all({\n\t\t\t\tremoteResult: remote.set(headKey(dbName), encodeJson(head)),\n\t\t\t\tsnapshot: local.clone(dbKey(dbName), snapshotKey(snapshotId)).pipe(\n\t\t\t\t\tEffect.flatMap(() => local.readStream(snapshotKey(snapshotId))),\n\t\t\t\t\tEffect.flatMap(\n\t\t\t\t\t\tOption.match({\n\t\t\t\t\t\t\tonNone: () => Effect.die(new Error(\"Snapshot not found after clone\")),\n\t\t\t\t\t\t\tonSome: (stream) => remote.writeStream(snapshotKey(snapshotId), stream),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t});\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteResult.etag,\n\t\t\t\t\tlastSyncedLocalChangeId: 0,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isNone(localHead)) {\n\t\t\tconst head = remoteHead.value.value;\n\t\t\tconst latestSnapshot = head.snapshots.at(-1);\n\n\t\t\tif (!latestSnapshot) {\n\t\t\t\treturn yield* Effect.die(new Error(\"Remote head has no snapshots\"));\n\t\t\t}\n\n\t\t\tconst lastAppliedBatchId = latestSnapshot.batchIdsApplied.at(-1);\n\t\t\tconst batchStartIndex = lastAppliedBatchId\n\t\t\t\t? head.batches.findIndex((b) => b.id === lastAppliedBatchId) + 1\n\t\t\t\t: 0;\n\t\t\tconst batchesToDownload = head.batches.slice(batchStartIndex);\n\n\t\t\tyield* pullFiles([\n\t\t\t\tsnapshotKey(latestSnapshot.id),\n\t\t\t\t...batchesToDownload.map((batch) => batchKey(batch.id)),\n\t\t\t]);\n\n\t\t\tyield* local.clone(snapshotKey(latestSnapshot.id), dbKey(dbName));\n\n\t\t\tconst db = yield* local.connect(dbKey(dbName));\n\t\t\tfor (const batch of batchesToDownload) {\n\t\t\t\tyield* applyBatch(db, batch);\n\t\t\t}\n\t\t\tconst lastSyncedLocalChangeId = yield* getLatestChangeId(db);\n\t\t\tyield* truncate(db);\n\t\t\tyield* Effect.promise(() => db.close());\n\n\t\t\tconst lastBatchId = batchesToDownload.at(-1)?.id;\n\t\t\tif (lastBatchId) {\n\t\t\t\tyield* local.clone(dbKey(dbName), baseKey(latestSnapshot.id, lastBatchId));\n\t\t\t}\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteHead.value.etag,\n\t\t\t\t\tlastSyncedLocalChangeId,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isNone(remoteHead) && Option.isSome(localHead)) {\n\t\t\treturn yield* Effect.die(\n\t\t\t\tnew Error(\n\t\t\t\t\t\"Local head exists but remote head does not. This is an unexpected state.\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isSome(localHead)) {\n\t\t\t// do nothing?\n\t\t}\n\n\t\tconst connectionConfigValue = {\n\t\t\tbucket: options.bucket,\n\t\t\tdbName,\n\t\t\tlivePath: dbKey(dbName),\n\t\t\tlocalDirectory: options.localDirectory ?? \"./.s3qlite/\",\n\t\t\tlocalHeadPath: headKey(dbName),\n\t\t\toptions,\n\t\t};\n\n\t\tconst connection = yield* local.connect(dbKey(dbName));\n\n\t\tlet currentConnection = connection;\n\n\t\tconst connectionRef = yield* Ref.make(connection);\n\n\t\tconst connectionStateValue = {\n\t\t\tgetConnection: Ref.get(connectionRef),\n\t\t\tsetConnection: (conn: typeof connection) =>\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tcurrentConnection = conn;\n\t\t\t\t}).pipe(Effect.flatMap(() => Ref.set(connectionRef, conn))),\n\t\t};\n\n\t\tconst ctx = Context.empty().pipe(\n\t\t\tContext.add(ConnectionConfig, connectionConfigValue),\n\t\t\tContext.add(ConnectionState, connectionStateValue),\n\t\t\tContext.add(LocalKV, local),\n\t\t\tContext.add(RemoteKV, remote),\n\t\t);\n\n\t\tconst wrapper = wrapDatabase(\n\t\t\t() => currentConnection,\n\t\t\t(call) => call(),\n\t\t) as unknown as S3qliteDatabase;\n\n\t\twrapper.pull = (() =>\n\t\t\tpull().pipe(Effect.provide(ctx), Effect.asVoid)) as typeof wrapper.pull;\n\t\twrapper.push = () => push().pipe(Effect.provide(ctx), Effect.asVoid);\n\t\twrapper.sync = (() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tyield* pull();\n\t\t\t\tyield* push();\n\t\t\t}).pipe(Effect.provide(ctx))) as typeof wrapper.sync;\n\t\twrapper.fork = () => Effect.die(new Error(\"Not implemented\"));\n\t\twrapper.checkpoint = () => Effect.die(new Error(\"Not implemented\"));\n\n\t\treturn wrapper;\n\t});"]}
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { Context, Effect, Option, Ref, Schedule } from \"effect\";\n\nimport { applyBatch } from \"./batches\";\nimport { getLatestChangeId } from \"./cdc/extract\";\nimport { truncate } from \"./cdc/truncate\";\nimport { ConnectionConfig, ConnectionState } from \"./contexts\";\nimport { fork } from \"./fork\";\nimport { makeFileKV } from \"./kv/fileKV\";\nimport { makeS3KV } from \"./kv/s3KV\";\nimport { pullFiles } from \"./kv/syncFiles\";\nimport { pull } from \"./pull\";\nimport { push } from \"./push\";\nimport {\n\tLocalKV,\n\tRemoteKV,\n\tbaseKey,\n\tbatchKey,\n\tdbKey,\n\tencodeJson,\n\tgetJson,\n\theadKey,\n\tsnapshotKey,\n} from \"./storage\";\nimport type { ConnectionOptions, Head, S3qliteDatabase, StoredHead } from \"./types\";\nimport { wrapDatabase } from \"./wrapDatabase\";\n\nexport const initializeContexts = (connectionOptions: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst s3kv = yield* makeS3KV(connectionOptions.bucket);\n\t\tconst filekv = yield* makeFileKV(connectionOptions.localDirectory ?? \"./.s3qlite/\");\n\t\treturn Context.empty().pipe(Context.add(RemoteKV, s3kv), Context.add(LocalKV, filekv));\n\t});\n\nexport const connect = (dbName: string, options: ConnectionOptions) =>\n\tEffect.gen(function* () {\n\t\tconst remote = yield* RemoteKV;\n\t\tconst local = yield* LocalKV;\n\n\t\tconst { localHead, remoteHead } = yield* Effect.all({\n\t\t\tlocalHead: getJson<StoredHead>(local, headKey(dbName)),\n\t\t\tremoteHead: getJson<Head>(remote, headKey(dbName)),\n\t\t\t// localDb: local.get(dbKey(dbName)),\n\t\t});\n\n\t\tif (Option.isNone(localHead) && Option.isNone(remoteHead)) {\n\t\t\tconst snapshotId = crypto.randomUUID();\n\n\t\t\t// connection is scoped, will die when the effect is done\n\t\t\tconst connection = yield* local.connect(dbKey(dbName));\n\t\t\tyield* truncate(connection);\n\t\t\tyield* Effect.promise(() => connection.close());\n\n\t\t\tconst head: Head = {\n\t\t\t\tsnapshots: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: snapshotId,\n\t\t\t\t\t\tbatchIdsApplied: [],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tbatches: [],\n\t\t\t};\n\n\t\t\t// TODO: this should be atomic, and the head(s) probably updated only after snapshot is written.\n\t\t\tconst { remoteResult } = yield* Effect.all({\n\t\t\t\tremoteResult: remote.set(headKey(dbName), encodeJson(head)),\n\t\t\t\tsnapshot: local.clone(dbKey(dbName), snapshotKey(snapshotId)).pipe(\n\t\t\t\t\tEffect.flatMap(() => local.readStream(snapshotKey(snapshotId))),\n\t\t\t\t\tEffect.flatMap(\n\t\t\t\t\t\tOption.match({\n\t\t\t\t\t\t\tonNone: () => Effect.die(new Error(\"Snapshot not found after clone\")),\n\t\t\t\t\t\t\tonSome: (stream) => remote.writeStream(snapshotKey(snapshotId), stream),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t});\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteResult.etag,\n\t\t\t\t\tlastSyncedLocalChangeId: 0,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isNone(localHead)) {\n\t\t\tconst head = remoteHead.value.value;\n\t\t\tconst latestSnapshot = head.snapshots.at(-1);\n\n\t\t\tif (!latestSnapshot) {\n\t\t\t\treturn yield* Effect.die(new Error(\"Remote head has no snapshots\"));\n\t\t\t}\n\n\t\t\tconst lastAppliedBatchId = latestSnapshot.batchIdsApplied.at(-1);\n\t\t\tconst batchStartIndex = lastAppliedBatchId\n\t\t\t\t? head.batches.findIndex((b) => b.id === lastAppliedBatchId) + 1\n\t\t\t\t: 0;\n\t\t\tconst batchesToDownload = head.batches.slice(batchStartIndex);\n\n\t\t\tyield* pullFiles([\n\t\t\t\tsnapshotKey(latestSnapshot.id),\n\t\t\t\t...batchesToDownload.map((batch) => batchKey(batch.id)),\n\t\t\t]);\n\n\t\t\tyield* local.clone(snapshotKey(latestSnapshot.id), dbKey(dbName));\n\n\t\t\tconst db = yield* local.connect(dbKey(dbName));\n\t\t\tfor (const batch of batchesToDownload) {\n\t\t\t\tyield* applyBatch(db, batch);\n\t\t\t}\n\t\t\tconst lastSyncedLocalChangeId = yield* getLatestChangeId(db);\n\t\t\tyield* truncate(db);\n\t\t\tyield* Effect.promise(() => db.close());\n\n\t\t\tconst lastBatchId = batchesToDownload.at(-1)?.id;\n\t\t\tif (lastBatchId) {\n\t\t\t\tyield* local.clone(dbKey(dbName), baseKey(latestSnapshot.id, lastBatchId));\n\t\t\t}\n\n\t\t\tyield* local.set(\n\t\t\t\theadKey(dbName),\n\t\t\t\tencodeJson<StoredHead>({\n\t\t\t\t\thead,\n\t\t\t\t\tremoteEtag: remoteHead.value.etag,\n\t\t\t\t\tlastSyncedLocalChangeId,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isNone(remoteHead) && Option.isSome(localHead)) {\n\t\t\treturn yield* Effect.die(\n\t\t\t\tnew Error(\n\t\t\t\t\t\"Local head exists but remote head does not. This is an unexpected state.\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (Option.isSome(remoteHead) && Option.isSome(localHead)) {\n\t\t\t// do nothing?\n\t\t}\n\n\t\tconst connectionConfigValue = {\n\t\t\tbucket: options.bucket,\n\t\t\tdbName,\n\t\t\tlivePath: dbKey(dbName),\n\t\t\tlocalDirectory: options.localDirectory ?? \"./.s3qlite/\",\n\t\t\tlocalHeadPath: headKey(dbName),\n\t\t\toptions,\n\t\t};\n\n\t\tconst connection = yield* local.connect(dbKey(dbName));\n\n\t\tlet currentConnection = connection;\n\n\t\tconst connectionRef = yield* Ref.make(connection);\n\n\t\tconst connectionStateValue = {\n\t\t\tgetConnection: Ref.get(connectionRef),\n\t\t\tsetConnection: (conn: typeof connection) =>\n\t\t\t\tEffect.sync(() => {\n\t\t\t\t\tcurrentConnection = conn;\n\t\t\t\t}).pipe(Effect.flatMap(() => Ref.set(connectionRef, conn))),\n\t\t};\n\n\t\tconst ctx = Context.empty().pipe(\n\t\t\tContext.add(ConnectionConfig, connectionConfigValue),\n\t\t\tContext.add(ConnectionState, connectionStateValue),\n\t\t\tContext.add(LocalKV, local),\n\t\t\tContext.add(RemoteKV, remote),\n\t\t);\n\n\t\tconst mutex = yield* Effect.makeSemaphore(1);\n\n\t\tconst wrapper = wrapDatabase(\n\t\t\t() => currentConnection,\n\t\t\t(call, method) => {\n\t\t\t\tif (method === \"iterate\" || method === \"transaction\") {\n\t\t\t\t\treturn call();\n\t\t\t\t}\n\n\t\t\t\treturn Effect.runPromise(\n\t\t\t\t\tmutex.withPermits(1)(\n\t\t\t\t\t\tEffect.promise(() =>\n\t\t\t\t\t\t\tPromise.resolve().then(call as () => Promise<unknown>),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t) as never;\n\t\t\t},\n\t\t) as unknown as S3qliteDatabase;\n\n\t\twrapper.pull = (() =>\n\t\t\tmutex.withPermits(1)(\n\t\t\t\tpull().pipe(Effect.provide(ctx), Effect.asVoid),\n\t\t\t)) as typeof wrapper.pull;\n\t\twrapper.push = () => mutex.withPermits(1)(push().pipe(Effect.provide(ctx), Effect.asVoid));\n\t\twrapper.sync = (() =>\n\t\t\tmutex.withPermits(1)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* pull();\n\t\t\t\t\tyield* push();\n\t\t\t\t}).pipe(Effect.retry(Schedule.recurs(2)), Effect.provide(ctx)),\n\t\t\t)) as typeof wrapper.sync;\n\t\twrapper.fork = (nextDbName) =>\n\t\t\tmutex.withPermits(1)(fork(nextDbName).pipe(Effect.provide(ctx)));\n\t\twrapper.checkpoint = () => Effect.die(new Error(\"Not implemented\"));\n\n\t\treturn wrapper;\n\t});"]}
|
package/esm/src/fork.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Data, Effect, Option } from "effect";
|
|
2
|
+
import { ConnectionConfig } from "./contexts.js";
|
|
3
|
+
import { RemoteKV, headKey } from "./storage.js";
|
|
4
|
+
export class SameNameError extends Data.TaggedError("SameNameError") {
|
|
5
|
+
}
|
|
6
|
+
export class AlreadyExistsError extends Data.TaggedError("AlreadyExistsError") {
|
|
7
|
+
}
|
|
8
|
+
export class SourceDoesNotExistError extends Data.TaggedError("SourceDoesNotExistError") {
|
|
9
|
+
}
|
|
10
|
+
export const fork = (nextDbName) => Effect.gen(function* () {
|
|
11
|
+
const remoteKV = yield* RemoteKV;
|
|
12
|
+
const { dbName } = yield* ConnectionConfig;
|
|
13
|
+
if (nextDbName === dbName) {
|
|
14
|
+
return yield* Effect.fail(new SameNameError({
|
|
15
|
+
dbName,
|
|
16
|
+
message: `Cannot fork database "${dbName}" into itself`,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
const existingFork = yield* remoteKV.get(headKey(nextDbName));
|
|
20
|
+
if (Option.isSome(existingFork)) {
|
|
21
|
+
return yield* Effect.fail(new AlreadyExistsError({
|
|
22
|
+
dbName: nextDbName,
|
|
23
|
+
message: `Database "${nextDbName}" already exists`,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
const sourceHead = yield* remoteKV.get(headKey(dbName));
|
|
27
|
+
if (Option.isNone(sourceHead)) {
|
|
28
|
+
return yield* Effect.fail(new SourceDoesNotExistError({
|
|
29
|
+
dbName,
|
|
30
|
+
message: `Source database "${dbName}" does not exist`,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
yield* remoteKV.set(headKey(nextDbName), sourceHead.value.value);
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.js","sourceRoot":"","sources":["../../../src/fork.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,OAAO,aAAc,SAAQ,IAAI,CAAC,WAAW,CAAC,eAAe,CAGjE;CAAG;AAEL,MAAM,OAAO,kBAAmB,SAAQ,IAAI,CAAC,WAAW,CAAC,oBAAoB,CAG3E;CAAG;AAEL,MAAM,OAAO,uBAAwB,SAAQ,IAAI,CAAC,WAAW,CAAC,yBAAyB,CAGrF;CAAG;AAIL,MAAM,CAAC,MAAM,IAAI,GAAG,CACnB,UAAkB,EAC4C,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC;IACjC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC;IAE3C,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACxB,IAAI,aAAa,CAAC;YACjB,MAAM;YACN,OAAO,EAAE,yBAAyB,MAAM,eAAe;SACvD,CAAC,CACF,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACxB,IAAI,kBAAkB,CAAC;YACtB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,aAAa,UAAU,kBAAkB;SAClD,CAAC,CACF,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACxB,IAAI,uBAAuB,CAAC;YAC3B,MAAM;YACN,OAAO,EAAE,oBAAoB,MAAM,kBAAkB;SACrD,CAAC,CACF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC","sourcesContent":["import { Data, Effect, Option } from \"effect\";\n\nimport { ConnectionConfig } from \"./contexts\";\nimport { RemoteKV, headKey } from \"./storage\";\n\nexport class SameNameError extends Data.TaggedError(\"SameNameError\")<{\n\treadonly dbName: string;\n\treadonly message: string;\n}> {}\n\nexport class AlreadyExistsError extends Data.TaggedError(\"AlreadyExistsError\")<{\n\treadonly dbName: string;\n\treadonly message: string;\n}> {}\n\nexport class SourceDoesNotExistError extends Data.TaggedError(\"SourceDoesNotExistError\")<{\n\treadonly dbName: string;\n\treadonly message: string;\n}> {}\n\nexport type ForkError = SameNameError | AlreadyExistsError | SourceDoesNotExistError;\n\nexport const fork = (\n\tnextDbName: string,\n): Effect.Effect<void, ForkError, ConnectionConfig | RemoteKV> =>\n\tEffect.gen(function* () {\n\t\tconst remoteKV = yield* RemoteKV;\n\t\tconst { dbName } = yield* ConnectionConfig;\n\n\t\tif (nextDbName === dbName) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew SameNameError({\n\t\t\t\t\tdbName,\n\t\t\t\t\tmessage: `Cannot fork database \"${dbName}\" into itself`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst existingFork = yield* remoteKV.get(headKey(nextDbName));\n\t\tif (Option.isSome(existingFork)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew AlreadyExistsError({\n\t\t\t\t\tdbName: nextDbName,\n\t\t\t\t\tmessage: `Database \"${nextDbName}\" already exists`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst sourceHead = yield* remoteKV.get(headKey(dbName));\n\t\tif (Option.isNone(sourceHead)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew SourceDoesNotExistError({\n\t\t\t\t\tdbName,\n\t\t\t\t\tmessage: `Source database \"${dbName}\" does not exist`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tyield* remoteKV.set(headKey(nextDbName), sourceHead.value.value);\n\t});\n"]}
|
package/esm/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { connect as connectInternal, initializeContexts } from "./connection.js"
|
|
|
3
3
|
export * from "./cdc/apply.js";
|
|
4
4
|
export * from "./cdc/extract.js";
|
|
5
5
|
export * from "./cdc/types.js";
|
|
6
|
+
export * from "./fork.js";
|
|
6
7
|
export * from "./kv/fileKV.js";
|
|
7
8
|
export * from "./kv/kv.js";
|
|
8
9
|
export * from "./kv/memoryKV.js";
|
package/esm/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG9E,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAE1B,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,CAAC;AAE9C,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,iBAAoC,EAAE,EAAE,CAC/E,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,IAAI,CACzC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACtB,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CACpE,CACD,CAAC","sourcesContent":["import { Effect } from \"effect\";\n\nimport { connect as connectInternal, initializeContexts } from \"./connection\";\nimport type { ConnectionOptions } from \"./types\";\n\nexport * from \"./cdc/apply\";\nexport * from \"./cdc/extract\";\nexport * from \"./cdc/types\";\nexport * from \"./kv/fileKV\";\nexport * from \"./kv/kv\";\nexport * from \"./kv/memoryKV\";\nexport * from \"./kv/s3KV\";\n\nexport { initializeContexts as initContexts };\n\nexport const connect = (dbName: string, connectionOptions: ConnectionOptions) =>\n\tinitializeContexts(connectionOptions).pipe(\n\t\tEffect.flatMap((ctx) =>\n\t\t\tconnectInternal(dbName, connectionOptions).pipe(Effect.provide(ctx)),\n\t\t),\n\t);"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG9E,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAE1B,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,CAAC;AAE9C,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,iBAAoC,EAAE,EAAE,CAC/E,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,IAAI,CACzC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACtB,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CACpE,CACD,CAAC","sourcesContent":["import { Effect } from \"effect\";\n\nimport { connect as connectInternal, initializeContexts } from \"./connection\";\nimport type { ConnectionOptions } from \"./types\";\n\nexport * from \"./cdc/apply\";\nexport * from \"./cdc/extract\";\nexport * from \"./cdc/types\";\nexport * from \"./fork\";\nexport * from \"./kv/fileKV\";\nexport * from \"./kv/kv\";\nexport * from \"./kv/memoryKV\";\nexport * from \"./kv/s3KV\";\n\nexport { initializeContexts as initContexts };\n\nexport const connect = (dbName: string, connectionOptions: ConnectionOptions) =>\n\tinitializeContexts(connectionOptions).pipe(\n\t\tEffect.flatMap((ctx) =>\n\t\t\tconnectInternal(dbName, connectionOptions).pipe(Effect.provide(ctx)),\n\t\t),\n\t);"]}
|
package/esm/src/push.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/push.ts"],"names":[],"mappings":"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","sourcesContent":["import { Effect, Option } from \"effect\";\n\nimport { extractBatch } from \"./batches\";\nimport { getLatestChangeId } from \"./cdc/extract\";\nimport { truncate } from \"./cdc/truncate\";\nimport { ConnectionConfig, ConnectionState } from \"./contexts\";\nimport type { ConflictError } from \"./kv/kv\";\nimport { pushFiles } from \"./kv/syncFiles\";\nimport {\n\tLocalKV,\n\tRemoteKV,\n\tbaseKey,\n\tbatchKey,\n\tdbKey,\n\tencodeJson,\n\tgetJson,\n\theadKey,\n} from \"./storage\";\nimport type { Batch, Head, StoredHead } from \"./types\";\n\nexport const push = (): Effect.Effect<\n\tvoid,\n\tConflictError,\n\tConnectionConfig | ConnectionState | LocalKV | RemoteKV\n> =>\n\tEffect.gen(function* () {\n\t\tconst localKV = yield* LocalKV;\n\t\tconst remoteKV = yield* RemoteKV;\n\t\tconst { dbName } = yield* ConnectionConfig;\n\t\tconst state = yield* ConnectionState;\n\t\tconst tursoConnection = { current: yield* state.getConnection };\n\n\t\tconst localHead = yield* getJson<StoredHead>(localKV, headKey(dbName)).pipe(\n\t\t\tEffect.orDie,\n\t\t\tEffect.flatMap(\n\t\t\t\tOption.match({\n\t\t\t\t\tonNone: () => Effect.die(new Error(\"Expected local stored head to exist\")),\n\t\t\t\t\tonSome: Effect.succeed,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\n\t\tconst pendingBatches: { batch: Batch; lastChangeId: number }[] = [];\n\n\t\tif (localHead.value.pendingBatches?.length) {\n\t\t\tpendingBatches.push(\n\t\t\t\t...localHead.value.pendingBatches.map((pb) => ({\n\t\t\t\t\tbatch: { id: pb.id },\n\t\t\t\t\tlastChangeId: pb.lastChangeId,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\n\t\tconst extracted = yield* extractBatch(\n\t\t\ttursoConnection.current,\n\t\t\tlocalHead.value.lastSyncedLocalChangeId,\n\t\t);\n\n\t\tif (Option.isSome(extracted)) {\n\t\t\tpendingBatches.push({\n\t\t\t\tbatch: extracted.value.batch,\n\t\t\t\tlastChangeId: extracted.value.lastLocalChangeId,\n\t\t\t});\n\t\t}\n\n\t\tif (pendingBatches.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tyield* pushFiles(pendingBatches.map((pb) => batchKey(pb.batch.id))).pipe(Effect.orDie);\n\n\t\tconst newHead: Head = {\n\t\t\tsnapshots: localHead.value.head.snapshots,\n\t\t\tbatches: localHead.value.head.batches.concat(pendingBatches.map((pb) => pb.batch)),\n\t\t};\n\n\t\tconst result = yield* remoteKV.cas(\n\t\t\theadKey(dbName),\n\t\t\tencodeJson(newHead),\n\t\t\tlocalHead.value.remoteEtag,\n\t\t);\n\n\t\tconst lastSyncedLocalChangeId = yield* getLatestChangeId(tursoConnection.current).pipe(\n\t\t\tEffect.orDie,\n\t\t);\n\n\t\tyield* localKV.set(\n\t\t\theadKey(dbName),\n\t\t\tencodeJson<StoredHead>({\n\t\t\t\thead: newHead,\n\t\t\t\tremoteEtag: result.etag,\n\t\t\t\tlastSyncedLocalChangeId,\n\t\t\t}),\n\t\t);\n\n\t\tconst lastSnapshotId = newHead.snapshots.at(-1)?.id;\n\t\tconst lastBatchId = newHead.batches.at(-1)?.id;\n\t\tif (lastSnapshotId && lastBatchId) {\n\t\t\tyield* truncate(tursoConnection.current);\n\t\t\tyield* localKV.clone(dbKey(dbName), baseKey(lastSnapshotId, lastBatchId));\n\t\t}\n\t});"]}
|
package/esm/src/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Database } from \"@tursodatabase/database\";\nimport type { Effect } from \"effect\";\n\nexport type Snapshot = {\n\tid: string;\n\n\t// thanks to this we can do full history\n\tbaseSnapshotId?: string;\n\tbatchIdsApplied: string[];\n};\n\nexport type Batch = {\n\tid: string;\n};\n\nexport type Head = {\n\tsnapshots: Snapshot[];\n\tbatches: Batch[];\n};\n\nexport type PendingBatch = {\n\tid: string;\n\tlastChangeId: number;\n};\n\nexport type StoredHead = {\n\thead: Head;\n\n\t/**\n\t * The latest etag of the remote head this instance has been synced to.\n\t */\n\tremoteEtag: string;\n\n\tlastSyncedLocalChangeId: number;\n\n\tpendingBatches?: PendingBatch[];\n};\n\n// export type ConflictResolutionMode = \"fail\" | \"preferRemote\" | \"preferLocal\";\n//\n// export type ConflictOperation = \"pull\" | \"push\" | \"sync\";\n//\n// export type ConflictResolutionContext = {\n// \treadonly cause: Error;\n// \treadonly localHead: StoredHead;\n// \treadonly operation: ConflictOperation;\n// \treadonly remoteHead: Head;\n// };\n//\n// export type ConflictResolver = (\n// \tcontext: ConflictResolutionContext,\n// ) => Effect.Effect<ConflictResolutionMode, Error>;\n//\n// type StrongConsistencyOptions = {\n// \tmode: \"strong\";\n// };\n//\n// type EventualConsistencyOptions = {\n// \tmode: \"eventual\";\n// \tsync?: \"manual\" | { everyMs: number };\n// };\n\nexport type ConnectionOptions = {\n\tbucket: string;\n\t// conflictResolution?: ConflictResolutionMode | ConflictResolver;\n\tlocalDirectory?: string;\n};\n// } & (StrongConsistencyOptions | EventualConsistencyOptions);\n\nexport type S3qliteDatabase = Database & {\n\tpull: () => Effect.Effect<void, Error>;\n\tpush: () => Effect.Effect<void,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Database } from \"@tursodatabase/database\";\nimport type { Effect } from \"effect\";\n\nimport type { AlreadyExistsError, SameNameError, SourceDoesNotExistError } from \"./fork\";\nimport type { ConflictError } from \"./kv/kv\";\n\nexport type Snapshot = {\n\tid: string;\n\n\t// thanks to this we can do full history\n\tbaseSnapshotId?: string;\n\tbatchIdsApplied: string[];\n};\n\nexport type Batch = {\n\tid: string;\n};\n\nexport type Head = {\n\tsnapshots: Snapshot[];\n\tbatches: Batch[];\n};\n\nexport type PendingBatch = {\n\tid: string;\n\tlastChangeId: number;\n};\n\nexport type StoredHead = {\n\thead: Head;\n\n\t/**\n\t * The latest etag of the remote head this instance has been synced to.\n\t */\n\tremoteEtag: string;\n\n\tlastSyncedLocalChangeId: number;\n\n\tpendingBatches?: PendingBatch[];\n};\n\n// export type ConflictResolutionMode = \"fail\" | \"preferRemote\" | \"preferLocal\";\n//\n// export type ConflictOperation = \"pull\" | \"push\" | \"sync\";\n//\n// export type ConflictResolutionContext = {\n// \treadonly cause: Error;\n// \treadonly localHead: StoredHead;\n// \treadonly operation: ConflictOperation;\n// \treadonly remoteHead: Head;\n// };\n//\n// export type ConflictResolver = (\n// \tcontext: ConflictResolutionContext,\n// ) => Effect.Effect<ConflictResolutionMode, Error>;\n//\n// type StrongConsistencyOptions = {\n// \tmode: \"strong\";\n// };\n//\n// type EventualConsistencyOptions = {\n// \tmode: \"eventual\";\n// \tsync?: \"manual\" | { everyMs: number };\n// };\n\nexport type ConnectionOptions = {\n\tbucket: string;\n\t// conflictResolution?: ConflictResolutionMode | ConflictResolver;\n\tlocalDirectory?: string;\n};\n// } & (StrongConsistencyOptions | EventualConsistencyOptions);\n\nexport type S3qliteDatabase = Database & {\n\tpull: () => Effect.Effect<void, Error>;\n\tpush: () => Effect.Effect<void, ConflictError>;\n\tsync: () => Effect.Effect<void, Error | ConflictError>;\n\tfork: (\n\t\tdbName: string,\n\t) => Effect.Effect<void, SameNameError | AlreadyExistsError | SourceDoesNotExistError>;\n\tcheckpoint: () => Effect.Effect<void, Error>;\n};"]}
|
package/esm/src/wrapDatabase.js
CHANGED
|
@@ -67,9 +67,17 @@ export const wrapDatabase = (getTarget, wrap) => {
|
|
|
67
67
|
return (...args) => wrapTransaction(Reflect.apply(value, getTarget(), args), wrap);
|
|
68
68
|
}
|
|
69
69
|
if (asyncDatabaseMethods.has(property)) {
|
|
70
|
-
return (...args) => wrap(() =>
|
|
70
|
+
return (...args) => wrap(() => {
|
|
71
|
+
const target = getTarget();
|
|
72
|
+
const current = Reflect.get(target, property, target);
|
|
73
|
+
return Reflect.apply(current, target, args);
|
|
74
|
+
}, property, args);
|
|
71
75
|
}
|
|
72
|
-
return (...args) =>
|
|
76
|
+
return (...args) => {
|
|
77
|
+
const target = getTarget();
|
|
78
|
+
const current = Reflect.get(target, property, target);
|
|
79
|
+
return Reflect.apply(current, target, args);
|
|
80
|
+
};
|
|
73
81
|
},
|
|
74
82
|
});
|
|
75
83
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapDatabase.js","sourceRoot":"","sources":["../../../src/wrapDatabase.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"wrapDatabase.js","sourceRoot":"","sources":["../../../src/wrapDatabase.ts"],"names":[],"mappings":"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","sourcesContent":["const asyncDatabaseMethods = new Set<PropertyKey>([\n\t\"all\",\n\t\"close\",\n\t\"exec\",\n\t\"get\",\n\t\"iterate\",\n\t\"pragma\",\n\t\"run\",\n]);\n\nconst asyncStatementMethods = new Set<PropertyKey>([\"all\", \"close\", \"get\", \"run\"]);\n\nconst syncStatementMethods = new Set<PropertyKey>([\"bind\", \"pluck\", \"raw\", \"safeIntegers\"]);\n\nconst wrapTransaction = <T extends (...args: readonly unknown[]) => unknown>(\n\ttransaction: T,\n\twrap: <R>(call: () => R, method: PropertyKey, args: readonly unknown[]) => R,\n): T =>\n\tnew Proxy(transaction, {\n\t\tapply: (target, thisArg, args) =>\n\t\t\twrap(() => Reflect.apply(target, thisArg, args), \"transaction\", args),\n\t\tget: (target, property, receiver) => {\n\t\t\tconst value = Reflect.get(target, property, receiver);\n\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\treturn wrapTransaction(value as (...args: readonly unknown[]) => unknown, wrap);\n\t\t},\n\t}) as T;\n\nconst wrapStatement = <T extends object>(\n\tstatement: T,\n\twrap: <R>(call: () => R, method: PropertyKey, args: readonly unknown[]) => R,\n): T => {\n\tlet proxy: T;\n\n\tproxy = new Proxy(statement, {\n\t\tget: (target, property, receiver) => {\n\t\t\tconst value = Reflect.get(target, property, receiver);\n\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\tif (syncStatementMethods.has(property)) {\n\t\t\t\treturn (...args: unknown[]) => {\n\t\t\t\t\tReflect.apply(value, target, args);\n\t\t\t\t\treturn proxy;\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (property === \"iterate\") {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\t(async function* () {\n\t\t\t\t\t\tconst iterable = wrap(\n\t\t\t\t\t\t\t() => Reflect.apply(value, target, args),\n\t\t\t\t\t\t\tproperty,\n\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t) as AsyncIterable<unknown>;\n\n\t\t\t\t\t\tfor await (const item of iterable) {\n\t\t\t\t\t\t\tyield item;\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t}\n\n\t\t\tif (asyncStatementMethods.has(property)) {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrap(() => Reflect.apply(value, target, args), property, args);\n\t\t\t}\n\n\t\t\treturn (...args: unknown[]) => Reflect.apply(value, target, args);\n\t\t},\n\t});\n\n\treturn proxy;\n};\n\nexport const wrapDatabase = <T extends object>(\n\tgetTarget: () => T,\n\twrap: <R>(call: () => R, method: PropertyKey, args: readonly unknown[]) => R,\n): T => {\n\treturn new Proxy({} as T, {\n\t\tget: (wrapper, property, receiver) => {\n\t\t\tif (Reflect.has(wrapper, property)) {\n\t\t\t\treturn Reflect.get(wrapper, property, receiver);\n\t\t\t}\n\n\t\t\tconst original = getTarget();\n\t\t\tconst value = Reflect.get(original, property, original);\n\n\t\t\tif (typeof value !== \"function\") {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\tif (property === \"prepare\") {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrapStatement(Reflect.apply(value, getTarget(), args) as object, wrap);\n\t\t\t}\n\n\t\t\tif (property === \"transaction\") {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrapTransaction(\n\t\t\t\t\t\tReflect.apply(value, getTarget(), args) as (\n\t\t\t\t\t\t\t...args: readonly unknown[]\n\t\t\t\t\t\t) => unknown,\n\t\t\t\t\t\twrap,\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (asyncDatabaseMethods.has(property)) {\n\t\t\t\treturn (...args: unknown[]) =>\n\t\t\t\t\twrap(\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tconst target = getTarget();\n\t\t\t\t\t\t\tconst current = Reflect.get(target, property, target) as (\n\t\t\t\t\t\t\t\t...args: readonly unknown[]\n\t\t\t\t\t\t\t) => unknown;\n\t\t\t\t\t\t\treturn Reflect.apply(current, target, args);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\targs,\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn (...args: unknown[]) => {\n\t\t\t\tconst target = getTarget();\n\t\t\t\tconst current = Reflect.get(target, property, target) as (\n\t\t\t\t\t...args: readonly unknown[]\n\t\t\t\t) => unknown;\n\t\t\t\treturn Reflect.apply(current, target, args);\n\t\t\t};\n\t\t},\n\t});\n};"]}
|