@mblaney/holster 1.0.4 → 1.0.5

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": "@mblaney/holster",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Holster is a service that allows synchronising data between devices using Node, Deno, Bun or the browser.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/radisk.js CHANGED
@@ -119,7 +119,6 @@ const Radisk = opt => {
119
119
  // 4. Read the previous file into memory
120
120
  // 5. Scan through in memory radix for all values lexically less than limit
121
121
  // 6. Merge and write all of those to the in-memory file and back to disk
122
- // 7. If file is to large then split. More details needed here
123
122
  radisk.save = (rad, cb) => {
124
123
  const save = {
125
124
  find: (tree, key) => {
@@ -176,70 +175,47 @@ const Radisk = opt => {
176
175
  cache = null
177
176
  const write = {
178
177
  text: "",
178
+ limit: "",
179
+ done: false,
179
180
  count: 0,
180
- file: file,
181
181
  each: (value, key, k, pre) => {
182
+ // each is called for all keys, but stop adding to write.text when
183
+ // write.slice is called, then the current contents of write.text
184
+ // will be written to file.
185
+ if (write.done) return
186
+
182
187
  write.count++
183
- var enc =
184
- Radisk.encode(pre.length) +
185
- "#" +
186
- Radisk.encode(k) +
187
- (typeof value === "undefined" ? "" : "=" + Radisk.encode(value)) +
188
- "\n"
188
+ value = typeof value === "undefined" ? "" : "=" + Radisk.encode(value)
189
+ const enc =
190
+ Radisk.encode(pre.length) + "#" + Radisk.encode(k) + value + "\n"
189
191
  // Cannot split the file if only have one entry to write. Also don't
190
192
  // start a split if in the middle of writing a node (pre > 0).
191
193
  if (
192
- pre.length === 0 &&
193
194
  write.count > 1 &&
195
+ pre.length === 0 &&
194
196
  write.text.length + enc.length > opt.size
195
197
  ) {
196
- write.text = ""
197
- // Otherwise split the entries in half.
198
- write.limit = Math.ceil(write.count / 2)
199
- write.count = 0
198
+ const end = k.indexOf(enq)
199
+ write.limit = end === -1 ? k : k.substring(0, end)
200
+ write.done = true
200
201
  write.sub = Radix()
201
202
  Radix.map(rad, write.slice)
202
- return true
203
+ radisk.write(write.limit, write.sub, cb)
204
+ return
203
205
  }
204
206
 
205
207
  write.text += enc
206
208
  },
207
- put: () => {
208
- opt.store.put(file, write.text, cb)
209
- },
210
209
  slice: (value, key) => {
211
- if (key < write.file) return
212
-
213
- if (++write.count > write.limit) {
214
- var name = write.file
215
- // Use only the soul of the key as the filename so that all
216
- // properties of a soul are written to the same file.
217
- let end = key.indexOf(enq)
218
- if (end === -1) {
219
- write.file = key
220
- } else {
221
- write.file = key.substring(0, end)
222
- }
223
- write.count = 0
224
- radisk.write(name, write.sub, write.next)
225
- return true
226
- }
210
+ if (key < write.limit) return
227
211
 
228
212
  write.sub(key, value)
229
213
  },
230
- next: err => {
231
- if (err) return cb(err)
232
-
233
- write.sub = Radix()
234
- if (!Radix.map(rad, write.slice)) {
235
- radisk.write(write.file, write.sub, cb)
236
- }
237
- },
238
214
  }
239
- // If Radix.map doesn't return true when called with write.each as a
240
- // callback then didn't need to split the data. The accumulated write.text
241
- // can then be stored with write.put().
242
- if (!Radix.map(rad, write.each, true)) write.put()
215
+ Radix.map(rad, write.each, true)
216
+ // There is always accumulated write.text to store once write.each has
217
+ // finished.
218
+ opt.store.put(file, write.text, cb)
243
219
  }
244
220
 
245
221
  radisk.read = (key, cb) => {
@@ -248,11 +224,8 @@ const Radisk = opt => {
248
224
  if (typeof value !== "undefined") return cb(u, value)
249
225
  }
250
226
  // Only the soul of the key is compared to filenames (see radisk.write).
251
- let soul = key
252
- let end = key.indexOf(enq)
253
- if (end !== -1) {
254
- soul = key.substring(0, end)
255
- }
227
+ const end = key.indexOf(enq)
228
+ const soul = end === -1 ? key : key.substring(0, end)
256
229
 
257
230
  const read = {
258
231
  lex: file => {
@@ -1,8 +1,6 @@
1
1
  import {describe, test} from "node:test"
2
2
  import assert from "node:assert/strict"
3
3
  import Radisk from "../src/radisk.js"
4
- import Names from "./names.js"
5
- const names = Names()
6
4
 
7
5
  describe("radisk", () => {
8
6
  const puts = {}
@@ -175,8 +173,8 @@ describe("radisk", () => {
175
173
  "!":
176
174
  '\x1F+0\x1F#\x1F"key\x1F=\x1F"value\ncontinued\x0312345\x1F\n' +
177
175
  '\x1F+1\x1F#\x1F"A\x1F=\x1F"valueA\x1F\n' +
178
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
179
- keyC: '\x1F+0\x1F#\x1F"keyC\x1F=\x1F"valueC\x1F\n',
176
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
177
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
180
178
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"' + big + "\x1F\n",
181
179
  })
182
180
  radisk("newFile", (err, value) => {
@@ -198,8 +196,8 @@ describe("radisk", () => {
198
196
  "!":
199
197
  '\x1F+0\x1F#\x1F"key\x1F=\x1F"value\ncontinued\x0312345\x1F\n' +
200
198
  '\x1F+1\x1F#\x1F"A\x1F=\x1F"valueA\x1F\n' +
201
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
202
- keyC: '\x1F+0\x1F#\x1F"keyC\x1F=\x1F"valueC\x1F\n',
199
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
200
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
203
201
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"' + big + "\x1F\n",
204
202
  small: '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n',
205
203
  })
@@ -220,8 +218,8 @@ describe("radisk", () => {
220
218
  "!":
221
219
  '\x1F+0\x1F#\x1F"key\x1F=\x1F"value\ncontinued\x0312345\x1F\n' +
222
220
  '\x1F+1\x1F#\x1F"A\x1F=\x1F"valueA\x1F\n' +
223
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
224
- keyC: '\x1F+0\x1F#\x1F"keyC\x1F=\x1F"valueC\x1F\n',
221
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
222
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
225
223
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
226
224
  small: '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n',
227
225
  })
@@ -244,8 +242,8 @@ describe("radisk", () => {
244
242
  '\x1F+1\x1F#\x1F"A\x1F=\x1F"' +
245
243
  big +
246
244
  "\x1F\n" +
247
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
248
- keyC: '\x1F+0\x1F#\x1F"keyC\x1F=\x1F"valueC\x1F\n',
245
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
246
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
249
247
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
250
248
  small: '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n',
251
249
  })
@@ -265,8 +263,8 @@ describe("radisk", () => {
265
263
  '\x1F+1\x1F#\x1F"A\x1F=\x1F"' +
266
264
  big +
267
265
  "\x1F\n" +
268
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
269
- keyC: '\x1F+0\x1F#\x1F"keyC\x1F=\x1F"valueC\x1F\n',
266
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
267
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
270
268
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
271
269
  small:
272
270
  '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n' +
@@ -292,9 +290,8 @@ describe("radisk", () => {
292
290
  '\x1F+1\x1F#\x1F"A\x1F=\x1F"' +
293
291
  big +
294
292
  "\x1F\n" +
295
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
296
- keyC:
297
- '\x1F+0\x1F#\x1F"key\x1F\n\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
293
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
294
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
298
295
  '\x1F+1\x1F#\x1F"D\x1F=\x1F"valueD\x1F\n',
299
296
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
300
297
  small:
@@ -319,9 +316,8 @@ describe("radisk", () => {
319
316
  "!":
320
317
  '\x1F+0\x1F#\x1F"key\x1F=\x1F \x1F\n' +
321
318
  '\x1F+1\x1F#\x1F"A\x1F=\x1F \x1F\n' +
322
- '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n',
323
- keyC:
324
- '\x1F+0\x1F#\x1F"key\x1F\n\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
319
+ '\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
320
+ '\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
325
321
  '\x1F+1\x1F#\x1F"D\x1F=\x1F"valueD\x1F\n',
326
322
  newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
327
323
  small:
@@ -0,0 +1,317 @@
1
+ import {describe, test} from "node:test"
2
+ import assert from "node:assert/strict"
3
+ import Radisk from "../src/radisk.js"
4
+
5
+ // ASCII character for enquiry.
6
+ const enq = String.fromCharCode(5)
7
+
8
+ describe("split", () => {
9
+ const puts = {}
10
+ const opt = {
11
+ size: 100,
12
+ store: {
13
+ get: (file, cb) => {
14
+ cb(null, puts[file])
15
+ },
16
+ put: (file, data, cb) => {
17
+ puts[file] = data
18
+ cb(null)
19
+ },
20
+ list: cb => {
21
+ Object.keys(puts).sort().forEach(cb)
22
+ cb(null)
23
+ },
24
+ },
25
+ }
26
+ const radisk = Radisk(opt)
27
+ const now = Date.now()
28
+ const lorem =
29
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
30
+ const parent = () => {
31
+ return (
32
+ '\x1F+0\x1F#\x1F"parent\x05\x1F\n' +
33
+ '\x1F+1\x1F#\x1F"boolean\x1F=\x1F+\x03' +
34
+ now +
35
+ "\x1F\n" +
36
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+42\x03' +
37
+ now +
38
+ "\x1F\n" +
39
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#child\x03' +
40
+ now +
41
+ "\x1F\n" +
42
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"parent value\x03' +
43
+ now +
44
+ "\x1F\n"
45
+ )
46
+ }
47
+ const child = () => {
48
+ return (
49
+ '\x1F+0\x1F#\x1F"child\x05\x1F\n' +
50
+ '\x1F+1\x1F#\x1F"boolean\x1F=\x1F+\x03' +
51
+ now +
52
+ "\x1F\n" +
53
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+43\x03' +
54
+ now +
55
+ "\x1F\n" +
56
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#grandchild\x03' +
57
+ now +
58
+ "\x1F\n" +
59
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"child value\x03' +
60
+ now +
61
+ "\x1F\n"
62
+ )
63
+ }
64
+ const grandchild = () => {
65
+ return (
66
+ '\x1F+0\x1F#\x1F"grandchild\x05\x1F\n' +
67
+ '\x1F+1\x1F#\x1F"boolean\x1F=\x1F-\x03' +
68
+ now +
69
+ "\x1F\n" +
70
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+44\x03' +
71
+ now +
72
+ "\x1F\n" +
73
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#toys\x03' +
74
+ now +
75
+ "\x1F\n" +
76
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"grandchild value\x03' +
77
+ now +
78
+ "\x1F\n"
79
+ )
80
+ }
81
+ const toys = () => {
82
+ return (
83
+ '\x1F+0\x1F#\x1F"toys\x05\x1F\n' +
84
+ '\x1F+1\x1F#\x1F"b\x1F\n' +
85
+ '\x1F+2\x1F#\x1F"all\x1F=\x1F"green\x03' +
86
+ now +
87
+ "\x1F\n" +
88
+ '\x1F+2\x1F#\x1F"ook\x1F=\x1F"' +
89
+ lorem +
90
+ "\x03" +
91
+ now +
92
+ "\x1F\n" +
93
+ '\x1F+1\x1F#\x1F"car\x1F=\x1F"red\x03' +
94
+ now +
95
+ "\x1F\n"
96
+ )
97
+ }
98
+ const alpha = () => {
99
+ return (
100
+ '\x1F+0\x1F#\x1F"alpha\x05\x1F\n' +
101
+ '\x1F+1\x1F#\x1F"boolean\x1F=\x1F+\x03' +
102
+ now +
103
+ "\x1F\n" +
104
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+1\x03' +
105
+ now +
106
+ "\x1F\n" +
107
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#beta\x03' +
108
+ now +
109
+ "\x1F\n" +
110
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"alpha value\x03' +
111
+ now +
112
+ "\x1F\n"
113
+ )
114
+ }
115
+
116
+ test("write a parent node", (t, done) => {
117
+ radisk("parent" + enq + "string", ["parent value", now])
118
+ radisk("parent" + enq + "number", [42, now])
119
+ radisk("parent" + enq + "boolean", [true, now])
120
+ radisk("parent" + enq + "rel", [{"#": "child"}, now])
121
+
122
+ setTimeout(() => {
123
+ assert.deepEqual(puts, {
124
+ "!": parent(),
125
+ })
126
+ done()
127
+ }, 10)
128
+ })
129
+
130
+ test("write a child node", (t, done) => {
131
+ radisk("child" + enq + "string", ["child value", now])
132
+ radisk("child" + enq + "number", [43, now])
133
+ radisk("child" + enq + "boolean", [true, now])
134
+ radisk("child" + enq + "rel", [{"#": "grandchild"}, now])
135
+
136
+ setTimeout(() => {
137
+ assert.deepEqual(puts, {
138
+ "!": child(),
139
+ parent: parent(),
140
+ })
141
+ done()
142
+ }, 10)
143
+ })
144
+
145
+ test("write a grandchild node", (t, done) => {
146
+ radisk("grandchild" + enq + "string", ["grandchild value", now])
147
+ radisk("grandchild" + enq + "number", [44, now])
148
+ radisk("grandchild" + enq + "boolean", [false, now])
149
+ radisk("grandchild" + enq + "rel", [{"#": "toys"}, now])
150
+
151
+ setTimeout(() => {
152
+ assert.deepEqual(puts, {
153
+ "!": child(),
154
+ parent: parent(),
155
+ grandchild: grandchild(),
156
+ })
157
+ done()
158
+ }, 10)
159
+ })
160
+
161
+ test("write grandchild toys", (t, done) => {
162
+ radisk("toys" + enq + "car", ["red", now])
163
+ radisk("toys" + enq + "ball", ["green", now])
164
+ radisk("toys" + enq + "book", [lorem, now])
165
+
166
+ setTimeout(() => {
167
+ assert.deepEqual(puts, {
168
+ "!": child(),
169
+ parent: parent(),
170
+ grandchild: grandchild(),
171
+ toys: toys(),
172
+ })
173
+ done()
174
+ }, 10)
175
+ })
176
+
177
+ test("write new first node", (t, done) => {
178
+ radisk("alpha" + enq + "string", ["alpha value", now])
179
+ radisk("alpha" + enq + "number", [1, now])
180
+ radisk("alpha" + enq + "boolean", [true, now])
181
+ radisk("alpha" + enq + "rel", [{"#": "beta"}, now])
182
+ radisk("beta" + enq + "string", ["beta value", now])
183
+
184
+ setTimeout(() => {
185
+ assert.deepEqual(puts, {
186
+ "!": alpha(),
187
+ beta:
188
+ '\x1F+0\x1F#\x1F"beta\x05string\x1F=\x1F"beta value\x03' +
189
+ now +
190
+ "\x1F\n" +
191
+ child(),
192
+ parent: parent(),
193
+ grandchild: grandchild(),
194
+ toys: toys(),
195
+ })
196
+ done()
197
+ }, 10)
198
+ })
199
+
200
+ // Note that "beta" remains a separate file here (nodes aren't combined).
201
+ test("write new second node", (t, done) => {
202
+ radisk("be" + enq + "string", ["be value", now])
203
+
204
+ setTimeout(() => {
205
+ assert.deepEqual(puts, {
206
+ "!": alpha(),
207
+ be:
208
+ '\x1F+0\x1F#\x1F"be\x05string\x1F=\x1F"be value\x03' + now + "\x1F\n",
209
+ beta:
210
+ '\x1F+0\x1F#\x1F"beta\x05string\x1F=\x1F"beta value\x03' +
211
+ now +
212
+ "\x1F\n" +
213
+ child(),
214
+ parent: parent(),
215
+ grandchild: grandchild(),
216
+ toys: toys(),
217
+ })
218
+ done()
219
+ }, 10)
220
+ })
221
+
222
+ test("add a parent item", (t, done) => {
223
+ radisk("parent" + enq + "after", ["this was added after", now])
224
+
225
+ setTimeout(() => {
226
+ assert.deepEqual(puts, {
227
+ "!": alpha(),
228
+ be:
229
+ '\x1F+0\x1F#\x1F"be\x05string\x1F=\x1F"be value\x03' + now + "\x1F\n",
230
+ beta:
231
+ '\x1F+0\x1F#\x1F"beta\x05string\x1F=\x1F"beta value\x03' +
232
+ now +
233
+ "\x1F\n" +
234
+ child(),
235
+ grandchild: grandchild(),
236
+ parent:
237
+ '\x1F+0\x1F#\x1F"parent\x05\x1F\n' +
238
+ '\x1F+1\x1F#\x1F"after\x1F=\x1F"this was added after\x03' +
239
+ now +
240
+ "\x1F\n" +
241
+ '\x1F+1\x1F#\x1F"boolean\x1F=\x1F+\x03' +
242
+ now +
243
+ "\x1F\n" +
244
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+42\x03' +
245
+ now +
246
+ "\x1F\n" +
247
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#child\x03' +
248
+ now +
249
+ "\x1F\n" +
250
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"parent value\x03' +
251
+ now +
252
+ "\x1F\n",
253
+ toys: toys(),
254
+ })
255
+ done()
256
+ }, 10)
257
+ })
258
+
259
+ test("add two child items", (t, done) => {
260
+ radisk("child" + enq + "book", [lorem, now])
261
+ radisk("child" + enq + "pi", [3.14159, now])
262
+
263
+ setTimeout(() => {
264
+ assert.deepEqual(puts, {
265
+ "!": alpha(),
266
+ be:
267
+ '\x1F+0\x1F#\x1F"be\x05string\x1F=\x1F"be value\x03' + now + "\x1F\n",
268
+ beta:
269
+ '\x1F+0\x1F#\x1F"beta\x05string\x1F=\x1F"beta value\x03' +
270
+ now +
271
+ "\x1F\n" +
272
+ '\x1F+0\x1F#\x1F"child\x05\x1F\n' +
273
+ '\x1F+1\x1F#\x1F"boo\x1F\n' +
274
+ '\x1F+2\x1F#\x1F"k\x1F=\x1F"' +
275
+ lorem +
276
+ "\x03" +
277
+ now +
278
+ "\x1F\n" +
279
+ '\x1F+2\x1F#\x1F"lean\x1F=\x1F+\x03' +
280
+ now +
281
+ "\x1F\n" +
282
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+43\x03' +
283
+ now +
284
+ "\x1F\n" +
285
+ '\x1F+1\x1F#\x1F"pi\x1F=\x1F+3.14159\x03' +
286
+ now +
287
+ "\x1F\n" +
288
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#grandchild\x03' +
289
+ now +
290
+ "\x1F\n" +
291
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"child value\x03' +
292
+ now +
293
+ "\x1F\n",
294
+ grandchild: grandchild(),
295
+ parent:
296
+ '\x1F+0\x1F#\x1F"parent\x05\x1F\n' +
297
+ '\x1F+1\x1F#\x1F"after\x1F=\x1F"this was added after\x03' +
298
+ now +
299
+ "\x1F\n" +
300
+ '\x1F+1\x1F#\x1F"boolean\x1F=\x1F+\x03' +
301
+ now +
302
+ "\x1F\n" +
303
+ '\x1F+1\x1F#\x1F"number\x1F=\x1F+42\x03' +
304
+ now +
305
+ "\x1F\n" +
306
+ '\x1F+1\x1F#\x1F"rel\x1F=\x1F#child\x03' +
307
+ now +
308
+ "\x1F\n" +
309
+ '\x1F+1\x1F#\x1F"string\x1F=\x1F"parent value\x03' +
310
+ now +
311
+ "\x1F\n",
312
+ toys: toys(),
313
+ })
314
+ done()
315
+ }, 10)
316
+ })
317
+ })