@happy0/flumeview-level-custom 1.0.0 → 2.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happy0/flumeview-level-custom",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "author": "Gordon Martin",
@@ -0,0 +1,104 @@
1
+ const Obv = require("obv");
2
+ const path = require("path");
3
+ const charwise = require("charwise");
4
+ const { Level } = require("level");
5
+ const pull = require("pull-stream");
6
+ const { createPullStream } = require("./stream/read");
7
+ const { METADATA_KEY } = require("./metadata");
8
+
9
+ module.exports = function (version, createWriter, filter) {
10
+ return function (log, name) {
11
+ const dir = path.dirname(log.filename);
12
+
13
+ const since = Obv();
14
+ const db = createLevelDb(dir, log, name);
15
+ let closed = false;
16
+
17
+ startConsumingLog(db, since, version);
18
+
19
+ let writer;
20
+
21
+ function destroy(cb) {
22
+ cb = cb || (() => {});
23
+ since.set(-1);
24
+ db.clear().then(cb);
25
+ }
26
+
27
+ async function startConsumingLog(db, since) {
28
+ const value = await db.get(METADATA_KEY, { keyEncoding: "utf8" });
29
+
30
+ if (!value) {
31
+ // No existing entry, we're consuming the log from the start
32
+ since.set(-1);
33
+ } else if (value.version === version) {
34
+ // The version has not changed, resume consuming the log from where we left off
35
+ // last time
36
+ since.set(value.since);
37
+ } else {
38
+ // version has changed, wipe db and start over.
39
+ destroy(() => {});
40
+ }
41
+ }
42
+
43
+ return {
44
+ // flumeview interface
45
+ methods: { get: "async", read: "source" },
46
+ since: since,
47
+ createSink: function (cb) {
48
+ writer = createWriter(db, since, version, cb);
49
+
50
+ if (!filter) {
51
+ return writer;
52
+ } else {
53
+ return pull(
54
+ pull.filter((item) => filter(item.value)),
55
+ writer,
56
+ );
57
+ }
58
+ },
59
+ destroy: destroy,
60
+ close: function (cb) {
61
+ cb = cb || (() => {});
62
+
63
+ closed = true;
64
+
65
+ if (writer) {
66
+ writer.abort(() => db.close().then(cb));
67
+ } else {
68
+ db.close().then(cb);
69
+ }
70
+ },
71
+ get: function (key, cb) {
72
+ db.get(key).then((result) => {
73
+ if (result === undefined) {
74
+ cb(result);
75
+ } else {
76
+ cb(null, result);
77
+ }
78
+ });
79
+ },
80
+ /**
81
+ *
82
+ * @param {*} opts The level DB stream key range options ( see 'level-read-stream' library ) as well as an optional 'live' parameter to indicate whether the
83
+ * stream should stay live an emit live updates. IF live, a {sync:true} object will be emitted when the stream is about to emit
84
+ * live values and each item will have a 'type' field (put, del) indicating whether the database operation type as well as the key
85
+ * and value
86
+ * @returns a pull-stream source of database entries satisfying the input range
87
+ */
88
+ read: function (opts) {
89
+ return createPullStream(db, opts);
90
+ },
91
+ };
92
+ };
93
+ };
94
+
95
+ function createLevelDb(dir, log, name) {
96
+ if (!log.filename) {
97
+ throw new Error("flumeview-level-custom can only be used with a file log");
98
+ }
99
+
100
+ return new Level(path.join(dir, name), {
101
+ keyEncoding: charwise,
102
+ valueEncoding: "json",
103
+ });
104
+ }
package/src/index.js CHANGED
@@ -1,164 +1,7 @@
1
- const Obv = require("obv");
2
- const path = require("path");
3
- const Write = require("pull-write");
4
- const charwise = require("charwise");
5
- const { Level } = require("level");
6
- const pull = require("pull-stream");
7
- const { createPullStream } = require("./stream/read");
1
+ const flumeViewLevelCustom = require("./flumeView");
2
+ const mapperBuilder = require("./mapperBuilder");
3
+ const metadata = require("./metadata");
8
4
 
9
- const METADATA_KEY = "\x00";
10
-
11
- const MAX_ITEM_BUFFER_SIZE = 0;
12
-
13
- /**
14
- *
15
- * @param {*} version the version of the flumeview we're building. If this value differs to the value stored in the view's metadata, we
16
- * will rebuild the index from scratch.
17
- * @param {*} asyncMapper A function that takes a level database and the current flumelog item value as parameters. It should return an array of
18
- * batch operation objects to be written to the database. It's important that these are returned rather than inserted via the db parameter so that the
19
- * flumeview metadata can be updated at the same time as the new entries with the current log position atomically.
20
- * @param {*} filter Optional function to filter out items in the logstream by prior to mapping them to database entries
21
- * @returns
22
- */
23
- module.exports = function (version, asyncMapper, filter) {
24
- return function (log, name) {
25
- const dir = path.dirname(log.filename);
26
-
27
- const since = Obv();
28
- const db = createLevelDb(dir, log, name);
29
- let closed = false;
30
-
31
- startConsumingLog(db, since, version);
32
-
33
- let writer;
34
-
35
- async function mapEntry(item) {
36
- const items = await asyncMapper(db, item);
37
- return items;
38
- }
39
-
40
- function createWriter(db, streamEndCb) {
41
- return Write(
42
- function write(batch, cb) {
43
- if (closed) {
44
- return cb(new Error("database closed while index was building"));
45
- } else if (batch.length !== 1) {
46
- // For v1 we're only exposing an interface that allows one item to be processed at a time.
47
- // For future versions we may consider batching optimisations
48
- throw new Error(
49
- "Batch should never have more or less than one item.",
50
- );
51
- } else {
52
- const item = batch[0];
53
-
54
- const metadata = {
55
- key: METADATA_KEY,
56
- value: { version: version, since: item.seq },
57
- valueEncoding: "json",
58
- keyEncoding: "utf8",
59
- type: "put",
60
- };
61
-
62
- const update = mapEntry(item.value);
63
-
64
- update
65
- .then((items) => {
66
- return db.batch([metadata, ...items]);
67
- })
68
- .then((_) => {
69
- since.set(item.seq);
70
- cb();
71
- })
72
- .catch((e) => console.error(e));
73
- }
74
- },
75
- null,
76
- MAX_ITEM_BUFFER_SIZE,
77
- streamEndCb,
78
- );
79
- }
80
-
81
- function destroy(cb) {
82
- cb = cb || (() => {});
83
- since.set(-1);
84
- db.clear().then(cb);
85
- }
86
-
87
- async function startConsumingLog(db, since) {
88
- const value = await db.get(METADATA_KEY, { keyEncoding: "utf8" });
89
-
90
- if (!value) {
91
- // No existing entry, we're consuming the log from the start
92
- since.set(-1);
93
- } else if (value.version === version) {
94
- // The version has not changed, resume consuming the log from where we left off
95
- // last time
96
- since.set(value.since);
97
- } else {
98
- // version has changed, wipe db and start over.
99
- destroy(() => {});
100
- }
101
- }
102
-
103
- return {
104
- // flumeview interface
105
- methods: { get: "async", read: "source" },
106
- since: since,
107
- createSink: function (cb) {
108
- writer = createWriter(db, cb);
109
-
110
- if (!filter) {
111
- return writer;
112
- } else {
113
- return pull(
114
- pull.filter((item) => filter(item.value)),
115
- writer,
116
- );
117
- }
118
- },
119
- destroy: destroy,
120
- close: function (cb) {
121
- cb = cb || (() => {});
122
-
123
- closed = true;
124
-
125
- if (writer) {
126
- writer.abort(() => db.close().then(cb));
127
- } else {
128
- db.close().then(cb);
129
- }
130
- },
131
- get: function (key, cb) {
132
- db.get(key).then((result) => {
133
- if (result === undefined) {
134
- cb(result);
135
- } else {
136
- cb(null, result);
137
- }
138
- });
139
- },
140
- /**
141
- *
142
- * @param {*} opts The level DB stream key range options ( see 'level-read-stream' library ) as well as an optional 'live' parameter to indicate whether the
143
- * stream should stay live an emit live updates. IF live, a {sync:true} object will be emitted when the stream is about to emit
144
- * live values and each item will have a 'type' field (put, del) indicating whether the database operation type as well as the key
145
- * and value
146
- * @returns a pull-stream source of database entries satisfying the input range
147
- */
148
- read: function (opts) {
149
- return createPullStream(db, opts);
150
- },
151
- };
152
- };
153
- };
154
-
155
- function createLevelDb(dir, log, name) {
156
- if (!log.filename) {
157
- throw new Error("flumeview-level-custom can only be used with a file log");
158
- }
159
-
160
- return new Level(path.join(dir, name), {
161
- keyEncoding: charwise,
162
- valueEncoding: "json",
163
- });
164
- }
5
+ module.exports.FlumeViewLevelCustom = flumeViewLevelCustom;
6
+ module.exports.mapperBuilder = mapperBuilder;
7
+ module.exports.metadata = metadata;
@@ -0,0 +1,37 @@
1
+ const Write = require("pull-write");
2
+ const { createMetadataUpdateItem } = require("./metadata");
3
+
4
+ module.exports.sequentialMapper = function (asyncMapper) {
5
+ const MAX_ITEM_BUFFER_SIZE = 0;
6
+
7
+ return function (db, since, version, streamEndCb) {
8
+ return Write(
9
+ function write(batch, cb) {
10
+ if (batch.length !== 1) {
11
+ cb(
12
+ new Error(
13
+ "Batch should never have more or less than one item for sequential mapper.",
14
+ ),
15
+ );
16
+ } else {
17
+ const item = batch[0];
18
+ const metadata = createMetadataUpdateItem(version, item.seq);
19
+ const update = asyncMapper(db, item.value);
20
+
21
+ Promise.resolve(update)
22
+ .then((items) => {
23
+ return db.batch([metadata, ...items]);
24
+ })
25
+ .then((_) => {
26
+ since.set(item.seq);
27
+ cb();
28
+ })
29
+ .catch((e) => console.error(e));
30
+ }
31
+ },
32
+ null,
33
+ MAX_ITEM_BUFFER_SIZE,
34
+ streamEndCb,
35
+ );
36
+ };
37
+ };
@@ -0,0 +1,16 @@
1
+ const METADATA_KEY = "\x00";
2
+
3
+ function createMetadataUpdateBatchItem(version, seq) {
4
+ return {
5
+ key: METADATA_KEY,
6
+ value: { version: version, since: seq },
7
+ valueEncoding: "json",
8
+ keyEncoding: "utf8",
9
+ type: "put",
10
+ };
11
+ }
12
+
13
+ module.exports = {
14
+ METADATA_KEY,
15
+ createMetadataUpdateItem: createMetadataUpdateBatchItem,
16
+ };
@@ -0,0 +1,236 @@
1
+ const { FlumeViewCustom, mapperBuilder } = require("../src/index");
2
+ const Flume = require("flumedb");
3
+ const util = require("util");
4
+ const pull = require("pull-stream");
5
+ const Log = require("flumelog-offset");
6
+ const codec = require("flumecodec");
7
+ const { rmSync } = require("fs");
8
+
9
+ function pullCollectPromise(source) {
10
+ return new Promise((resolve, reject) => {
11
+ return pull(
12
+ source,
13
+ pull.collect((err, result) => {
14
+ if (result) {
15
+ resolve(result);
16
+ } else {
17
+ reject(err);
18
+ }
19
+ }),
20
+ );
21
+ });
22
+ }
23
+
24
+ function chessView(flumeDb) {
25
+ async function chessMapper(db, item) {
26
+ // Simplified - in a really scuttlebutt environment we'd have to deal with invite acceptances / game ends arriving
27
+ // out of order from different user's feeds
28
+
29
+ if (item.value.content.type === "chess_invite") {
30
+ const gameId = item.key;
31
+
32
+ const entry = {
33
+ id: gameId,
34
+ inviter: item.value.author,
35
+ invitee: item.value.content.inviting,
36
+ white:
37
+ item.value.content.myColor === "white"
38
+ ? item.value.author
39
+ : item.value.content.inviting,
40
+ black:
41
+ item.value.content.myColor === "black"
42
+ ? item.value.author
43
+ : item.value.content.inviting,
44
+ state: "invite",
45
+ };
46
+
47
+ return [
48
+ {
49
+ type: "put",
50
+ key: `/games/${gameId}`,
51
+ value: entry,
52
+ },
53
+ {
54
+ type: "put",
55
+ key: `/player/inviter/${entry.inviter}/id/${gameId}`,
56
+ value: entry,
57
+ },
58
+ {
59
+ type: "put",
60
+ key: `/player/invited/${entry.invitee}/id/${gameId}`,
61
+ value: entry,
62
+ },
63
+ ];
64
+ } else if (item.value.content.type === "chess_invite_accept") {
65
+ const gameId = item.value.content.root;
66
+ const existingEntry = await db.get(`/games/${gameId}`);
67
+
68
+ existingEntry.state = "accepted";
69
+
70
+ const deletions = [
71
+ {
72
+ type: "del",
73
+ key: `/player/inviter/${existingEntry.inviter}/id/${gameId}`,
74
+ },
75
+ {
76
+ type: "del",
77
+ key: `/player/invited/${existingEntry.invitee}/id/${gameId}`,
78
+ },
79
+ ];
80
+
81
+ const insertions = [
82
+ { type: "put", key: `/games/${gameId}`, value: existingEntry },
83
+ {
84
+ type: "put",
85
+ key: `/player/live_game/${existingEntry.inviter}/game/${gameId}`,
86
+ value: existingEntry,
87
+ },
88
+ {
89
+ type: "put",
90
+ key: `/player/live_game/${existingEntry.invitee}/game/${gameId}`,
91
+ value: existingEntry,
92
+ },
93
+ ];
94
+
95
+ return [...deletions, ...insertions];
96
+ }
97
+ }
98
+
99
+ const chessFlumeView = FlumeViewCustom(
100
+ 1,
101
+ mapperBuilder.sequentialMapper(chessMapper),
102
+ );
103
+
104
+ const db = flumeDb.use("flumeviewCustom", chessFlumeView);
105
+
106
+ const chessView = db["flumeviewCustom"];
107
+ const append = util.promisify(db.append);
108
+
109
+ return {
110
+ append: (item) => {
111
+ return append(item);
112
+ },
113
+ getOutboundInvites: function (inviter) {
114
+ return chessView.read({
115
+ gt: `/player/inviter/${inviter}/`,
116
+ lt: `/player/inviter/${inviter}/\uffff`,
117
+ });
118
+ },
119
+ getInboundInvites: function (invitee) {
120
+ return chessView.read({
121
+ gt: `/player/invited/${invitee}/`,
122
+ lt: `/player/invited/${invitee}/\uffff`,
123
+ });
124
+ },
125
+ getGamesInProgress: function (playerId) {
126
+ return chessView.read({
127
+ gt: `/player/live_game/${playerId}/`,
128
+ lt: `/player/live_game/${playerId}/\uffff`,
129
+ });
130
+ },
131
+ };
132
+ }
133
+
134
+ describe("End to end scenario", () => {
135
+ let db;
136
+
137
+ afterEach(async () => {
138
+ if (db) {
139
+ db.close(() => {});
140
+ db = null;
141
+ rmSync("/tmp/flumeview-level-custom", { recursive: true, force: true });
142
+ }
143
+ });
144
+
145
+ test("Chess game index scenario invites (non-live)", async () => {
146
+ db = Flume(
147
+ Log("/tmp/flumeview-level-custom/log2.offset", 1024, codec.json),
148
+ );
149
+
150
+ const chess = chessView(db);
151
+
152
+ const items = [
153
+ {
154
+ key: "%Zf7y1aoZ/51P5IzF6GEvXNRkBqs+X7r+hG5djhQfwpM=.sha256",
155
+ value: {
156
+ previous: "%EA7jGV3XotJ3qQRMwpDbswj2xfHkEFCbZtSiXfr6d6E=.sha256",
157
+ sequence: 19942,
158
+ author: "@RJ09Kfs3neEZPrbpbWVDxkN92x9moe3aPusOMOc4S2I=.ed25519",
159
+ timestamp: 1791495457292,
160
+ hash: "sha256",
161
+ content: {
162
+ type: "chess_invite",
163
+ inviting: "@STgkEO48Kucd57i7x0vBqfkz/euvt2XdglwF6gZLtdk=.ed25519",
164
+ myColor: "black",
165
+ branch: [
166
+ "%AxUibLnrkZrzA6bk15LpGGDepPZOR2nEn24Kql0YYho=.sha256",
167
+ "%t/8R6p+0Hkq7vDacYwiMOoKNzRf9DD2Ukhiyhxmsroc=.sha256",
168
+ "%01ksKwoAAkrY+bjYdP1WCgtZHHGGcZ8bXmEoYnUlyn4=.sha256",
169
+ "%9oK3ltfnvH9/7h5sxD6Cu+IYoSXcOddTMPJ5L6dLsUM=.sha256",
170
+ "%ldUBk14tkOXNoLECOP9BYDyCyBh4p9KvfHAIzpwcK9k=.sha256",
171
+ ],
172
+ },
173
+ signature:
174
+ "LSq6Ul3o71i7QlPANr3xEhuazhHhPyByLoCGtxv+Nhu37VeI35UOIjCXGBfUNl+9c4f54y6XRNZXS4/ctQvhAg==.sig.ed25519",
175
+ },
176
+ },
177
+ {
178
+ key: "%GpeF1QW5NEiyMDSnotDYoU/2YDFAVVVNYJDleKpnhtw=.sha256",
179
+ value: {
180
+ previous: "%sat8SZr1QshItA3IZK4e6221k8i+3NOf0JLH6PlgIzw=.sha256",
181
+ sequence: 5561,
182
+ author: "@STgkEO48Kucd57i7x0vBqfkz/euvt2XdglwF6gZLtdk=.ed25519",
183
+ timestamp: 1793634892038,
184
+ hash: "sha256",
185
+ content: {
186
+ type: "chess_invite_accept",
187
+ root: "%Zf7y1aoZ/51P5IzF6GEvXNRkBqs+X7r+hG5djhQfwpM=.sha256",
188
+ branch: "%Zf7y1aoZ/51P5IzF6GEvXNRkBqs+X7r+hG5djhQfwpM=.sha256",
189
+ },
190
+ signature:
191
+ "S/ZvvE9VNtkcI6vMJmijlwU8kDAjKLlWhAMenA/tZQRs1MOsDnl3AySfNMAkNEIJw4pUP0//EeTUOFPQgECwCA==.sig.ed25519",
192
+ },
193
+ timestamp: 1793639323422,
194
+ rts: 1793634892038,
195
+ },
196
+ ];
197
+
198
+ await chess.append(items[0]);
199
+
200
+ const source = chess.getInboundInvites(
201
+ "@STgkEO48Kucd57i7x0vBqfkz/euvt2XdglwF6gZLtdk=.ed25519",
202
+ );
203
+
204
+ const result = await pullCollectPromise(source);
205
+
206
+ expect(result.length).toStrictEqual(1);
207
+
208
+ const invitedSource = chess.getOutboundInvites(
209
+ "@RJ09Kfs3neEZPrbpbWVDxkN92x9moe3aPusOMOc4S2I=.ed25519",
210
+ );
211
+ const invitedResult = await pullCollectPromise(invitedSource);
212
+ expect(invitedResult.length).toStrictEqual(1);
213
+
214
+ const inProgressSource = chess.getGamesInProgress(
215
+ "@RJ09Kfs3neEZPrbpbWVDxkN92x9moe3aPusOMOc4S2I=.ed25519",
216
+ );
217
+ const inProgressResult = await pullCollectPromise(inProgressSource);
218
+ expect(inProgressResult.length).toStrictEqual(0);
219
+
220
+ // After invite acceptance the invites should disappear and there should be an 'in progress' result
221
+ await chess.append(items[1]);
222
+
223
+ const invitedSource2 = chess.getInboundInvites(
224
+ "@RJ09Kfs3neEZPrbpbWVDxkN92x9moe3aPusOMOc4S2I=.ed25519",
225
+ );
226
+ const invitedResult2 = await pullCollectPromise(invitedSource2);
227
+ expect(invitedResult2.length).toStrictEqual(0);
228
+
229
+ const inProgressSource2 = chess.getGamesInProgress(
230
+ "@RJ09Kfs3neEZPrbpbWVDxkN92x9moe3aPusOMOc4S2I=.ed25519",
231
+ );
232
+
233
+ const inProgressResult2 = await pullCollectPromise(inProgressSource2);
234
+ expect(inProgressResult2.length).toStrictEqual(1);
235
+ });
236
+ });
@@ -1,5 +1,5 @@
1
- var Log = require("flumelog-offset");
2
- const FlumeviewCustom = require("../src");
1
+ const Log = require("flumelog-offset");
2
+ const { mapperBuilder, FlumeViewCustom } = require("../src");
3
3
  const Flume = require("flumedb");
4
4
  const codec = require("flumecodec");
5
5
  const util = require("util");
@@ -8,25 +8,33 @@ const pull = require("pull-stream");
8
8
  const Aborter = require("pull-abortable");
9
9
 
10
10
  describe("flumeview-level-custom flumeview builder tests", () => {
11
- function mapper(db, item) {
12
- return [{ type: "put", key: item.id, value: item.value }];
11
+ function defaultWriter() {
12
+ return mapperBuilder.sequentialMapper(defaultMapper);
13
+ }
14
+
15
+ function createWriter(mapper) {
16
+ return mapperBuilder.sequentialMapper(mapper);
17
+ }
18
+
19
+ function defaultMapper(db, item) {
20
+ return Promise.resolve([{ type: "put", key: item.id, value: item.value }]);
13
21
  }
14
22
 
15
23
  let db = null;
16
24
 
17
- describe("get", () => {
18
- afterEach(async () => {
19
- if (db) {
20
- db.close(() => {});
21
- db = null;
22
- rmSync("/tmp/flumeview-level-custom", { recursive: true, force: true });
23
- }
24
- });
25
+ afterEach(async () => {
26
+ if (db) {
27
+ db.close(() => {});
28
+ db = null;
29
+ rmSync("/tmp/flumeview-level-custom", { recursive: true, force: true });
30
+ }
31
+ });
25
32
 
33
+ describe("get", () => {
26
34
  test("Test without optional filter function parameter set", async () => {
27
35
  db = Flume(
28
36
  Log("/tmp/flumeview-level-custom/log2.offset", 1024, codec.json),
29
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
37
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter()));
30
38
 
31
39
  const get = util.promisify(db["flumeviewCustom"].get);
32
40
  const append = util.promisify(db.append);
@@ -43,7 +51,7 @@ describe("flumeview-level-custom flumeview builder tests", () => {
43
51
 
44
52
  db = Flume(
45
53
  Log("/tmp/flumeview-level-custom/log.offset", 1024, codec.json),
46
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper, filter));
54
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter(), filter));
47
55
 
48
56
  const append = util.promisify(db.append);
49
57
  const get = util.promisify(db["flumeviewCustom"].get);
@@ -63,18 +71,10 @@ describe("flumeview-level-custom flumeview builder tests", () => {
63
71
  });
64
72
 
65
73
  describe("read", () => {
66
- afterEach(async () => {
67
- if (db) {
68
- db.close(() => {});
69
- db = null;
70
- rmSync("/tmp/flumeview-level-custom", { recursive: true, force: true });
71
- }
72
- });
73
-
74
74
  test("non-live read", async () => {
75
75
  db = Flume(
76
76
  Log("/tmp/flumeview-level-custom/log.offset", 1024, codec.json),
77
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
77
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter()));
78
78
 
79
79
  const append = util.promisify(db.append);
80
80
  const read = db["flumeviewCustom"].read;
@@ -111,7 +111,7 @@ describe("flumeview-level-custom flumeview builder tests", () => {
111
111
  test("Read live stream", async () => {
112
112
  db = Flume(
113
113
  Log("/tmp/flumeview-level-custom/log.offset", 1024, codec.json),
114
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
114
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter()));
115
115
 
116
116
  const append = util.promisify(db.append);
117
117
  const read = db["flumeviewCustom"].read;
@@ -173,7 +173,7 @@ describe("flumeview-level-custom flumeview builder tests", () => {
173
173
 
174
174
  db = Flume(
175
175
  Log("/tmp/flumeview-level-custom/log.offset", 1024, codec.json),
176
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
176
+ ).use("flumeviewCustom", FlumeViewCustom(1, createWriter(mapper)));
177
177
 
178
178
  const append = util.promisify(db.append);
179
179
  const read = db["flumeviewCustom"].read;
@@ -247,7 +247,7 @@ describe("flumeview-level-custom flumeview builder tests", () => {
247
247
  test("Starts from last offset reached on startup", async () => {
248
248
  db = Flume(
249
249
  Log("/tmp/flumeview-level-custom/log2.offset", 1024, codec.json),
250
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
250
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter()));
251
251
 
252
252
  const get = util.promisify(db["flumeviewCustom"].get);
253
253
  const append = util.promisify(db.append);
@@ -274,7 +274,7 @@ describe("flumeview-level-custom flumeview builder tests", () => {
274
274
 
275
275
  db = Flume(
276
276
  Log("/tmp/flumeview-level-custom/log2.offset", 1024, codec.json),
277
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
277
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter()));
278
278
 
279
279
  const read = db["flumeviewCustom"].read;
280
280
 
@@ -322,7 +322,7 @@ describe("flumeview-level-custom flumeview builder tests", () => {
322
322
 
323
323
  db = Flume(
324
324
  Log("/tmp/flumeview-level-custom/log2.offset", 1024, codec.json),
325
- ).use("flumeviewCustom", FlumeviewCustom(1, mapper));
325
+ ).use("flumeviewCustom", FlumeViewCustom(1, defaultWriter()));
326
326
 
327
327
  const get = util.promisify(db["flumeviewCustom"].get);
328
328
  const append = util.promisify(db.append);
@@ -337,7 +337,10 @@ describe("flumeview-level-custom flumeview builder tests", () => {
337
337
 
338
338
  db = Flume(
339
339
  Log("/tmp/flumeview-level-custom/log2.offset", 1024, codec.json),
340
- ).use("flumeviewCustom", FlumeviewCustom(2, newSchemaMapper));
340
+ ).use(
341
+ "flumeviewCustom",
342
+ FlumeViewCustom(2, createWriter(newSchemaMapper)),
343
+ );
341
344
 
342
345
  const get2 = util.promisify(db["flumeviewCustom"].get);
343
346
 
package/vitest.config.ts CHANGED
@@ -3,5 +3,6 @@ import { defineConfig } from "vitest/config";
3
3
  export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
+ maxConcurrency: 1,
6
7
  },
7
8
  });