@mblaney/holster 1.0.35 → 2.0.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/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/dist/holster.js +4 -0
- package/dist/holster.js.map +7 -0
- package/package.json +22 -5
- package/src/dup.js +4 -5
- package/src/get.js +10 -19
- package/src/ham.js +22 -49
- package/src/holster.js +172 -58
- package/src/index.js +1 -1
- package/src/radisk.js +89 -86
- package/src/sea-utils.js +1 -1
- package/src/sea.js +0 -52
- package/src/store.js +8 -8
- package/src/user.js +0 -4
- package/src/wire.js +209 -79
- package/ts/src/array.ts +78 -0
- package/ts/src/buffer.ts +441 -0
- package/ts/src/dup.ts +38 -0
- package/ts/src/get.ts +83 -0
- package/ts/src/ham.ts +258 -0
- package/ts/src/holster.ts +968 -0
- package/ts/src/index.ts +35 -0
- package/ts/src/radisk.ts +591 -0
- package/ts/src/radix.ts +157 -0
- package/ts/src/schemas.ts +515 -0
- package/ts/src/sea-utils.ts +103 -0
- package/ts/src/sea.ts +388 -0
- package/ts/src/store.ts +321 -0
- package/ts/src/user.ts +377 -0
- package/ts/src/utils.ts +232 -0
- package/ts/src/wire.ts +961 -0
- package/.dockerignore +0 -9
- package/.github/workflows/node.js.yml +0 -31
- package/.github/workflows/npm-publish.yml +0 -33
- package/Dockerfile +0 -20
- package/examples/index.html +0 -33
- package/examples/index.js +0 -49
- package/examples/user.html +0 -31
- package/examples/user.js +0 -93
- package/examples/wire.html +0 -27
- package/examples/wire.js +0 -55
- package/test/holster.get.test.js +0 -94
- package/test/holster.lex.test.js +0 -196
- package/test/holster.off.test.js +0 -53
- package/test/holster.on.test.js +0 -264
- package/test/holster.put.test.js +0 -358
- package/test/holster.secure.test.js +0 -202
- package/test/holster.user.get.test.js +0 -114
- package/test/holster.user.lex.test.js +0 -149
- package/test/holster.user.off.test.js +0 -73
- package/test/holster.user.on.test.js +0 -300
- package/test/holster.user.put.test.js +0 -463
- package/test/kitty/! +0 -9
- package/test/names.js +0 -225
- package/test/radisk.cache.test.js +0 -213
- package/test/radisk.test.js +0 -331
- package/test/radix.test.js +0 -117
- package/test/sea.test.js +0 -284
- package/test/split.test.js +0 -317
- package/test/store.test.js +0 -316
- package/test/system/basic.test.js +0 -29
- package/test/system/chained-on.test.js +0 -41
- package/test/system/concurrent-listeners.test.js +0 -125
- package/test/system/immediate-read.test.js +0 -30
- package/test/system/listener-before-data.test.js +0 -51
- package/test/system/promise-wrapper.test.js +0 -47
- package/test/system/user-chained-on.test.js +0 -60
- package/test/system/user-concurrent-listeners.test.js +0 -142
- package/test/system/user-listener-before-data.test.js +0 -66
- package/test/user.test.js +0 -173
- package/test/wire.test.js +0 -158
package/.dockerignore
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
-
|
|
4
|
-
name: Node.js CI
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
branches: [ "main" ]
|
|
9
|
-
pull_request:
|
|
10
|
-
branches: [ "main" ]
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
build:
|
|
14
|
-
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
node-version: [18.x, 20.x, 22.x]
|
|
20
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- uses: actions/checkout@v4
|
|
24
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
-
uses: actions/setup-node@v4
|
|
26
|
-
with:
|
|
27
|
-
node-version: ${{ matrix.node-version }}
|
|
28
|
-
cache: 'npm'
|
|
29
|
-
- run: npm ci
|
|
30
|
-
- run: npm run build --if-present
|
|
31
|
-
- run: npm test
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
-
|
|
4
|
-
name: Node.js Package
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
release:
|
|
8
|
-
types: [created]
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
build:
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v4
|
|
15
|
-
- uses: actions/setup-node@v4
|
|
16
|
-
with:
|
|
17
|
-
node-version: 20
|
|
18
|
-
- run: npm ci
|
|
19
|
-
- run: npm test
|
|
20
|
-
|
|
21
|
-
publish-npm:
|
|
22
|
-
needs: build
|
|
23
|
-
runs-on: ubuntu-latest
|
|
24
|
-
steps:
|
|
25
|
-
- uses: actions/checkout@v4
|
|
26
|
-
- uses: actions/setup-node@v4
|
|
27
|
-
with:
|
|
28
|
-
node-version: 20
|
|
29
|
-
registry-url: https://registry.npmjs.org/
|
|
30
|
-
- run: npm ci
|
|
31
|
-
- run: npm publish
|
|
32
|
-
env:
|
|
33
|
-
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
package/Dockerfile
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Use official Node.js runtime as base image
|
|
2
|
-
FROM node:20-alpine
|
|
3
|
-
|
|
4
|
-
# Set working directory in container
|
|
5
|
-
WORKDIR /app
|
|
6
|
-
|
|
7
|
-
# Copy package.json and package-lock.json (if available)
|
|
8
|
-
COPY package*.json ./
|
|
9
|
-
|
|
10
|
-
# Install dependencies
|
|
11
|
-
RUN npm install
|
|
12
|
-
|
|
13
|
-
# Copy the rest of the application code
|
|
14
|
-
COPY . .
|
|
15
|
-
|
|
16
|
-
# Expose the HTTP port and WebSocket port
|
|
17
|
-
EXPOSE 3000 8765
|
|
18
|
-
|
|
19
|
-
# Run the server
|
|
20
|
-
CMD ["node", "src/index.js"]
|
package/examples/index.html
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<title>Holster - test examples</title>
|
|
6
|
-
</head>
|
|
7
|
-
<body>
|
|
8
|
-
<h2>Holster - test examples</h2>
|
|
9
|
-
<p>
|
|
10
|
-
This file loads <code>index.js</code> which contains examples that use the
|
|
11
|
-
Holster API. See the console log for test output.
|
|
12
|
-
</p>
|
|
13
|
-
<p>
|
|
14
|
-
The tests make two updates to the same data, but they occur in random
|
|
15
|
-
order. The "species" field is then logged and should match the update that
|
|
16
|
-
happened last.
|
|
17
|
-
</p>
|
|
18
|
-
<p>
|
|
19
|
-
The <code>index.js</code> file is loaded as a module, which allows it to
|
|
20
|
-
import the development build of Holster. You can switch to the production
|
|
21
|
-
build by changing the import path at the top of the file.
|
|
22
|
-
</p>
|
|
23
|
-
<p>
|
|
24
|
-
See <a href="/examples/user.html">user.html</a> for examples that test the
|
|
25
|
-
Holster.user API.
|
|
26
|
-
</p>
|
|
27
|
-
<p>
|
|
28
|
-
See <a href="/examples/wire.html">wire.html</a> for examples that test the
|
|
29
|
-
Holster.wire API.
|
|
30
|
-
</p>
|
|
31
|
-
<script type="module" src="/examples/index.js"></script>
|
|
32
|
-
</body>
|
|
33
|
-
</html>
|
package/examples/index.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// Switch src to build for the production version.
|
|
2
|
-
import Holster from "../src/holster.js"
|
|
3
|
-
|
|
4
|
-
// Set readFromDisk to true to force reading from disk.
|
|
5
|
-
// (Restart the server so that graph is not in memory.)
|
|
6
|
-
const readFromDisk = false
|
|
7
|
-
|
|
8
|
-
const holster = Holster({indexedDB: !readFromDisk})
|
|
9
|
-
globalThis.holster = holster
|
|
10
|
-
|
|
11
|
-
if (readFromDisk) {
|
|
12
|
-
// Wait for the websocket to connect.
|
|
13
|
-
setTimeout(() => {
|
|
14
|
-
holster.get("mark").next("boss", {".": "species"}, data => {
|
|
15
|
-
console.log("read from disk:", data)
|
|
16
|
-
})
|
|
17
|
-
}, 1000)
|
|
18
|
-
} else {
|
|
19
|
-
// Create two updates that could happen in either order.
|
|
20
|
-
setTimeout(() => {
|
|
21
|
-
const update1 = {
|
|
22
|
-
name: "Mark Nadal",
|
|
23
|
-
boss: {
|
|
24
|
-
name: "Fluffy",
|
|
25
|
-
species: "a kitty",
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
holster.get("mark").put(update1, err => console.log("update1:", err))
|
|
29
|
-
}, 1000 * Math.random())
|
|
30
|
-
|
|
31
|
-
setTimeout(() => {
|
|
32
|
-
const update2 = {
|
|
33
|
-
name: "Mark",
|
|
34
|
-
boss: {
|
|
35
|
-
species: "felis silvestris",
|
|
36
|
-
color: "ginger",
|
|
37
|
-
},
|
|
38
|
-
}
|
|
39
|
-
holster.get("mark").put(update2, err => console.log("update2:", err))
|
|
40
|
-
}, 1000 * Math.random())
|
|
41
|
-
|
|
42
|
-
// The API sets the state on properties to the current time, so the result
|
|
43
|
-
// here depends on which update was called first.
|
|
44
|
-
setTimeout(() => {
|
|
45
|
-
holster.get("mark").next("boss", {".": "species"}, data => {
|
|
46
|
-
console.log("get:", data)
|
|
47
|
-
})
|
|
48
|
-
}, 2000)
|
|
49
|
-
}
|
package/examples/user.html
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<title>Holster.user - test examples</title>
|
|
6
|
-
</head>
|
|
7
|
-
<body>
|
|
8
|
-
<h2>Holster.user - test examples</h2>
|
|
9
|
-
<p>
|
|
10
|
-
This file loads <code>user.js</code> which contains examples that test the
|
|
11
|
-
Holster.user API. See the console log for test output.
|
|
12
|
-
</p>
|
|
13
|
-
<p>
|
|
14
|
-
The tests create two users, Alice and Bob. They both write data to their
|
|
15
|
-
own user space. Bob writes secret data that can only be read by Alice,
|
|
16
|
-
then Alice logs in again to read the message.
|
|
17
|
-
</p>
|
|
18
|
-
<p>
|
|
19
|
-
These tests also show how to use the SEA API (Security, Encryption, and
|
|
20
|
-
Authorization), which can be loaded on it's own, but it made available via
|
|
21
|
-
holster.SEA. The holster.user API uses the SEA API internally for creating
|
|
22
|
-
and authenticating users.
|
|
23
|
-
</p>
|
|
24
|
-
<p>
|
|
25
|
-
The <code>user.js</code> file is loaded as a module, which allows it to
|
|
26
|
-
import the development build of Holster. You can switch to the production
|
|
27
|
-
build by changing the import path at the top of the file.
|
|
28
|
-
</p>
|
|
29
|
-
<script type="module" src="./user.js"></script>
|
|
30
|
-
</body>
|
|
31
|
-
</html>
|
package/examples/user.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// Switch src to build for the production version.
|
|
2
|
-
import Holster from "../src/holster.js"
|
|
3
|
-
|
|
4
|
-
const holster = Holster({indexedDB: true})
|
|
5
|
-
globalThis.holster = holster
|
|
6
|
-
|
|
7
|
-
const user = holster.user()
|
|
8
|
-
|
|
9
|
-
let alice, bob
|
|
10
|
-
// Wait for the websocket to connect.
|
|
11
|
-
setTimeout(() => {
|
|
12
|
-
user.create("alice", "password", err => {
|
|
13
|
-
if (err && err !== "Username already exists") {
|
|
14
|
-
console.log(err)
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
user.auth("alice", "password", err => {
|
|
19
|
-
if (err) {
|
|
20
|
-
console.log(err)
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Store Alice's encryption key so Bob can use it to create shared data.
|
|
25
|
-
alice = {epub: user.is.epub}
|
|
26
|
-
console.log("Alice logged in.", user.is)
|
|
27
|
-
|
|
28
|
-
const data = "Success! Only Alice can put data on their account"
|
|
29
|
-
user.get("protected").put(data, err => {
|
|
30
|
-
if (err) {
|
|
31
|
-
console.log(err)
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
user.get("protected", console.log)
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
}, 1000)
|
|
40
|
-
|
|
41
|
-
setTimeout(() => {
|
|
42
|
-
user.create("bob", "stronger password", err => {
|
|
43
|
-
if (err && err !== "Username already exists") {
|
|
44
|
-
console.log(err)
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
user.auth("bob", "stronger password", async err => {
|
|
49
|
-
if (err) {
|
|
50
|
-
console.log(err)
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Store Bob's public keys so Alice can use them to read shared data.
|
|
55
|
-
bob = {pub: user.is.pub, epub: user.is.epub}
|
|
56
|
-
console.log("Bob logged in.", user.is)
|
|
57
|
-
|
|
58
|
-
// For this test let's create shared private data for Alice.
|
|
59
|
-
const secret = await holster.SEA.secret(alice, user.is)
|
|
60
|
-
const shared = await holster.SEA.encrypt("Hello Alice!", secret)
|
|
61
|
-
user.get("shared").put(shared, err => {
|
|
62
|
-
if (err) {
|
|
63
|
-
console.log(err)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
console.log("Success! Bob wrote secret data to his account.")
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
}, 2000)
|
|
72
|
-
|
|
73
|
-
setTimeout(() => {
|
|
74
|
-
if (!bob) {
|
|
75
|
-
console.log("Something went wrong, bob didn't log in.")
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Use Bob's public key to fetch shared data under his account.
|
|
80
|
-
user.get([bob.pub, "shared"], enc => {
|
|
81
|
-
user.auth("alice", "password", async err => {
|
|
82
|
-
if (err) {
|
|
83
|
-
console.log(err)
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
console.log("Alice logged in again.", user.is)
|
|
88
|
-
const secret = await holster.SEA.secret(bob, user.is)
|
|
89
|
-
const shared = await holster.SEA.decrypt(enc, secret)
|
|
90
|
-
console.log("Alice's shared data from Bob:", shared)
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
}, 3000)
|
package/examples/wire.html
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<title>Holster.wire - test examples</title>
|
|
6
|
-
</head>
|
|
7
|
-
<body>
|
|
8
|
-
<h2>Holster.wire - test examples</h2>
|
|
9
|
-
<p>
|
|
10
|
-
This file loads <code>wire.js</code> which contains examples that use the
|
|
11
|
-
Holster.wire API. These examples show how conflict resolution works, see
|
|
12
|
-
the console log for test output.
|
|
13
|
-
</p>
|
|
14
|
-
<p>
|
|
15
|
-
These tests also make two updates in random order, but the wire API can
|
|
16
|
-
set state vectors on updates to control which value should be applied.
|
|
17
|
-
This means when the "species" field is logged it will always be the same
|
|
18
|
-
no matter which order the tests run in.
|
|
19
|
-
</p>
|
|
20
|
-
<p>
|
|
21
|
-
The <code>wire.js</code> file is loaded as a module, which allows it to
|
|
22
|
-
import the development build of Holster. You can switch to the production
|
|
23
|
-
build by changing the import path at the top of the file.
|
|
24
|
-
</p>
|
|
25
|
-
<script type="module" src="./wire.js"></script>
|
|
26
|
-
</body>
|
|
27
|
-
</html>
|
package/examples/wire.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// Switch src to build for the production version.
|
|
2
|
-
import Holster from "../src/holster.js"
|
|
3
|
-
|
|
4
|
-
// Set readFromDisk to true to force reading from disk.
|
|
5
|
-
// (Restart the server so that graph is not in memory.)
|
|
6
|
-
const readFromDisk = false
|
|
7
|
-
|
|
8
|
-
const holster = Holster({indexedDB: !readFromDisk})
|
|
9
|
-
globalThis.holster = holster
|
|
10
|
-
|
|
11
|
-
if (readFromDisk) {
|
|
12
|
-
// Wait for the websocket to connect.
|
|
13
|
-
setTimeout(() => {
|
|
14
|
-
holster.wire.get({"#": "FDSA", ".": "species"}, msg => {
|
|
15
|
-
console.log("read from disk:", msg)
|
|
16
|
-
})
|
|
17
|
-
}, 1000)
|
|
18
|
-
} else {
|
|
19
|
-
// Create two updates that could happen in either order.
|
|
20
|
-
setTimeout(() => {
|
|
21
|
-
const update1 = {
|
|
22
|
-
ASDF: {
|
|
23
|
-
_: {"#": "ASDF", ">": {name: 2, boss: 2}},
|
|
24
|
-
name: "Mark Nadal",
|
|
25
|
-
boss: {"#": "FDSA"},
|
|
26
|
-
},
|
|
27
|
-
FDSA: {
|
|
28
|
-
_: {"#": "FDSA", ">": {name: 2, species: 2, slave: 2}},
|
|
29
|
-
name: "Fluffy",
|
|
30
|
-
species: "a kitty",
|
|
31
|
-
slave: {"#": "ASDF"},
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
holster.wire.put(update1, msg => console.log("update1:", msg))
|
|
35
|
-
}, 1000 * Math.random())
|
|
36
|
-
|
|
37
|
-
setTimeout(() => {
|
|
38
|
-
const update2 = {
|
|
39
|
-
ASDF: {_: {"#": "ASDF", ">": {name: 1}}, name: "Mark"},
|
|
40
|
-
FDSA: {
|
|
41
|
-
_: {"#": "FDSA", ">": {species: 2, color: 3}},
|
|
42
|
-
species: "felis silvestris",
|
|
43
|
-
color: "ginger",
|
|
44
|
-
},
|
|
45
|
-
}
|
|
46
|
-
holster.wire.put(update2, msg => console.log("update2:", msg))
|
|
47
|
-
}, 1000 * Math.random())
|
|
48
|
-
|
|
49
|
-
// They should always produce the same result no matter which order.
|
|
50
|
-
setTimeout(() => {
|
|
51
|
-
holster.wire.get({"#": "FDSA", ".": "species"}, msg => {
|
|
52
|
-
console.log("get:", msg)
|
|
53
|
-
})
|
|
54
|
-
}, 2000)
|
|
55
|
-
}
|
package/test/holster.get.test.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import fs from "fs"
|
|
2
|
-
import {Server} from "mock-socket"
|
|
3
|
-
import {describe, test} from "node:test"
|
|
4
|
-
import assert from "node:assert/strict"
|
|
5
|
-
import Holster from "../src/holster.js"
|
|
6
|
-
|
|
7
|
-
describe("holster.get", () => {
|
|
8
|
-
const wss = new Server("ws://localhost:1234")
|
|
9
|
-
const holster = Holster({file: "test/holster.get", wss: wss, maxAge: 100})
|
|
10
|
-
|
|
11
|
-
test("empty key callback null", (t, done) => {
|
|
12
|
-
holster.get("", data => {
|
|
13
|
-
assert.equal(data, null)
|
|
14
|
-
done()
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test("null key callback null", (t, done) => {
|
|
19
|
-
holster.get(null, data => {
|
|
20
|
-
assert.equal(data, null)
|
|
21
|
-
done()
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test("underscore as key callback null", (t, done) => {
|
|
26
|
-
holster.get("_", data => {
|
|
27
|
-
assert.equal(data, null)
|
|
28
|
-
done()
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test("get unknown key callback null", (t, done) => {
|
|
33
|
-
holster.get("unknown", data => {
|
|
34
|
-
assert.equal(data, null)
|
|
35
|
-
done()
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test("get unknown keys in for loop callbacks null", (t, done) => {
|
|
40
|
-
let count = 0
|
|
41
|
-
for (let i = 0; i < 5; i++) {
|
|
42
|
-
holster.get("unknown" + i, data => {
|
|
43
|
-
assert.equal(data, null)
|
|
44
|
-
count++
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
setTimeout(() => {
|
|
48
|
-
if (count === 5) done()
|
|
49
|
-
}, 200)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
test("nested unknown keys both callbacks null", (t, done) => {
|
|
53
|
-
holster.get("unknown", data => {
|
|
54
|
-
assert.equal(data, null)
|
|
55
|
-
|
|
56
|
-
holster.get("unknown", data => {
|
|
57
|
-
assert.equal(data, null)
|
|
58
|
-
done()
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
test("get already called callback null", (t, done) => {
|
|
64
|
-
holster.get("chained").get("unknown", data => {
|
|
65
|
-
assert.equal(data, null)
|
|
66
|
-
done()
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test("next chained unknown keys callback null", (t, done) => {
|
|
71
|
-
holster.get("chained").next("unknown", data => {
|
|
72
|
-
assert.equal(data, null)
|
|
73
|
-
done()
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
test("nested next chained unknown keys callback null", (t, done) => {
|
|
78
|
-
holster.get("chained").next("unknown", data => {
|
|
79
|
-
assert.equal(data, null)
|
|
80
|
-
|
|
81
|
-
holster.get("chained").next("unknown", data => {
|
|
82
|
-
assert.equal(data, null)
|
|
83
|
-
done()
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test("cleanup", (t, done) => {
|
|
89
|
-
fs.rm("test/holster.get", {recursive: true, force: true}, err => {
|
|
90
|
-
assert.equal(err, null)
|
|
91
|
-
done()
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
})
|
package/test/holster.lex.test.js
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import fs from "fs"
|
|
2
|
-
import {Server} from "mock-socket"
|
|
3
|
-
import {describe, test} from "node:test"
|
|
4
|
-
import assert from "node:assert/strict"
|
|
5
|
-
import Holster from "../src/holster.js"
|
|
6
|
-
|
|
7
|
-
describe("holster.lex", () => {
|
|
8
|
-
const wss = new Server("ws://localhost:1234")
|
|
9
|
-
const holster = Holster({file: "test/holster.lex", wss: wss, maxAge: 100})
|
|
10
|
-
|
|
11
|
-
test("object on root in graph format", (t, done) => {
|
|
12
|
-
const plain = {
|
|
13
|
-
key: "plain value",
|
|
14
|
-
true: true,
|
|
15
|
-
false: false,
|
|
16
|
-
number: 42,
|
|
17
|
-
}
|
|
18
|
-
holster.get("plain").put(plain, err => {
|
|
19
|
-
assert.equal(err, null)
|
|
20
|
-
|
|
21
|
-
// Prefix tests. (Need to be sequential to match return values.)
|
|
22
|
-
holster.get("plain", {".": {"*": "k"}}, data => {
|
|
23
|
-
assert.deepEqual(data, {key: "plain value"})
|
|
24
|
-
|
|
25
|
-
holster.get("plain", {".": {"*": "t"}}, data => {
|
|
26
|
-
assert.deepEqual(data, {true: true})
|
|
27
|
-
|
|
28
|
-
holster.get("plain", {".": {"*": "f"}}, data => {
|
|
29
|
-
assert.deepEqual(data, {false: false})
|
|
30
|
-
|
|
31
|
-
holster.get("plain", {".": {"*": "n"}}, data => {
|
|
32
|
-
assert.deepEqual(data, {number: 42})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// Both less than and greater than.
|
|
36
|
-
holster.get("plain", {".": {"<": "n", ">": "falsy"}}, data => {
|
|
37
|
-
assert.deepEqual(data, {key: "plain value"})
|
|
38
|
-
|
|
39
|
-
// Only less than.
|
|
40
|
-
holster.get("plain", {".": {"<": "k"}}, data => {
|
|
41
|
-
assert.deepEqual(data, {false: false})
|
|
42
|
-
|
|
43
|
-
// Only greater than.
|
|
44
|
-
holster.get("plain", {".": {">": "numbers"}}, data => {
|
|
45
|
-
assert.deepEqual(data, {true: true})
|
|
46
|
-
done()
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test("put and get nested object", (t, done) => {
|
|
57
|
-
const nested = {
|
|
58
|
-
key: "nested value",
|
|
59
|
-
other: "other value",
|
|
60
|
-
child: {
|
|
61
|
-
has: "child value",
|
|
62
|
-
other: "other child value",
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
holster.get("nested").put(nested, err => {
|
|
66
|
-
assert.equal(err, null)
|
|
67
|
-
|
|
68
|
-
// Getting a nested object requires waiting for radisk to write to disk,
|
|
69
|
-
// as it will batch the writes. (Default wait is 1 millisecond.)
|
|
70
|
-
setTimeout(() => {
|
|
71
|
-
holster.get("nested", {".": {"*": "k"}}, data => {
|
|
72
|
-
assert.deepEqual(data, {key: "nested value"})
|
|
73
|
-
|
|
74
|
-
holster.get("nested").next("child", {".": {"*": "h"}}, data => {
|
|
75
|
-
assert.deepEqual(data, {has: "child value"})
|
|
76
|
-
|
|
77
|
-
holster.get("nested").next("child", {".": {"<": "has"}}, data => {
|
|
78
|
-
// Less than means less than or equal to in lex.
|
|
79
|
-
assert.deepEqual(data, {has: "child value"})
|
|
80
|
-
done()
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
}, 2)
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test("chained get before put nested object", (t, done) => {
|
|
89
|
-
const nested = {
|
|
90
|
-
key: "hello nested value",
|
|
91
|
-
other: "other value",
|
|
92
|
-
child: {
|
|
93
|
-
has: "hello child value",
|
|
94
|
-
other: "other child value",
|
|
95
|
-
},
|
|
96
|
-
}
|
|
97
|
-
holster
|
|
98
|
-
.get("hello")
|
|
99
|
-
.next("nested")
|
|
100
|
-
.put(nested, err => {
|
|
101
|
-
assert.equal(err, null)
|
|
102
|
-
|
|
103
|
-
setTimeout(() => {
|
|
104
|
-
holster.get("hello").next("nested", {".": {"*": "k"}}, data => {
|
|
105
|
-
assert.deepEqual(data, {key: "hello nested value"})
|
|
106
|
-
|
|
107
|
-
holster
|
|
108
|
-
.get("hello")
|
|
109
|
-
.next("nested")
|
|
110
|
-
.next("child", {".": {"*": "h"}}, data => {
|
|
111
|
-
assert.deepEqual(data, {has: "hello child value"})
|
|
112
|
-
|
|
113
|
-
holster
|
|
114
|
-
.get("hello")
|
|
115
|
-
.next("nested")
|
|
116
|
-
.next("child", {".": {">": "other"}}, data => {
|
|
117
|
-
// Greater than means greater than or equal to in lex.
|
|
118
|
-
assert.deepEqual(data, {other: "other child value"})
|
|
119
|
-
done()
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
}, 2)
|
|
124
|
-
})
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
test("put and get two nested objects", (t, done) => {
|
|
128
|
-
const two = {
|
|
129
|
-
key: "two nested values",
|
|
130
|
-
child1: {
|
|
131
|
-
has: "child value 1",
|
|
132
|
-
},
|
|
133
|
-
child2: {
|
|
134
|
-
has: "child value 2",
|
|
135
|
-
},
|
|
136
|
-
}
|
|
137
|
-
holster.get("two").put(two, err => {
|
|
138
|
-
assert.equal(err, null)
|
|
139
|
-
|
|
140
|
-
setTimeout(() => {
|
|
141
|
-
holster.get("two", {".": {"*": "child"}}, data => {
|
|
142
|
-
assert.deepEqual(data, {
|
|
143
|
-
child1: two.child1,
|
|
144
|
-
child2: two.child2,
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
holster
|
|
148
|
-
.get("two")
|
|
149
|
-
.next("child1", {".": {">": "g", "<": "i"}}, data => {
|
|
150
|
-
assert.deepEqual(data, {has: "child value 1"})
|
|
151
|
-
done()
|
|
152
|
-
})
|
|
153
|
-
})
|
|
154
|
-
}, 2)
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test("on and put nested object", (t, done) => {
|
|
159
|
-
const nested = {
|
|
160
|
-
key: "nested key",
|
|
161
|
-
child: {
|
|
162
|
-
has: "child key",
|
|
163
|
-
},
|
|
164
|
-
}
|
|
165
|
-
// The node needs to exist before it can be listend to for updates.
|
|
166
|
-
holster.get("on-nested").put(nested, err => {
|
|
167
|
-
assert.equal(err, null)
|
|
168
|
-
|
|
169
|
-
const update = {
|
|
170
|
-
child: {
|
|
171
|
-
has: "child update",
|
|
172
|
-
},
|
|
173
|
-
}
|
|
174
|
-
holster
|
|
175
|
-
.get("on-nested")
|
|
176
|
-
.next("child")
|
|
177
|
-
.on({".": {">": "h"}}, data => {
|
|
178
|
-
assert.deepEqual(data, update.child)
|
|
179
|
-
done()
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
setTimeout(() => {
|
|
183
|
-
holster.get("on-nested").put(update, err => {
|
|
184
|
-
assert.equal(err, null)
|
|
185
|
-
})
|
|
186
|
-
}, 10)
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
test("cleanup", (t, done) => {
|
|
191
|
-
fs.rm("test/holster.lex", {recursive: true, force: true}, err => {
|
|
192
|
-
assert.equal(err, null)
|
|
193
|
-
done()
|
|
194
|
-
})
|
|
195
|
-
})
|
|
196
|
-
})
|