braid-text 0.2.36 → 0.2.37
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/index.js +151 -27
- package/package.json +1 -1
- package/test/test.html +186 -0
package/index.js
CHANGED
|
@@ -13,7 +13,6 @@ let braid_text = {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
let waiting_puts = 0
|
|
16
|
-
let prev_put_p = null
|
|
17
16
|
|
|
18
17
|
let max_encoded_key_size = 240
|
|
19
18
|
|
|
@@ -152,28 +151,15 @@ braid_text.serve = async (req, res, options = {}) => {
|
|
|
152
151
|
|
|
153
152
|
waiting_puts++
|
|
154
153
|
if (braid_text.verbose) console.log(`waiting_puts(after++) = ${waiting_puts}`)
|
|
154
|
+
let done_my_turn = (statusCode, x, statusText, headers) => {
|
|
155
|
+
waiting_puts--
|
|
156
|
+
if (braid_text.verbose) console.log(`waiting_puts(after--) = ${waiting_puts}`)
|
|
157
|
+
my_end(statusCode, x, statusText, headers)
|
|
158
|
+
}
|
|
155
159
|
|
|
156
|
-
let my_prev_put_p = prev_put_p
|
|
157
|
-
let done_my_turn = null
|
|
158
|
-
prev_put_p = new Promise(
|
|
159
|
-
(done) =>
|
|
160
|
-
(done_my_turn = (statusCode, x, statusText, headers) => {
|
|
161
|
-
waiting_puts--
|
|
162
|
-
if (braid_text.verbose) console.log(`waiting_puts(after--) = ${waiting_puts}`)
|
|
163
|
-
my_end(statusCode, x, statusText, headers)
|
|
164
|
-
done()
|
|
165
|
-
})
|
|
166
|
-
)
|
|
167
|
-
let patches = null
|
|
168
|
-
let ee = null
|
|
169
160
|
try {
|
|
170
|
-
patches = await req.patches()
|
|
161
|
+
var patches = await req.patches()
|
|
171
162
|
for (let p of patches) p.content = p.content_text
|
|
172
|
-
} catch (e) { ee = e }
|
|
173
|
-
await my_prev_put_p
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
if (ee) throw ee
|
|
177
163
|
|
|
178
164
|
let body = null
|
|
179
165
|
if (patches[0]?.unit === 'everything') {
|
|
@@ -181,8 +167,20 @@ braid_text.serve = async (req, res, options = {}) => {
|
|
|
181
167
|
patches = null
|
|
182
168
|
}
|
|
183
169
|
|
|
184
|
-
|
|
185
|
-
|
|
170
|
+
if (req.parents) await wait_for_events(
|
|
171
|
+
options.key,
|
|
172
|
+
req.parents,
|
|
173
|
+
resource.actor_seqs,
|
|
174
|
+
// approximation of memory usage for this update
|
|
175
|
+
body ? body.length :
|
|
176
|
+
patches.reduce((a, b) => a + b.range.length + b.content.length, 0),
|
|
177
|
+
options.put_buffer_max_time,
|
|
178
|
+
options.put_buffer_max_space)
|
|
179
|
+
|
|
180
|
+
var {change_count} = await braid_text.put(resource, { peer, version: req.version, parents: req.parents, patches, body, merge_type })
|
|
181
|
+
|
|
182
|
+
if (req.version) got_event(options.key, req.version[0], change_count)
|
|
183
|
+
|
|
186
184
|
res.setHeader("Version", get_current_version())
|
|
187
185
|
|
|
188
186
|
options.put_cb(options.key, resource.val)
|
|
@@ -509,7 +507,7 @@ braid_text.put = async (key, options) => {
|
|
|
509
507
|
|
|
510
508
|
resource.need_defrag = true
|
|
511
509
|
|
|
512
|
-
|
|
510
|
+
var post_commit_updates = []
|
|
513
511
|
|
|
514
512
|
if (options.merge_type != "dt") {
|
|
515
513
|
patches = get_xf_patches(resource.doc, v_before)
|
|
@@ -586,7 +584,7 @@ braid_text.put = async (key, options) => {
|
|
|
586
584
|
x.patches = patches
|
|
587
585
|
}
|
|
588
586
|
if (braid_text.verbose) console.log(`sending: ${JSON.stringify(x)}`)
|
|
589
|
-
|
|
587
|
+
post_commit_updates.push([client, x])
|
|
590
588
|
client.my_last_sent_version = x.version
|
|
591
589
|
}
|
|
592
590
|
} else {
|
|
@@ -597,20 +595,27 @@ braid_text.put = async (key, options) => {
|
|
|
597
595
|
if (braid_text.verbose) console.log(`sending: ${JSON.stringify(x)}`)
|
|
598
596
|
for (let client of resource.simpleton_clients) {
|
|
599
597
|
if (client.my_timeout) continue
|
|
600
|
-
|
|
598
|
+
post_commit_updates.push([client, x])
|
|
601
599
|
client.my_last_sent_version = x.version
|
|
602
600
|
}
|
|
603
601
|
}
|
|
604
602
|
}
|
|
605
603
|
|
|
606
|
-
|
|
604
|
+
var x = {
|
|
607
605
|
version: [og_v],
|
|
608
606
|
parents: og_parents,
|
|
609
607
|
patches: og_patches,
|
|
610
608
|
}
|
|
611
609
|
for (let client of resource.clients) {
|
|
612
|
-
if (!peer || client.peer !== peer)
|
|
610
|
+
if (!peer || client.peer !== peer)
|
|
611
|
+
post_commit_updates.push([client, x])
|
|
613
612
|
}
|
|
613
|
+
|
|
614
|
+
await resource.db_delta(resource.doc.getPatchSince(v_before))
|
|
615
|
+
|
|
616
|
+
for (var [client, x] of post_commit_updates) client.subscribe(x)
|
|
617
|
+
|
|
618
|
+
return { change_count }
|
|
614
619
|
}
|
|
615
620
|
|
|
616
621
|
braid_text.list = async () => {
|
|
@@ -820,6 +825,106 @@ async function file_sync(key, process_delta, get_init) {
|
|
|
820
825
|
}
|
|
821
826
|
}
|
|
822
827
|
|
|
828
|
+
async function wait_for_events(
|
|
829
|
+
key,
|
|
830
|
+
events,
|
|
831
|
+
actor_seqs,
|
|
832
|
+
my_space,
|
|
833
|
+
max_time = 3000,
|
|
834
|
+
max_space = 5 * 1024 * 1024) {
|
|
835
|
+
|
|
836
|
+
if (!wait_for_events.namespaces) wait_for_events.namespaces = {}
|
|
837
|
+
if (!wait_for_events.namespaces[key]) wait_for_events.namespaces[key] = {}
|
|
838
|
+
var ns = wait_for_events.namespaces[key]
|
|
839
|
+
|
|
840
|
+
if (!wait_for_events.space_used) wait_for_events.space_used = 0
|
|
841
|
+
if (wait_for_events.space_used + my_space > max_space) return
|
|
842
|
+
wait_for_events.space_used += my_space
|
|
843
|
+
|
|
844
|
+
var p_done = null
|
|
845
|
+
var p = new Promise(done => p_done = done)
|
|
846
|
+
|
|
847
|
+
var missing = 0
|
|
848
|
+
var on_find = () => {
|
|
849
|
+
missing--
|
|
850
|
+
if (!missing) p_done()
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
for (let event of events) {
|
|
854
|
+
var [actor, seq] = decode_version(event)
|
|
855
|
+
if (actor_seqs?.[actor]?.has(seq)) continue
|
|
856
|
+
missing++
|
|
857
|
+
|
|
858
|
+
if (!ns.actor_seqs) ns.actor_seqs = {}
|
|
859
|
+
if (!ns.actor_seqs[actor]) ns.actor_seqs[actor] = []
|
|
860
|
+
sorted_set_insert(ns.actor_seqs[actor], seq)
|
|
861
|
+
|
|
862
|
+
if (!ns.events) ns.events = {}
|
|
863
|
+
if (!ns.events[event]) ns.events[event] = new Set()
|
|
864
|
+
ns.events[event].add(on_find)
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
if (missing) {
|
|
868
|
+
var t = setTimeout(() => {
|
|
869
|
+
for (let event of events) {
|
|
870
|
+
var [actor, seq] = decode_version(event)
|
|
871
|
+
|
|
872
|
+
var cbs = ns.events[event]
|
|
873
|
+
if (!cbs) continue
|
|
874
|
+
|
|
875
|
+
cbs.delete(on_find)
|
|
876
|
+
if (cbs.size) continue
|
|
877
|
+
|
|
878
|
+
delete ns.events[event]
|
|
879
|
+
|
|
880
|
+
var seqs = ns.actor_seqs[actor]
|
|
881
|
+
if (!seqs) continue
|
|
882
|
+
|
|
883
|
+
sorted_set_delete(seqs, seq)
|
|
884
|
+
if (seqs.length) continue
|
|
885
|
+
|
|
886
|
+
delete ns.actor_seqs[actor]
|
|
887
|
+
}
|
|
888
|
+
p_done()
|
|
889
|
+
}, max_time)
|
|
890
|
+
|
|
891
|
+
await p
|
|
892
|
+
|
|
893
|
+
clearTimeout(t)
|
|
894
|
+
}
|
|
895
|
+
wait_for_events.space_used -= my_space
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
async function got_event(key, event, change_count) {
|
|
899
|
+
var ns = wait_for_events.namespaces?.[key]
|
|
900
|
+
if (!ns) return
|
|
901
|
+
|
|
902
|
+
var [actor, seq] = decode_version(event)
|
|
903
|
+
var base_seq = seq + 1 - change_count
|
|
904
|
+
|
|
905
|
+
var seqs = ns.actor_seqs?.[actor]
|
|
906
|
+
if (!seqs) return
|
|
907
|
+
|
|
908
|
+
// binary search to find the first i >= base_seq
|
|
909
|
+
var i = 0, end = seqs.length
|
|
910
|
+
while (i < end) {
|
|
911
|
+
var mid = (i + end) >> 1
|
|
912
|
+
seqs[mid] < base_seq ? i = mid + 1 : end = mid
|
|
913
|
+
}
|
|
914
|
+
var start = i
|
|
915
|
+
|
|
916
|
+
// iterate up through seq
|
|
917
|
+
while (i < seqs.length && seqs[i] <= seq) {
|
|
918
|
+
var e = actor + "-" + seqs[i]
|
|
919
|
+
ns.events?.[e]?.forEach(cb => cb())
|
|
920
|
+
delete ns.events?.[e]
|
|
921
|
+
i++
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
seqs.splice(start, i - start)
|
|
925
|
+
if (!seqs.length) delete ns.actor_seqs[actor]
|
|
926
|
+
}
|
|
927
|
+
|
|
823
928
|
//////////////////////////////////////////////////////////////////
|
|
824
929
|
//////////////////////////////////////////////////////////////////
|
|
825
930
|
//////////////////////////////////////////////////////////////////
|
|
@@ -1908,6 +2013,25 @@ function ascii_ify(s) {
|
|
|
1908
2013
|
return s.replace(/[^\x20-\x7E]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))
|
|
1909
2014
|
}
|
|
1910
2015
|
|
|
2016
|
+
function sorted_set_find(arr, val) {
|
|
2017
|
+
var left = 0, right = arr.length
|
|
2018
|
+
while (left < right) {
|
|
2019
|
+
var mid = (left + right) >> 1
|
|
2020
|
+
arr[mid] < val ? left = mid + 1 : right = mid
|
|
2021
|
+
}
|
|
2022
|
+
return left
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
function sorted_set_insert(arr, val) {
|
|
2026
|
+
var i = sorted_set_find(arr, val)
|
|
2027
|
+
if (arr[i] !== val) arr.splice(i, 0, val)
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
function sorted_set_delete(arr, val) {
|
|
2031
|
+
var i = sorted_set_find(arr, val)
|
|
2032
|
+
if (arr[i] === val) arr.splice(i, 1)
|
|
2033
|
+
}
|
|
2034
|
+
|
|
1911
2035
|
braid_text.get_resource = get_resource
|
|
1912
2036
|
|
|
1913
2037
|
braid_text.encode_filename = encode_filename
|
package/package.json
CHANGED
package/test/test.html
CHANGED
|
@@ -68,6 +68,192 @@ async function runTest(testName, testFunction, expectedResult) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
runTest(
|
|
72
|
+
"test when PUT cache/buffer size fails",
|
|
73
|
+
async () => {
|
|
74
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
75
|
+
|
|
76
|
+
var f1 = braid_fetch(`/${key}`, {
|
|
77
|
+
method: 'PUT',
|
|
78
|
+
version: ['hi-3000000'],
|
|
79
|
+
parents: ['yo-0'],
|
|
80
|
+
body: 'A'.repeat(3000000)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
await new Promise(done => setTimeout(done, 300))
|
|
84
|
+
|
|
85
|
+
var f2 = braid_fetch(`/${key}`, {
|
|
86
|
+
method: 'PUT',
|
|
87
|
+
version: ['ih-3000000'],
|
|
88
|
+
parents: ['yo-0'],
|
|
89
|
+
body: 'B'.repeat(3000000)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
await new Promise(done => setTimeout(done, 300))
|
|
93
|
+
|
|
94
|
+
var r = await braid_fetch(`/${key}`, {
|
|
95
|
+
method: 'PUT',
|
|
96
|
+
version: ['yo-0'],
|
|
97
|
+
parents: [],
|
|
98
|
+
body: 'x'
|
|
99
|
+
})
|
|
100
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
101
|
+
|
|
102
|
+
return `f1: ${(await f1).status}, f2: ${(await f2).status}`
|
|
103
|
+
},
|
|
104
|
+
'f1: 200, f2: 309'
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
runTest(
|
|
108
|
+
"test multiple patches",
|
|
109
|
+
async () => {
|
|
110
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
111
|
+
|
|
112
|
+
var r = await braid_fetch(`/${key}`, {
|
|
113
|
+
method: 'PUT',
|
|
114
|
+
version: ['hi-0'],
|
|
115
|
+
parents: [],
|
|
116
|
+
body: 'A'
|
|
117
|
+
})
|
|
118
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
119
|
+
|
|
120
|
+
var r = await braid_fetch(`/${key}`, {
|
|
121
|
+
method: 'PUT',
|
|
122
|
+
version: ['yo-1'],
|
|
123
|
+
parents: ['hi-0'],
|
|
124
|
+
patches: [
|
|
125
|
+
{unit: 'text', range: '[0:0]', content: 'C'},
|
|
126
|
+
{unit: 'text', range: '[1:1]', content: 'T'}
|
|
127
|
+
]
|
|
128
|
+
})
|
|
129
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
130
|
+
|
|
131
|
+
var r2 = await braid_fetch(`/${key}`)
|
|
132
|
+
return await r2.text()
|
|
133
|
+
},
|
|
134
|
+
'CAT'
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
runTest(
|
|
138
|
+
"test PUT after subscribing",
|
|
139
|
+
async () => {
|
|
140
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
141
|
+
|
|
142
|
+
var p_done
|
|
143
|
+
var p = new Promise(done => p_done = done)
|
|
144
|
+
|
|
145
|
+
var r = await braid_fetch(`/${key}`, {
|
|
146
|
+
subscribe: true
|
|
147
|
+
})
|
|
148
|
+
r.subscribe(update => {
|
|
149
|
+
if (update.version[0] === 'hi-0')
|
|
150
|
+
p_done(update.patches[0].content_text)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
var r = await braid_fetch(`/${key}`, {
|
|
154
|
+
method: 'PUT',
|
|
155
|
+
version: ['hi-0'],
|
|
156
|
+
parents: [],
|
|
157
|
+
body: 'x'
|
|
158
|
+
})
|
|
159
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
160
|
+
|
|
161
|
+
return await p
|
|
162
|
+
},
|
|
163
|
+
'x'
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
runTest(
|
|
167
|
+
"test out-of-order PUTs",
|
|
168
|
+
async () => {
|
|
169
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
170
|
+
|
|
171
|
+
var f = braid_fetch(`/${key}`, {
|
|
172
|
+
method: 'PUT',
|
|
173
|
+
version: ['hi-1'],
|
|
174
|
+
parents: ['hi-0'],
|
|
175
|
+
patches: [{unit: 'text', range: '[1:1]', content: 'y'}]
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
await new Promise(done => setTimeout(done, 500))
|
|
179
|
+
|
|
180
|
+
var r = await braid_fetch(`/${key}`, {
|
|
181
|
+
method: 'PUT',
|
|
182
|
+
version: ['hi-0'],
|
|
183
|
+
parents: [],
|
|
184
|
+
body: 'x'
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
188
|
+
|
|
189
|
+
r = await f
|
|
190
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
191
|
+
|
|
192
|
+
var r2 = await braid_fetch(`/${key}`)
|
|
193
|
+
return await r2.text()
|
|
194
|
+
},
|
|
195
|
+
'xy'
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
runTest(
|
|
199
|
+
"test out-of-order PUTs (trial two)",
|
|
200
|
+
async () => {
|
|
201
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
202
|
+
|
|
203
|
+
var f = braid_fetch(`/${key}`, {
|
|
204
|
+
method: 'PUT',
|
|
205
|
+
version: ['ab-1'],
|
|
206
|
+
parents: ['hi-0'],
|
|
207
|
+
patches: [{unit: 'text', range: '[1:1]', content: 'y'}]
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
await new Promise(done => setTimeout(done, 500))
|
|
211
|
+
|
|
212
|
+
var r = await braid_fetch(`/${key}`, {
|
|
213
|
+
method: 'PUT',
|
|
214
|
+
version: ['hi-1'],
|
|
215
|
+
parents: [],
|
|
216
|
+
body: 'xz'
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
220
|
+
|
|
221
|
+
r = await f
|
|
222
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
223
|
+
|
|
224
|
+
var r2 = await braid_fetch(`/${key}`)
|
|
225
|
+
return await r2.text()
|
|
226
|
+
},
|
|
227
|
+
'xyz'
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
runTest(
|
|
231
|
+
"test in-order PUTs",
|
|
232
|
+
async () => {
|
|
233
|
+
var key = 'test-' + Math.random().toString(36).slice(2)
|
|
234
|
+
|
|
235
|
+
var r = await braid_fetch(`/${key}`, {
|
|
236
|
+
method: 'PUT',
|
|
237
|
+
version: ['hi-0'],
|
|
238
|
+
parents: [],
|
|
239
|
+
body: 'x'
|
|
240
|
+
})
|
|
241
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
242
|
+
|
|
243
|
+
var r = await braid_fetch(`/${key}`, {
|
|
244
|
+
method: 'PUT',
|
|
245
|
+
version: ['hi-1'],
|
|
246
|
+
parents: ['hi-0'],
|
|
247
|
+
patches: [{unit: 'text', range: '[1:1]', content: 'y'}]
|
|
248
|
+
})
|
|
249
|
+
if (!r.ok) throw 'got: ' + r.statusCode
|
|
250
|
+
|
|
251
|
+
var r2 = await braid_fetch(`/${key}`)
|
|
252
|
+
return await r2.text()
|
|
253
|
+
},
|
|
254
|
+
'xy'
|
|
255
|
+
)
|
|
256
|
+
|
|
71
257
|
runTest(
|
|
72
258
|
"test transfer-encoding dt (with parents)",
|
|
73
259
|
async () => {
|