braid-text 0.2.107 → 0.2.108

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/test/tests.js DELETED
@@ -1,2899 +0,0 @@
1
- // Shared test definitions that work in both Node.js and browser environments
2
- // This file exports a function that takes a test runner and braid_fetch implementation
3
-
4
- function defineTests(runTest, braid_fetch) {
5
-
6
- runTest(
7
- "test subscribe update with body_text",
8
- async () => {
9
- var key = 'test' + Math.random().toString(36).slice(2)
10
-
11
- var r = await braid_fetch(`/${key}`, {
12
- method: 'PUT',
13
- body: 'hi'
14
- })
15
- if (!r.ok) return 'got: ' + r.status
16
-
17
- var r1 = await braid_fetch(`/eval`, {
18
- method: 'PUT',
19
- body: `void (async () => {
20
- var x = await new Promise(done => {
21
- braid_text.get(new URL('http://localhost:8889/${key}'), {
22
- subscribe: update => {
23
- if (update.body_text === 'hi') done(update.body_text)
24
- }
25
- })
26
- })
27
- res.end(x)
28
- })()`
29
- })
30
- if (!r1.ok) return 'got: ' + r.status
31
-
32
- return await r1.text()
33
- },
34
- 'hi'
35
- )
36
-
37
- runTest(
38
- "test braid_text.sync, key to url, where url breaks",
39
- async () => {
40
- var key = 'test' + Math.random().toString(36).slice(2)
41
-
42
- var r = await braid_fetch(`/eval`, {
43
- method: 'PUT',
44
- body: `void (async () => {
45
- var count = 0
46
- var ac = new AbortController()
47
- braid_text.sync('/${key}', new URL('http://localhost:8889/have_error'), {
48
- signal: ac.signal,
49
- on_pre_connect: () => {
50
- count++
51
- if (count === 2) {
52
- res.end('it reconnected!')
53
- ac.abort()
54
- }
55
- }
56
- })
57
- })()`
58
- })
59
- return await r.text()
60
-
61
- },
62
- 'it reconnected!'
63
- )
64
-
65
- runTest(
66
- "test braid_text.sync, url to key",
67
- async () => {
68
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
69
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
70
-
71
- var r = await braid_fetch(`/${key_a}`, {
72
- method: 'PUT',
73
- body: 'hi'
74
- })
75
- if (!r.ok) return 'got: ' + r.status
76
-
77
- var r = await braid_fetch(`/eval`, {
78
- method: 'PUT',
79
- body: `void (async () => {
80
- braid_text.sync(new URL('http://localhost:8889/${key_a}'), '/${key_b}')
81
- res.end('')
82
- })()`
83
- })
84
- if (!r.ok) return 'got: ' + r.status
85
-
86
- await new Promise(done => setTimeout(done, 100))
87
-
88
- var r = await braid_fetch(`/${key_b}`)
89
- return 'got: ' + (await r.text())
90
- },
91
- 'got: hi'
92
- )
93
-
94
- runTest(
95
- "test braid_text.sync, url to resource",
96
- async () => {
97
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
98
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
99
-
100
- var r = await braid_fetch(`/${key_a}`, {
101
- method: 'PUT',
102
- body: 'hi'
103
- })
104
- if (!r.ok) return 'got: ' + r.status
105
-
106
- var r = await braid_fetch(`/eval`, {
107
- method: 'PUT',
108
- body: `void (async () => {
109
- var resource = await braid_text.get_resource('/${key_b}')
110
- braid_text.sync(new URL('http://localhost:8889/${key_a}'), resource)
111
- res.end('')
112
- })()`
113
- })
114
- if (!r.ok) return 'got: ' + r.status
115
-
116
- await new Promise(done => setTimeout(done, 100))
117
-
118
- var r = await braid_fetch(`/${key_b}`)
119
- return 'got: ' + (await r.text())
120
- },
121
- 'got: hi'
122
- )
123
-
124
- runTest(
125
- "test braid_text.sync, with two urls",
126
- async () => {
127
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
128
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
129
-
130
- var r = await braid_fetch(`/eval`, {
131
- method: 'PUT',
132
- body: `void (async () => {
133
- try {
134
- await braid_text.sync(new URL('http://localhost:8889/${key_a}'),
135
- new URL('http://localhost:8889/${key_b}'))
136
- res.end('no error')
137
- } catch (e) {
138
- res.end('' + e)
139
- }
140
- })()`
141
- })
142
- if (!r.ok) return 'got: ' + r.status
143
-
144
- return 'got: ' + (await r.text())
145
- },
146
- 'got: Error: one parameter should be local string key, and the other a remote URL object'
147
- )
148
-
149
- runTest(
150
- "test braid_text.sync, key to url",
151
- async () => {
152
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
153
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
154
-
155
- var r = await braid_fetch(`/${key_a}`, {
156
- method: 'PUT',
157
- body: 'hi'
158
- })
159
- if (!r.ok) return 'got: ' + r.status
160
-
161
- var r = await braid_fetch(`/eval`, {
162
- method: 'PUT',
163
- body: `void (async () => {
164
- braid_text.sync('/${key_a}', new URL('http://localhost:8889/${key_b}'))
165
- res.end('')
166
- })()`
167
- })
168
- if (!r.ok) return 'got: ' + r.status
169
-
170
- await new Promise(done => setTimeout(done, 100))
171
-
172
- var r = await braid_fetch(`/${key_b}`)
173
- return 'got: ' + (await r.text())
174
- },
175
- 'got: hi'
176
- )
177
-
178
- runTest(
179
- "test braid_text.sync, key to url, when HEAD fails",
180
- async () => {
181
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
182
-
183
- var r = await braid_fetch(`/${key_a}`, {
184
- method: 'PUT',
185
- body: 'hi'
186
- })
187
- if (!r.ok) return 'got: ' + r.status
188
-
189
- var r = await braid_fetch(`/eval`, {
190
- method: 'PUT',
191
- body: `void (async () => {
192
- var count = 0
193
- var ac = new AbortController()
194
- braid_text.sync('/${key_a}', new URL('http://localhost:8889/have_error'), {
195
- signal: ac.signal,
196
- on_pre_connect: () => {
197
- count++
198
- if (count === 2) {
199
- res.end('it reconnected!')
200
- ac.abort()
201
- }
202
- }
203
- })
204
- })()`
205
- })
206
- if (!r.ok) return 'got: ' + r.status
207
-
208
- return await r.text()
209
- },
210
- 'it reconnected!'
211
- )
212
-
213
- runTest(
214
- "test when remote doesn't have a fork-point that we think they have",
215
- async () => {
216
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
217
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
218
- var key_c = 'test-c-' + Math.random().toString(36).slice(2)
219
-
220
- var r = await braid_fetch(`/${key_a}`, {
221
- method: 'PUT',
222
- body: 'hi'
223
- })
224
- if (!r.ok) return 'got: ' + r.status
225
-
226
- var r = await braid_fetch(`/eval`, {
227
- method: 'PUT',
228
- body: `void (async () => {
229
- braid_text.sync('/${key_a}', new URL('http://localhost:8889/${key_b}'))
230
- await new Promise(done => setTimeout(done, 100))
231
- braid_text.sync('/${key_a}', new URL('http://localhost:8889/${key_c}'))
232
- await new Promise(done => setTimeout(done, 100))
233
- res.end('')
234
- })()`
235
- })
236
- if (!r.ok) return 'got: ' + r.status
237
-
238
- return await (await braid_fetch(`/${key_c}`)).text()
239
- },
240
- 'hi'
241
- )
242
-
243
- runTest(
244
- "test when we don't have a fork-point with remote, but they do have a shared version",
245
- async () => {
246
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
247
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
248
-
249
- var r = await braid_fetch(`/${key_a}`, {
250
- method: 'PUT',
251
- body: 'hi'
252
- })
253
- if (!r.ok) return 'got: ' + r.status
254
-
255
- var r = await braid_fetch(`/eval`, {
256
- method: 'PUT',
257
- body: `void (async () => {
258
- var ac = new AbortController()
259
- braid_text.get('/${key_a}', {
260
- signal: ac.signal,
261
- subscribe: update => braid_text.put('/${key_b}', update)
262
- })
263
- await new Promise(done => setTimeout(done, 100))
264
- ac.abort()
265
- res.end('')
266
- })()`
267
- })
268
- if (!r.ok) return 'got: ' + r.status
269
-
270
- var r = await braid_fetch(`/${key_a}`, {
271
- method: 'PUT',
272
- body: 'yo'
273
- })
274
- if (!r.ok) return 'got: ' + r.status
275
-
276
- var r = await braid_fetch(`/eval`, {
277
- method: 'PUT',
278
- body: `void (async () => {
279
- var ac = new AbortController()
280
- braid_text.sync('/${key_a}', new URL('http://localhost:8889/${key_b}'), {signal: ac.signal})
281
- await new Promise(done => setTimeout(done, 100))
282
- ac.abort()
283
- res.end('')
284
- })()`
285
- })
286
- if (!r.ok) return 'got: ' + r.status
287
-
288
- return await (await braid_fetch(`/${key_b}`)).text()
289
- },
290
- 'yo'
291
- )
292
-
293
- runTest(
294
- "test braid_text.sync, with two keys",
295
- async () => {
296
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
297
- var key_b = 'test-b-' + Math.random().toString(36).slice(2)
298
-
299
- var r = await braid_fetch(`/eval`, {
300
- method: 'PUT',
301
- body: `void (async () => {
302
- try {
303
- await braid_text.sync('/${key_a}', '/${key_b}')
304
- res.end('no error')
305
- } catch (e) {
306
- res.end('' + e)
307
- }
308
- })()`
309
- })
310
- if (!r.ok) return 'got: ' + r.status
311
-
312
- return 'got: ' + (await r.text())
313
- },
314
- 'got: Error: one parameter should be local string key, and the other a remote URL object'
315
- )
316
-
317
- runTest(
318
- "test putting version with multiple event ids, should have error",
319
- async () => {
320
- var key_a = 'test-a-' + Math.random().toString(36).slice(2)
321
-
322
- var r = await braid_fetch(`/${key_a}`, {
323
- method: 'PUT',
324
- version: ['abc-1', 'xyz-2'],
325
- body: 'hi'
326
- })
327
- return '' + (await r.text()).includes('cannot put a version with multiple ids')
328
- },
329
- 'true'
330
- )
331
-
332
- runTest(
333
- "test braid_text.get(url), with no options",
334
- async () => {
335
- var key = 'test-' + Math.random().toString(36).slice(2)
336
- let r = await braid_fetch(`/${key}`, {
337
- method: 'PUT',
338
- body: 'hi'
339
- })
340
- if (!r.ok) return 'got: ' + r.status
341
-
342
- var r1 = await braid_fetch(`/eval`, {
343
- method: 'PUT',
344
- body: `void (async () => {
345
- res.end(await braid_text.get(new URL('http://localhost:8889/${key}')))
346
- })()`
347
- })
348
-
349
- return 'got: ' + (await r1.text())
350
- },
351
- 'got: hi'
352
- )
353
-
354
- runTest(
355
- "test braid_text.get(url), with headers",
356
- async () => {
357
- var key = 'test-' + Math.random().toString(36).slice(2)
358
- let r = await braid_fetch(`/${key}`, {
359
- method: 'PUT',
360
- version: ['xyz-1'],
361
- body: 'hi'
362
- })
363
- if (!r.ok) return 'got: ' + r.status
364
-
365
- var r1 = await braid_fetch(`/eval`, {
366
- method: 'PUT',
367
- body: `void (async () => {
368
- res.end(await braid_text.get(new URL('http://localhost:8889/${key}'), {
369
- headers: {
370
- version: '"xyz-0"'
371
- }
372
- }))
373
- })()`
374
- })
375
-
376
- return 'got: ' + (await r1.text())
377
- },
378
- 'got: h'
379
- )
380
-
381
- runTest(
382
- "test braid_text.get(url), with parents",
383
- async () => {
384
- var key = 'test-' + Math.random().toString(36).slice(2)
385
- let r = await braid_fetch(`/${key}`, {
386
- method: 'PUT',
387
- version: ['xyz-1'],
388
- body: 'hi'
389
- })
390
- if (!r.ok) return 'got: ' + r.status
391
-
392
- var r1 = await braid_fetch(`/eval`, {
393
- method: 'PUT',
394
- body: `void (async () => {
395
- res.end(await braid_text.get(new URL('http://localhost:8889/${key}'), {
396
- parents: ['xyz-0']
397
- }))
398
- })()`
399
- })
400
-
401
- return 'got: ' + (await r1.text())
402
- },
403
- 'got: h'
404
- )
405
-
406
- runTest(
407
- "test braid_text.get(url), with version and peer",
408
- async () => {
409
- var key = 'test-' + Math.random().toString(36).slice(2)
410
- let r = await braid_fetch(`/${key}`, {
411
- method: 'PUT',
412
- version: ['xyz-1'],
413
- body: 'hi'
414
- })
415
- if (!r.ok) return 'got: ' + r.status
416
-
417
- var r1 = await braid_fetch(`/eval`, {
418
- method: 'PUT',
419
- body: `void (async () => {
420
- res.end(await braid_text.get(new URL('http://localhost:8889/${key}'), {
421
- version: ['xyz-0'],
422
- peer: 'xyz'
423
- }))
424
- })()`
425
- })
426
-
427
- return 'got: ' + (await r1.text())
428
- },
429
- 'got: h'
430
- )
431
-
432
- runTest(
433
- "test braid_text.get(url) with subscription",
434
- async () => {
435
- var key = 'test-' + Math.random().toString(36).slice(2)
436
- let r = await braid_fetch(`/${key}`, {
437
- method: 'PUT',
438
- version: ['xyz-1'],
439
- body: 'hi'
440
- })
441
- if (!r.ok) return 'got: ' + r.status
442
-
443
- var r1 = await braid_fetch(`/eval`, {
444
- method: 'PUT',
445
- body: `void (async () => {
446
- var url = new URL('http://localhost:8889/${key}')
447
- var ac = new AbortController()
448
- var update = await new Promise(done => {
449
- braid_text.get(url, {
450
- signal: ac.signal,
451
- subscribe: update => {
452
- ac.abort()
453
- done(update)
454
- }
455
- })
456
- })
457
- res.end(update.body)
458
- })()`
459
- })
460
-
461
- return 'got: ' + (await r1.text())
462
- },
463
- 'got: hi'
464
- )
465
-
466
- runTest(
467
- "test braid_text.put(url), with body",
468
- async () => {
469
- var key = 'test-' + Math.random().toString(36).slice(2)
470
-
471
- var r = await braid_fetch(`/eval`, {
472
- method: 'PUT',
473
- body: `void (async () => {
474
- var r = await braid_text.put(new URL('http://localhost:8889/${key}'),
475
- {body: 'yo'})
476
- res.end('')
477
- })()`
478
- })
479
- if (!r.ok) return 'got: ' + r.status
480
-
481
- let r1 = await braid_fetch(`/${key}`)
482
- return 'got: ' + (await r1.text())
483
- },
484
- 'got: yo'
485
- )
486
-
487
- runTest(
488
- "test braid_text.put(url), with body and headers",
489
- async () => {
490
- var key = 'test-' + Math.random().toString(36).slice(2)
491
-
492
- var r = await braid_fetch(`/eval`, {
493
- method: 'PUT',
494
- body: `void (async () => {
495
- var r = await braid_text.put(new URL('http://localhost:8889/${key}'),
496
- {body: 'yo', headers: {version: '"abc123-1"'}})
497
- res.end('')
498
- })()`
499
- })
500
- if (!r.ok) return 'got: ' + r.status
501
-
502
- let r2 = await braid_fetch(`/${key}`)
503
- return 'got: ' + (await r2.text()) + ' -- version: ' + r2.headers.get('version')
504
- },
505
- 'got: yo -- version: "abc123-1"'
506
- )
507
-
508
- runTest(
509
- "test braid_text.put(url), with body and version and parents",
510
- async () => {
511
- var key = 'test-' + Math.random().toString(36).slice(2)
512
- let r = await braid_fetch(`/${key}`, {
513
- method: 'PUT',
514
- body: 'hi',
515
- version: ['abc-1']
516
- })
517
-
518
- var r1 = await braid_fetch(`/eval`, {
519
- method: 'PUT',
520
- body: `void (async () => {
521
- var r = await braid_text.put(new URL('http://localhost:8889/${key}'),
522
- {body: 'yo', version: ['xyz-3'], parents: ['abc-1']})
523
- res.end('')
524
- })()`
525
- })
526
- if (!r1.ok) return 'got: ' + r1.status
527
-
528
- let r2 = await braid_fetch(`/${key}`)
529
- return 'got: ' + (await r2.text()) + ' -- version: ' + r2.headers.get('version')
530
- },
531
- 'got: yo -- version: "xyz-3"'
532
- )
533
-
534
- runTest(
535
- "test braid_text.put(url), with peer",
536
- async () => {
537
- var key = 'test-' + Math.random().toString(36).slice(2)
538
-
539
- var r1 = await braid_fetch(`/eval`, {
540
- method: 'PUT',
541
- body: `void (async () => {
542
- var r = await braid_text.put(new URL('http://localhost:8889/${key}'),
543
- {body: 'yo', peer: 'xyz', parents: []})
544
- res.end('')
545
- })()`
546
- })
547
- if (!r1.ok) return 'got: ' + r1.status
548
-
549
- let r2 = await braid_fetch(`/${key}`)
550
- return 'got: ' + (await r2.text()) + ' -- version: ' + r2.headers.get('version')
551
- },
552
- 'got: yo -- version: "xyz-1"'
553
- )
554
-
555
- runTest(
556
- "test loading a meta file from disk",
557
- async () => {
558
- var key = 'test-' + Math.random().toString(36).slice(2)
559
-
560
- var r1 = await braid_fetch(`/eval`, {
561
- method: 'PUT',
562
- body: `void (async () => {
563
- var resource = await braid_text.get_resource('/${key}')
564
- resource.meta = { test_meta_info: 42 }
565
- resource.change_meta()
566
-
567
- await new Promise(done => setTimeout(done, 200))
568
-
569
- delete braid_text.cache['/${key}']
570
-
571
- var resource = await braid_text.get_resource('/${key}')
572
- res.end(JSON.stringify(resource.meta))
573
- })()`
574
- })
575
-
576
- return (await r1.text())
577
- },
578
- '{"test_meta_info":42}'
579
- )
580
-
581
- runTest(
582
- "test selection-sharing-prototype PUT and GET",
583
- async () => {
584
- let key = 'test-' + Math.random().toString(36).slice(2)
585
-
586
- let time = Date.now()
587
-
588
- let r = await braid_fetch(`/${key}`, {
589
- method: 'PUT',
590
- body: JSON.stringify({
591
- hello: {
592
- yo: 'hi',
593
- time
594
- }
595
- }),
596
- headers: {
597
- 'selection-sharing-prototype': 'true'
598
- }
599
- })
600
- if (!r.ok) return 'got: ' + r.status
601
-
602
- let r2 = await braid_fetch(`/${key}`, {
603
- method: 'GET',
604
- headers: {
605
- 'selection-sharing-prototype': 'true'
606
- }
607
- })
608
- if (!r2.ok) return 'got: ' + r2.status
609
-
610
- let o = await r2.json()
611
- return o.hello.time === time ? 'times match' : 'bad'
612
- },
613
- 'times match'
614
- )
615
-
616
- runTest(
617
- "test selection-sharing-prototype GET/subscribe",
618
- async () => {
619
- let key = 'test-' + Math.random().toString(36).slice(2)
620
-
621
- var a = new AbortController()
622
- let r = await braid_fetch(`/${key}`, {
623
- method: 'GET',
624
- signal: a.signal,
625
- subscribe: true,
626
- peer: 'abc',
627
- headers: {
628
- 'selection-sharing-prototype': 'true'
629
- }
630
- })
631
- if (!r.ok) return 'got: ' + r.status
632
- var p = new Promise(done => {
633
- r.subscribe(update => {
634
- var body = update.body_text
635
- if (body.length > 2) done(body)
636
- })
637
- })
638
-
639
- var time = Date.now()
640
-
641
- let r2 = await braid_fetch(`/${key}`, {
642
- method: 'PUT',
643
- peer: 'xyz',
644
- body: JSON.stringify({
645
- hello: {
646
- yo: 'hi',
647
- time
648
- }
649
- }),
650
- headers: {
651
- 'selection-sharing-prototype': 'true'
652
- }
653
- })
654
- if (!r2.ok) return 'got: ' + r2.status
655
-
656
- var ret_val = JSON.parse(await p).hello.time === time ? 'times match' : 'bad'
657
-
658
- a.abort()
659
-
660
- return ret_val
661
- },
662
- 'times match'
663
- )
664
-
665
- runTest(
666
- "test selection-sharing-prototype PUT old cursor",
667
- async () => {
668
- let key = 'test-' + Math.random().toString(36).slice(2)
669
-
670
- let time = Date.now()
671
-
672
- let r = await braid_fetch(`/${key}`, {
673
- method: 'PUT',
674
- body: JSON.stringify({
675
- hello: {
676
- yo: 'hi',
677
- time
678
- }
679
- }),
680
- headers: {
681
- 'selection-sharing-prototype': 'true'
682
- }
683
- })
684
- if (!r.ok) return 'got: ' + r.status
685
-
686
- let r3 = await braid_fetch(`/${key}`, {
687
- method: 'PUT',
688
- body: JSON.stringify({
689
- hello: {
690
- yo: 'hoop',
691
- time: time - 5
692
- }
693
- }),
694
- headers: {
695
- 'selection-sharing-prototype': 'true'
696
- }
697
- })
698
- if (!r3.ok) return 'got: ' + r3.status
699
-
700
- let r2 = await braid_fetch(`/${key}`, {
701
- method: 'GET',
702
- headers: {
703
- 'selection-sharing-prototype': 'true'
704
- }
705
- })
706
- if (!r2.ok) return 'got: ' + r2.status
707
-
708
- let o = await r2.json()
709
- return o.hello.yo
710
- },
711
- 'hi'
712
- )
713
-
714
- runTest(
715
- "test selection-sharing-prototype PUT really old cursor",
716
- async () => {
717
- let key = 'test-' + Math.random().toString(36).slice(2)
718
-
719
- let time = Date.now() - 1000 * 60 * 60 * 24
720
-
721
- let r = await braid_fetch(`/${key}`, {
722
- method: 'PUT',
723
- body: JSON.stringify({
724
- hello: {
725
- yo: 'hi',
726
- time
727
- }
728
- }),
729
- headers: {
730
- 'selection-sharing-prototype': 'true'
731
- }
732
- })
733
- if (!r.ok) return 'got: ' + r.status
734
-
735
- let r2 = await braid_fetch(`/${key}`, {
736
- method: 'GET',
737
- headers: {
738
- 'selection-sharing-prototype': 'true'
739
- }
740
- })
741
- if (!r2.ok) return 'got: ' + r2.status
742
-
743
- let o = await r2.json()
744
- return JSON.stringify(o)
745
- },
746
- '{}'
747
- )
748
-
749
- runTest(
750
- "test PUT digest (good)",
751
- async () => {
752
- let key = 'test-' + Math.random().toString(36).slice(2)
753
-
754
- async function get_digest(s) {
755
- var bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(s))
756
- return `sha-256=:${btoa(String.fromCharCode(...new Uint8Array(bytes)))}:`
757
- }
758
-
759
- let r = await braid_fetch(`/${key}`, {
760
- method: 'PUT',
761
- version: ['hi-1'],
762
- parents: [],
763
- body: 'xx',
764
- headers: {
765
- 'Repr-Digest': await get_digest('xx')
766
- }
767
- })
768
- if (!r.ok) return 'got: ' + r.status
769
- return 'ok'
770
- },
771
- 'ok'
772
- )
773
-
774
- runTest(
775
- "test PUT digest (bad)",
776
- async () => {
777
- let key = 'test-' + Math.random().toString(36).slice(2)
778
-
779
- async function get_digest(s) {
780
- var bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(s))
781
- return `sha-256=:${btoa(String.fromCharCode(...new Uint8Array(bytes)))}:`
782
- }
783
-
784
- let r = await braid_fetch(`/${key}`, {
785
- method: 'PUT',
786
- version: ['hi-1'],
787
- parents: [],
788
- body: 'xx',
789
- headers: {
790
- 'Repr-Digest': await get_digest('yy')
791
- }
792
- })
793
- if (!r.ok) return 'got: ' + r.status
794
- return 'ok'
795
- },
796
- 'got: 550'
797
- )
798
-
799
- runTest(
800
- "test subscribing and verifying digests [simpleton]",
801
- async () => {
802
- let key = 'test-' + Math.random().toString(36).slice(2)
803
-
804
- let r = await braid_fetch(`/${key}`, {
805
- method: 'PUT',
806
- version: ['hi-1'],
807
- parents: [],
808
- body: 'xx'
809
- })
810
- if (!r.ok) throw 'got: ' + r.statusCode
811
-
812
- var a = new AbortController()
813
- let r2 = await braid_fetch(`/${key}`, {
814
- signal: a.signal,
815
- version: ['hi-0'],
816
- subscribe: true
817
- })
818
- var parts = []
819
- var p = new Promise(async (done, fail) => {
820
- r2.subscribe(update => {
821
- parts.push(update.extra_headers['repr-digest'])
822
- if (parts.length > 1) {
823
- done()
824
- a.abort()
825
- }
826
- }, fail)
827
- })
828
-
829
- await new Promise(done => setTimeout(done, 300))
830
- let rr = await braid_fetch(`/${key}`, {
831
- method: 'PUT',
832
- version: ['hi-2'],
833
- parents: ['hi-1'],
834
- patches: [{unit: "text", range: "[1:1]", content: "Y"}]
835
- })
836
- if (!rr.ok) throw 'got: ' + rr.statusCode
837
-
838
- await p
839
- return JSON.stringify(parts)
840
- },
841
- '["sha-256=:Xd6JaIf2dUybFb/jpEGuSAbfL96UABMR4IvxEGIuC74=:","sha-256=:77cl3INcGEtczN0zK3eOgW/YWYAOm8ub73LkVcF2/rA=:"]'
842
- )
843
-
844
- runTest(
845
- "test subscribing and verifying digests [dt]",
846
- async () => {
847
- let key = 'test-' + Math.random().toString(36).slice(2)
848
-
849
- let r = await braid_fetch(`/${key}`, {
850
- method: 'PUT',
851
- version: ['hi-1'],
852
- parents: [],
853
- body: 'xx'
854
- })
855
- if (!r.ok) throw 'got: ' + r.statusCode
856
-
857
- var a = new AbortController()
858
- let r2 = await braid_fetch(`/${key}`, {
859
- signal: a.signal,
860
- version: ['hi-0'],
861
- headers: { 'merge-type': 'dt' },
862
- subscribe: true
863
- })
864
- var parts = []
865
- var p = new Promise(async (done, fail) => {
866
- r2.subscribe(update => {
867
- parts.push(update.extra_headers['repr-digest'])
868
- if (parts.length > 1) {
869
- done()
870
- a.abort()
871
- }
872
- }, fail)
873
- })
874
-
875
- await new Promise(done => setTimeout(done, 300))
876
- let rr = await braid_fetch(`/${key}`, {
877
- method: 'PUT',
878
- version: ['hi-2'],
879
- parents: ['hi-1'],
880
- patches: [{unit: "text", range: "[2:2]", content: "Y"}]
881
- })
882
- if (!rr.ok) throw 'got: ' + rr.statusCode
883
-
884
- await p
885
- return JSON.stringify(parts)
886
- },
887
- '["sha-256=:Xd6JaIf2dUybFb/jpEGuSAbfL96UABMR4IvxEGIuC74=:","sha-256=:QknHazou37wCCwv3JXnCoAvXcKszP6xBTxLIiUAETgI=:"]'
888
- )
889
-
890
- runTest(
891
- "test PUTing a version that the server already has",
892
- async () => {
893
- var key = 'test-' + Math.random().toString(36).slice(2)
894
-
895
- var r1 = await braid_fetch(`/${key}`, {
896
- method: 'PUT',
897
- version: ['hi-0'],
898
- parents: [],
899
- body: 'x'
900
- })
901
-
902
- var r2 = await braid_fetch(`/${key}`, {
903
- method: 'PUT',
904
- version: ['hi-0'],
905
- parents: [],
906
- body: 'x'
907
- })
908
-
909
- return r1.status + " " + r2.status
910
- },
911
- '200 200'
912
- )
913
-
914
- runTest(
915
- "test validate_already_seen_versions with same version",
916
- async () => {
917
- var key = 'test-' + Math.random().toString(36).slice(2)
918
-
919
- var r1 = await braid_fetch(`/eval`, {
920
- method: 'PUT',
921
- body: `void (async () => {
922
- var resource = await braid_text.get_resource('/${key}')
923
-
924
- var {change_count} = await braid_text.put(resource, { peer: "abc", version: ["hi-2"], parents: [], patches: [{unit: "text", range: "[0:0]", content: "XYZ"}], merge_type: "dt" })
925
-
926
- res.end('' + change_count)
927
- })()`
928
- })
929
-
930
- var r2 = await braid_fetch(`/eval`, {
931
- method: 'PUT',
932
- body: `void (async () => {
933
- var resource = await braid_text.get_resource('/${key}')
934
-
935
- var {change_count} = await braid_text.put(resource, { peer: "abc", version: ["hi-2"], parents: [], patches: [{unit: "text", range: "[0:0]", content: "XYZ"}], merge_type: "dt", validate_already_seen_versions: true })
936
-
937
- res.end('' + change_count)
938
- })()`
939
- })
940
-
941
- return (await r1.text()) + " " + (await r2.text())
942
- },
943
- '3 3'
944
- )
945
-
946
- runTest(
947
- "test validate_already_seen_versions with modified version",
948
- async () => {
949
- var key = 'test-' + Math.random().toString(36).slice(2)
950
-
951
- var r1 = await braid_fetch(`/eval`, {
952
- method: 'PUT',
953
- body: `void (async () => {
954
- var resource = await braid_text.get_resource('/${key}')
955
-
956
- var {change_count} = await braid_text.put(resource, { peer: "abc", version: ["hi-2"], parents: [], patches: [{unit: "text", range: "[0:0]", content: "XYZ"}], merge_type: "dt" })
957
-
958
- res.end('' + change_count)
959
- })()`
960
- })
961
-
962
- var r2 = await braid_fetch(`/eval`, {
963
- method: 'PUT',
964
- body: `void (async () => {
965
- var resource = await braid_text.get_resource('/${key}')
966
-
967
- try {
968
- var {change_count} = await braid_text.put(resource, { peer: "abc", version: ["hi-2"], parents: [], patches: [{unit: "text", range: "[0:0]", content: "ABC"}], merge_type: "dt", validate_already_seen_versions: true })
969
-
970
- res.end('' + change_count)
971
- } catch (e) {
972
- res.end(e.message)
973
- }
974
- })()`
975
- })
976
-
977
- return await r2.text()
978
- },
979
- 'invalid update: different from previous update with same version'
980
- )
981
-
982
- runTest(
983
- "test loading a previously saved resource",
984
- async () => {
985
- var key = 'test-' + Math.random().toString(36).slice(2)
986
-
987
- var f1 = await braid_fetch(`/${key}`, {
988
- method: 'PUT',
989
- version: ['hi-2'],
990
- parents: [],
991
- body: 'abc'
992
- })
993
-
994
- var f1 = await braid_fetch(`/eval`, {
995
- method: 'PUT',
996
- body: `
997
- delete braid_text.cache['/${key}']
998
- res.end()
999
- `
1000
- })
1001
-
1002
- var r = await braid_fetch(`/${key}`)
1003
- return await r.text()
1004
- },
1005
- 'abc'
1006
- )
1007
-
1008
- runTest(
1009
- "test non-contigous ids",
1010
- async () => {
1011
- var key = 'test-' + Math.random().toString(36).slice(2)
1012
-
1013
- var r = await braid_fetch(`/${key}`, {
1014
- method: 'PUT',
1015
- version: ['hi-10'],
1016
- parents: [],
1017
- body: 'abc'
1018
- })
1019
-
1020
- var r = await braid_fetch(`/${key}`, {
1021
- method: 'PUT',
1022
- version: ['hi-20'],
1023
- parents: ['hi-10'],
1024
- body: 'ABC'
1025
- })
1026
-
1027
- var f1 = await braid_fetch(`/eval`, {
1028
- method: 'PUT',
1029
- body: `
1030
- delete braid_text.cache['/${key}']
1031
- res.end()
1032
- `
1033
- })
1034
-
1035
- var r = await braid_fetch(`/${key}`)
1036
- return await r.text()
1037
- },
1038
- 'ABC'
1039
- )
1040
-
1041
- runTest(
1042
- "test when PUT cache/buffer size fails",
1043
- async () => {
1044
- var key = 'test-' + Math.random().toString(36).slice(2)
1045
-
1046
- var f1 = braid_fetch(`/${key}`, {
1047
- method: 'PUT',
1048
- version: ['hi-3000000'],
1049
- parents: ['yo-0'],
1050
- body: 'A'.repeat(3000000)
1051
- })
1052
-
1053
- await new Promise(done => setTimeout(done, 300))
1054
-
1055
- var f2 = braid_fetch(`/${key}`, {
1056
- method: 'PUT',
1057
- version: ['ih-3000000'],
1058
- parents: ['yo-0'],
1059
- body: 'B'.repeat(3000000)
1060
- })
1061
-
1062
- await new Promise(done => setTimeout(done, 300))
1063
-
1064
- var r = await braid_fetch(`/${key}`, {
1065
- method: 'PUT',
1066
- version: ['yo-0'],
1067
- parents: [],
1068
- body: 'x'
1069
- })
1070
- if (!r.ok) throw 'got: ' + r.statusCode
1071
-
1072
- return `f1: ${(await f1).status}, f2: ${(await f2).status}`
1073
- },
1074
- 'f1: 200, f2: 309'
1075
- )
1076
-
1077
- runTest(
1078
- "test multiple patches",
1079
- async () => {
1080
- var key = 'test-' + Math.random().toString(36).slice(2)
1081
-
1082
- var r = await braid_fetch(`/${key}`, {
1083
- method: 'PUT',
1084
- version: ['hi-0'],
1085
- parents: [],
1086
- body: 'A'
1087
- })
1088
- if (!r.ok) throw 'got: ' + r.statusCode
1089
-
1090
- var r = await braid_fetch(`/${key}`, {
1091
- method: 'PUT',
1092
- version: ['yo-1'],
1093
- parents: ['hi-0'],
1094
- patches: [
1095
- {unit: 'text', range: '[0:0]', content: 'C'},
1096
- {unit: 'text', range: '[1:1]', content: 'T'}
1097
- ]
1098
- })
1099
- if (!r.ok) throw 'got: ' + r.statusCode
1100
-
1101
- var r2 = await braid_fetch(`/${key}`)
1102
- return await r2.text()
1103
- },
1104
- 'CAT'
1105
- )
1106
-
1107
- runTest(
1108
- "test PUT after subscribing",
1109
- async () => {
1110
- var key = 'test-' + Math.random().toString(36).slice(2)
1111
-
1112
- var p_done
1113
- var p = new Promise(done => p_done = done)
1114
-
1115
- var a = new AbortController()
1116
- var r = await braid_fetch(`/${key}`, {
1117
- signal: a.signal,
1118
- subscribe: true
1119
- })
1120
- r.subscribe(update => {
1121
- if (update.version[0] === 'hi-0') {
1122
- p_done(update.patches[0].content_text)
1123
- a.abort()
1124
- }
1125
- })
1126
-
1127
- var r = await braid_fetch(`/${key}`, {
1128
- method: 'PUT',
1129
- version: ['hi-0'],
1130
- parents: [],
1131
- body: 'x'
1132
- })
1133
- if (!r.ok) throw 'got: ' + r.statusCode
1134
-
1135
- return await p
1136
- },
1137
- 'x'
1138
- )
1139
-
1140
- runTest(
1141
- "test put awaits subscriber callbacks",
1142
- async () => {
1143
- var key = 'test-' + Math.random().toString(36).slice(2)
1144
-
1145
- var r = await braid_fetch(`/eval`, {
1146
- method: 'PUT',
1147
- body: `void (async () => {
1148
- var order = []
1149
-
1150
- // Subscribe with an async callback that takes some time
1151
- braid_text.get('/${key}', {
1152
- subscribe: async (update) => {
1153
- if (update.version?.[0]?.startsWith('test-v')) {
1154
- order.push('subscriber-start')
1155
- await new Promise(done => setTimeout(done, 50))
1156
- order.push('subscriber-end')
1157
- }
1158
- }
1159
- })
1160
-
1161
- // Wait for subscription to be established
1162
- await new Promise(done => setTimeout(done, 50))
1163
-
1164
- // Put should await the subscriber callback
1165
- await braid_text.put('/${key}', {
1166
- version: ['test-v-0'],
1167
- parents: [],
1168
- body: 'hello'
1169
- })
1170
- order.push('put-done')
1171
-
1172
- // If put properly awaited, order should be: subscriber-start, subscriber-end, put-done
1173
- // If put didn't await, order would be: subscriber-start, put-done, subscriber-end
1174
- res.end(order.join(','))
1175
- })()`
1176
- })
1177
- if (!r.ok) return 'eval failed: ' + r.status
1178
-
1179
- return await r.text()
1180
- },
1181
- 'subscriber-start,subscriber-end,put-done'
1182
- )
1183
-
1184
- runTest(
1185
- "test out-of-order PUTs",
1186
- async () => {
1187
- var key = 'test-' + Math.random().toString(36).slice(2)
1188
-
1189
- var f = braid_fetch(`/${key}`, {
1190
- method: 'PUT',
1191
- version: ['hi-1'],
1192
- parents: ['hi-0'],
1193
- patches: [{unit: 'text', range: '[1:1]', content: 'y'}]
1194
- })
1195
-
1196
- await new Promise(done => setTimeout(done, 500))
1197
-
1198
- var r = await braid_fetch(`/${key}`, {
1199
- method: 'PUT',
1200
- version: ['hi-0'],
1201
- parents: [],
1202
- body: 'x'
1203
- })
1204
-
1205
- if (!r.ok) throw 'got: ' + r.status
1206
-
1207
- r = await f
1208
- if (!r.ok) throw 'got: ' + r.status
1209
-
1210
- var r2 = await braid_fetch(`/${key}`)
1211
- return await r2.text()
1212
- },
1213
- 'xy'
1214
- )
1215
-
1216
- runTest(
1217
- "test out-of-order PUTs (trial two)",
1218
- async () => {
1219
- var key = 'test-' + Math.random().toString(36).slice(2)
1220
-
1221
- var f = braid_fetch(`/${key}`, {
1222
- method: 'PUT',
1223
- version: ['ab-1'],
1224
- parents: ['hi-0'],
1225
- patches: [{unit: 'text', range: '[1:1]', content: 'y'}]
1226
- })
1227
-
1228
- await new Promise(done => setTimeout(done, 500))
1229
-
1230
- var r = await braid_fetch(`/${key}`, {
1231
- method: 'PUT',
1232
- version: ['hi-1'],
1233
- parents: [],
1234
- body: 'xz'
1235
- })
1236
-
1237
- if (!r.ok) throw 'got: ' + r.statusCode
1238
-
1239
- r = await f
1240
- if (!r.ok) throw 'got: ' + r.statusCode
1241
-
1242
- var r2 = await braid_fetch(`/${key}`)
1243
- return await r2.text()
1244
- },
1245
- 'xyz'
1246
- )
1247
-
1248
- runTest(
1249
- "test in-order PUTs",
1250
- async () => {
1251
- var key = 'test-' + Math.random().toString(36).slice(2)
1252
-
1253
- var r = await braid_fetch(`/${key}`, {
1254
- method: 'PUT',
1255
- version: ['hi-0'],
1256
- parents: [],
1257
- body: 'x'
1258
- })
1259
- if (!r.ok) throw 'got: ' + r.statusCode
1260
-
1261
- var r = await braid_fetch(`/${key}`, {
1262
- method: 'PUT',
1263
- version: ['hi-1'],
1264
- parents: ['hi-0'],
1265
- patches: [{unit: 'text', range: '[1:1]', content: 'y'}]
1266
- })
1267
- if (!r.ok) throw 'got: ' + r.statusCode
1268
-
1269
- var r2 = await braid_fetch(`/${key}`)
1270
- return await r2.text()
1271
- },
1272
- 'xy'
1273
- )
1274
-
1275
- runTest(
1276
- "test put with transfer-encoding: dt",
1277
- async () => {
1278
- await dt_p
1279
- var key = 'test-' + Math.random().toString(36).slice(2)
1280
- var doc = new Doc('hi')
1281
- doc.ins(0, 'xy')
1282
-
1283
- var bytes = doc.toBytes()
1284
-
1285
- var r1 = await braid_fetch(`/eval`, {
1286
- method: 'PUT',
1287
- body: `void (async () => {
1288
- var key = '/${key}'
1289
-
1290
- var {change_count} = await braid_text.put(key, {
1291
- body: new Uint8Array([${'' + bytes}]),
1292
- transfer_encoding: "dt"
1293
- })
1294
- var {body, version} = await braid_text.get(key, {})
1295
-
1296
- res.end('' + change_count + " " + body + " " + version)
1297
- })()`
1298
- })
1299
-
1300
- return await r1.text()
1301
- },
1302
- '2 xy hi-1'
1303
- )
1304
-
1305
- runTest(
1306
- "test transfer-encoding dt (with parents)",
1307
- async () => {
1308
- await dt_p
1309
- let key = 'test-' + Math.random().toString(36).slice(2)
1310
- var doc = new Doc('hi')
1311
- doc.ins(0, 'x')
1312
-
1313
- let r = await braid_fetch(`/${key}`, {
1314
- method: 'PUT',
1315
- version: ['hi-1'],
1316
- parents: [],
1317
- body: 'xy'
1318
- })
1319
- if (!r.ok) throw 'got: ' + r.statusCode
1320
-
1321
- let r2 = await braid_fetch(`/${key}`, {
1322
- parents: ['hi-0'],
1323
- headers: {
1324
- 'Accept-Transfer-Encoding': 'dt'
1325
- }
1326
- })
1327
-
1328
- doc.mergeBytes([...new Uint8Array(await r2.arrayBuffer())])
1329
- var text = doc.get()
1330
- doc.free()
1331
-
1332
- return r2.headers.get('current-version') + ' ' + r2.headers.get('x-transfer-encoding') + ' ' + text + ' ' + r2.statusText
1333
- },
1334
- '"hi-1" dt xy Multiresponse'
1335
- )
1336
-
1337
- runTest(
1338
- "test transfer-encoding dt",
1339
- async () => {
1340
- await dt_p
1341
- let key = 'test-' + Math.random().toString(36).slice(2)
1342
-
1343
- let r = await braid_fetch(`/${key}`, {
1344
- method: 'PUT',
1345
- version: ['hi-1'],
1346
- parents: [],
1347
- body: 'xy'
1348
- })
1349
- if (!r.ok) throw 'got: ' + r.statusCode
1350
-
1351
- let r2 = await braid_fetch(`/${key}`, {
1352
- headers: {
1353
- 'Accept-Transfer-Encoding': 'dt'
1354
- }
1355
- })
1356
-
1357
- var doc = new Doc('yo')
1358
- doc.mergeBytes([...new Uint8Array(await r2.arrayBuffer())])
1359
- var text = doc.get()
1360
- doc.free()
1361
-
1362
- return r2.headers.get('current-version') + ' ' + r2.headers.get('x-transfer-encoding') + ' ' + text
1363
- },
1364
- '"hi-1" dt xy'
1365
- )
1366
-
1367
- runTest(
1368
- "test GETing old version explicitly with transfer-encoding dt",
1369
- async () => {
1370
- await dt_p
1371
- let key = 'test-' + Math.random().toString(36).slice(2)
1372
-
1373
- let r = await braid_fetch(`/${key}`, {
1374
- method: 'PUT',
1375
- version: ['hi∑-1'],
1376
- parents: [],
1377
- body: 'xy'
1378
- })
1379
- if (!r.ok) throw 'got: ' + r.statusCode
1380
-
1381
- let r2 = await braid_fetch(`/${key}`, {
1382
- version: ['hi∑-0'],
1383
- headers: {
1384
- 'Accept-Transfer-Encoding': 'dt'
1385
- }
1386
- })
1387
-
1388
- var doc = new Doc('yo')
1389
- doc.mergeBytes([...new Uint8Array(await r2.arrayBuffer())])
1390
- var text = doc.get()
1391
- doc.free()
1392
-
1393
- return r2.headers.get('current-version') + ' ' + text + ' ' + JSON.parse(r2.headers.get('current-version'))
1394
- },
1395
- '"hi\\u2211-1" x hi∑-1'
1396
- )
1397
-
1398
- runTest(
1399
- "test GETing current version explicitly with transfer-encoding dt",
1400
- async () => {
1401
- await dt_p
1402
- let key = 'test-' + Math.random().toString(36).slice(2)
1403
-
1404
- let r = await braid_fetch(`/${key}`, {
1405
- method: 'PUT',
1406
- version: ['hi∑-1'],
1407
- parents: [],
1408
- body: 'xy'
1409
- })
1410
- if (!r.ok) throw 'got: ' + r.statusCode
1411
-
1412
- let r2 = await braid_fetch(`/${key}`, {
1413
- version: ['hi∑-1'],
1414
- headers: {
1415
- 'Accept-Transfer-Encoding': 'dt'
1416
- }
1417
- })
1418
-
1419
- var doc = new Doc('yo')
1420
- doc.mergeBytes([...new Uint8Array(await r2.arrayBuffer())])
1421
- var text = doc.get()
1422
- doc.free()
1423
-
1424
- return r2.headers.get('current-version') + ' ' + text + ' ' + JSON.parse(r2.headers.get('current-version'))
1425
- },
1426
- '"hi\\u2211-1" xy hi∑-1'
1427
- )
1428
-
1429
- runTest(
1430
- "test for Current-Version when GETing old version",
1431
- async () => {
1432
- await dt_p
1433
- let key = 'test-' + Math.random().toString(36).slice(2)
1434
-
1435
- let r = await braid_fetch(`/${key}`, {
1436
- method: 'PUT',
1437
- version: ['hi∑-1'],
1438
- parents: [],
1439
- body: 'xy'
1440
- })
1441
- if (!r.ok) throw 'got: ' + r.statusCode
1442
-
1443
- let r2 = await braid_fetch(`/${key}`, {
1444
- version: ['hi∑-0']
1445
- })
1446
-
1447
- var text = await r2.text()
1448
-
1449
- return r2.headers.get('current-version') + ' ' + r2.headers.get('version') + ' ' + text + ' ' + JSON.parse(r2.headers.get('current-version'))
1450
- },
1451
- '"hi\\u2211-1" "hi\\u2211-0" x hi∑-1'
1452
- )
1453
-
1454
- runTest(
1455
- "test HEAD for GET without subscribe",
1456
- async () => {
1457
- await dt_p
1458
- let key = 'test-' + Math.random().toString(36).slice(2)
1459
-
1460
- let r = await braid_fetch(`/${key}`, {
1461
- method: 'PUT',
1462
- version: ['hi∑-1'],
1463
- parents: [],
1464
- body: 'xy'
1465
- })
1466
- if (!r.ok) throw 'got: ' + r.statusCode
1467
-
1468
- let r2 = await braid_fetch(`/${key}`, {
1469
- method: 'HEAD'
1470
- })
1471
-
1472
- var text = await r2.text()
1473
-
1474
- return r2.headers.get('version') + ' ' + JSON.parse(r2.headers.get('version')) + ` text:[${text}]`
1475
- },
1476
- '"hi\\u2211-1" hi∑-1 text:[]'
1477
- )
1478
-
1479
- runTest(
1480
- "test HEAD for GET without subscribe (with transfer-encoding)",
1481
- async () => {
1482
- await dt_p
1483
- let key = 'test-' + Math.random().toString(36).slice(2)
1484
-
1485
- let r = await braid_fetch(`/${key}`, {
1486
- method: 'PUT',
1487
- version: ['hi∑-1'],
1488
- parents: [],
1489
- body: 'xy'
1490
- })
1491
- if (!r.ok) throw 'got: ' + r.statusCode
1492
-
1493
- let r2 = await braid_fetch(`/${key}`, {
1494
- method: 'HEAD',
1495
- headers: {
1496
- 'accept-transfer-encoding': 'dt'
1497
- }
1498
- })
1499
-
1500
- var buf = await r2.arrayBuffer()
1501
-
1502
- return r2.headers.get('current-version') + ' ' + JSON.parse(r2.headers.get('current-version')) + ` buf.byteLength:${buf.byteLength}`
1503
- },
1504
- '"hi\\u2211-1" hi∑-1 buf.byteLength:0'
1505
- )
1506
-
1507
- runTest(
1508
- "test accept-encoding updates(dt) (with parents)",
1509
- async () => {
1510
- await dt_p
1511
- let key = 'test-' + Math.random().toString(36).slice(2)
1512
- var doc = new Doc('hi')
1513
- doc.ins(0, 'x')
1514
-
1515
- let r = await braid_fetch(`/${key}`, {
1516
- method: 'PUT',
1517
- version: ['hi-1'],
1518
- parents: [],
1519
- body: 'xy'
1520
- })
1521
- if (!r.ok) throw 'got: ' + r.statusCode
1522
-
1523
- var a = new AbortController()
1524
- let r2 = await braid_fetch(`/${key}`, {
1525
- signal: a.signal,
1526
- parents: ['hi-0'],
1527
- subscribe: true,
1528
- headers: {
1529
- 'merge-type': 'dt',
1530
- 'X-Accept-Encoding': 'updates(dt)'
1531
- }
1532
- })
1533
-
1534
- return await new Promise(done => {
1535
- r2.subscribe(u => {
1536
- doc.mergeBytes(u.body)
1537
- done(doc.get())
1538
- doc.free()
1539
- a.abort()
1540
- })
1541
- })
1542
- },
1543
- 'xy'
1544
- )
1545
-
1546
- runTest(
1547
- "test accept-encoding updates(dt) (with parents which are current version)",
1548
- async () => {
1549
- await dt_p
1550
- let key = 'test-' + Math.random().toString(36).slice(2)
1551
- var doc = new Doc('hi')
1552
- doc.ins(0, 'xy')
1553
-
1554
- let r = await braid_fetch(`/${key}`, {
1555
- method: 'PUT',
1556
- version: ['hi-1'],
1557
- parents: [],
1558
- body: 'xy'
1559
- })
1560
- if (!r.ok) throw 'got: ' + r.statusCode
1561
-
1562
- var a = new AbortController()
1563
- let r2 = await braid_fetch(`/${key}`, {
1564
- signal: a.signal,
1565
- parents: ['hi-1'],
1566
- subscribe: true,
1567
- headers: {
1568
- 'merge-type': 'dt',
1569
- 'X-Accept-Encoding': 'updates(dt)'
1570
- }
1571
- })
1572
-
1573
- return await new Promise(done => {
1574
- r2.subscribe(u => {
1575
- doc.mergeBytes(u.body)
1576
- done(doc.get())
1577
- doc.free()
1578
- a.abort()
1579
- })
1580
- })
1581
- },
1582
- 'xy'
1583
- )
1584
-
1585
- runTest(
1586
- "test accept-encoding updates(dt)",
1587
- async () => {
1588
- await dt_p
1589
- let key = 'test-' + Math.random().toString(36).slice(2)
1590
-
1591
- let r = await braid_fetch(`/${key}`, {
1592
- method: 'PUT',
1593
- version: ['hi-1'],
1594
- parents: [],
1595
- body: 'xy'
1596
- })
1597
- if (!r.ok) throw 'got: ' + r.statusCode
1598
-
1599
- var a = new AbortController()
1600
- let r2 = await braid_fetch(`/${key}`, {
1601
- signal: a.signal,
1602
- subscribe: true,
1603
- headers: {
1604
- 'merge-type': 'dt',
1605
- 'X-Accept-Encoding': 'updates(dt)'
1606
- }
1607
- })
1608
-
1609
- var doc = new Doc('yo')
1610
- return await new Promise(done => {
1611
- r2.subscribe(u => {
1612
- doc.mergeBytes(u.body)
1613
- done(doc.get())
1614
- doc.free()
1615
- a.abort()
1616
- })
1617
- })
1618
- },
1619
- 'xy'
1620
- )
1621
-
1622
- runTest(
1623
- "test accept-encoding updates(dt), getting non-encoded update",
1624
- async () => {
1625
- await dt_p
1626
- let key = 'test-' + Math.random().toString(36).slice(2)
1627
-
1628
- let r = await braid_fetch(`/${key}`, {
1629
- method: 'PUT',
1630
- version: ['hi-1'],
1631
- parents: [],
1632
- body: 'xy'
1633
- })
1634
- if (!r.ok) throw 'got: ' + r.statusCode
1635
-
1636
- var a = new AbortController()
1637
- let r2 = await braid_fetch(`/${key}`, {
1638
- signal: a.signal,
1639
- subscribe: true,
1640
- headers: {
1641
- 'merge-type': 'dt',
1642
- 'X-Accept-Encoding': 'updates(dt)'
1643
- }
1644
- })
1645
-
1646
- setTimeout(async () => {
1647
- await braid_fetch(`/${key}`, {
1648
- method: 'PUT',
1649
- version: ['yo-0'],
1650
- parents: ['hi-1'],
1651
- patches: [{unit: 'text', range: '[2:2]', content: 'z'}]
1652
- })
1653
- }, 200)
1654
-
1655
- var results = []
1656
-
1657
- var doc = new Doc('yo')
1658
- return await new Promise(done => {
1659
- r2.subscribe(u => {
1660
- if (!u.status) {
1661
- doc.mergeBytes(u.body)
1662
- results.push(doc.get())
1663
- doc.free()
1664
- } else {
1665
- results.push(u.patches[0].content_text)
1666
- done(results.join(''))
1667
- a.abort()
1668
- }
1669
- })
1670
- })
1671
- },
1672
- 'xyz'
1673
- )
1674
-
1675
- runTest(
1676
- "test Version we get from PUTing",
1677
- async () => {
1678
- await dt_p
1679
- let key = 'test-' + Math.random().toString(36).slice(2)
1680
-
1681
- let r = await braid_fetch(`/${key}`, {
1682
- method: 'PUT',
1683
- version: ['hi∑-1'],
1684
- parents: [],
1685
- body: 'xy'
1686
- })
1687
- if (!r.ok) throw 'got: ' + r.statusCode
1688
-
1689
- return r.headers.get('version')
1690
- },
1691
- '"hi\\u2211-1"'
1692
- )
1693
-
1694
- runTest(
1695
- "test error code when missing parents",
1696
- async () => {
1697
- let key = 'test-' + Math.random().toString(36).slice(2)
1698
- let r = await braid_fetch(`/${key}`, {
1699
- method: 'PUT',
1700
- version: ['hi-1'],
1701
- parents: ['missing-0', 'y😀-0'],
1702
- body: 'xx'
1703
- })
1704
- return r.status + ' ' + r.ok + ' ' + r.statusText + ' ' + r.headers.get('Version')
1705
- },
1706
- '309 false Version Unknown Here "missing-0", "y\\ud83d\\ude00-0"'
1707
- )
1708
-
1709
- runTest(
1710
- "test subscribing starting at a version using simpleton",
1711
- async () => {
1712
- let key = 'test-' + Math.random().toString(36).slice(2)
1713
-
1714
- let r = await braid_fetch(`/${key}`, {
1715
- method: 'PUT',
1716
- version: ['hi-1'],
1717
- parents: [],
1718
- body: 'xx'
1719
- })
1720
- if (!r.ok) throw 'got: ' + r.statusCode
1721
-
1722
- var a = new AbortController()
1723
- let r2 = await braid_fetch(`/${key}`, {
1724
- signal: a.signal,
1725
- version: ['hi-0'],
1726
- subscribe: true
1727
- })
1728
- return await new Promise(async (done, fail) => {
1729
- r2.subscribe(update => {
1730
- done(JSON.stringify(update.parents))
1731
- a.abort()
1732
- }, fail)
1733
- })
1734
- },
1735
- JSON.stringify([ "hi-0" ])
1736
- )
1737
-
1738
- runTest(
1739
- "test subscribing starting at a version using dt",
1740
- async () => {
1741
- let key = 'test-' + Math.random().toString(36).slice(2)
1742
-
1743
- let r = await braid_fetch(`/${key}`, {
1744
- method: 'PUT',
1745
- version: ['hi-1'],
1746
- parents: [],
1747
- body: 'xx'
1748
- })
1749
- if (!r.ok) throw 'got: ' + r.statusCode
1750
-
1751
- var a = new AbortController()
1752
- let r2 = await braid_fetch(`/${key}`, {
1753
- signal: a.signal,
1754
- version: ['hi-0'],
1755
- subscribe: true,
1756
- headers: {
1757
- 'Merge-Type': 'dt'
1758
- }
1759
- })
1760
- return r2.headers.get('merge-type') + ':' + await new Promise(async (done, fail) => {
1761
- r2.subscribe(update => {
1762
- done(JSON.stringify(update.parents))
1763
- a.abort()
1764
- }, fail)
1765
- })
1766
- },
1767
- 'dt:' + JSON.stringify([ "hi-0" ])
1768
- )
1769
-
1770
- runTest(
1771
- "test subscribing starting at the latest version using dt",
1772
- async () => {
1773
- let key = 'test-' + Math.random().toString(36).slice(2)
1774
-
1775
- let r = await braid_fetch(`/${key}`, {
1776
- method: 'PUT',
1777
- version: ['hi-1'],
1778
- parents: [],
1779
- body: 'xx'
1780
- })
1781
- if (!r.ok) throw 'got: ' + r.statusCode
1782
-
1783
- var a = new AbortController()
1784
- let r2 = await braid_fetch(`/${key}`, {
1785
- signal: a.signal,
1786
- version: ['hi-1'],
1787
- subscribe: true,
1788
- headers: {
1789
- 'Merge-Type': 'dt'
1790
- }
1791
- })
1792
- return await new Promise(async (done, fail) => {
1793
- r2.subscribe(update => {
1794
- done('got something')
1795
- a.abort()
1796
- }, fail)
1797
- setTimeout(() => {
1798
- done('got nothing')
1799
- a.abort()
1800
- }, 1500)
1801
- })
1802
- },
1803
- 'got nothing'
1804
- )
1805
-
1806
- runTest(
1807
- "test subscribing starting at beginning using dt",
1808
- async () => {
1809
- let key = 'test-' + Math.random().toString(36).slice(2)
1810
-
1811
- let r = await braid_fetch(`/${key}`, {
1812
- method: 'PUT',
1813
- version: ['hi-1'],
1814
- parents: [],
1815
- body: 'xx'
1816
- })
1817
- if (!r.ok) throw 'got: ' + r.statusCode
1818
-
1819
- var a = new AbortController()
1820
- let r2 = await braid_fetch(`/${key}`, {
1821
- signal: a.signal,
1822
- subscribe: true,
1823
- headers: {
1824
- 'Merge-Type': 'dt'
1825
- }
1826
- })
1827
- return r2.headers.get('merge-type') + ':' + await new Promise(async (done, fail) => {
1828
- r2.subscribe(update => {
1829
- if (update.version[0] === 'hi-1') {
1830
- done('got it!')
1831
- a.abort()
1832
- }
1833
- }, fail)
1834
- })
1835
- },
1836
- 'dt:got it!'
1837
- )
1838
-
1839
- runTest(
1840
- "test dt_create_bytes with big agent name",
1841
- async () => {
1842
- let x = await (await fetch(`/test.html?dt_create_bytes_big_name`)).json()
1843
- return JSON.stringify(x)
1844
- },
1845
- JSON.stringify({ok: true})
1846
- )
1847
-
1848
- runTest(
1849
- "test dt_create_bytes with many agent names",
1850
- async () => {
1851
- let x = await (await fetch(`/test.html?dt_create_bytes_many_names`)).json()
1852
- return JSON.stringify(x)
1853
- },
1854
- JSON.stringify({ok: true})
1855
- )
1856
-
1857
- runTest(
1858
- "test deleting a resource",
1859
- async () => {
1860
- let key = 'test-' + Math.random().toString(36).slice(2)
1861
-
1862
- await fetch(`/${key}`, {
1863
- method: 'PUT',
1864
- body: 'hi'
1865
- })
1866
-
1867
- await fetch(`/${key}`, {method: 'DELETE'})
1868
-
1869
- let r = await fetch(`/${key}`)
1870
-
1871
- return await r.text()
1872
- },
1873
- ''
1874
- )
1875
-
1876
- runTest(
1877
- "test deleting a resource completely removes all traces",
1878
- async () => {
1879
- let key = 'test-delete-complete-' + Math.random().toString(36).slice(2)
1880
-
1881
- // Create a resource with some content
1882
- // "hello world" is 11 characters, so version should be alice-10 (positions 0-10 inclusive)
1883
- await braid_fetch(`/${key}`, {
1884
- method: 'PUT',
1885
- version: ['alice-10'],
1886
- parents: [],
1887
- body: 'hello world'
1888
- })
1889
-
1890
- // Verify it exists in cache using eval endpoint
1891
- let r1 = await braid_fetch(`/eval`, {
1892
- method: 'PUT',
1893
- body: `res.end(braid_text.cache['/${key}'] ? 'exists' : 'missing')`
1894
- })
1895
- if ((await r1.text()) !== 'exists') return 'Resource not in cache after creation'
1896
-
1897
- // Delete the resource
1898
- await braid_fetch(`/${key}`, {method: 'DELETE'})
1899
-
1900
- // Verify it's removed from cache
1901
- let r2 = await braid_fetch(`/eval`, {
1902
- method: 'PUT',
1903
- body: `res.end(braid_text.cache['/${key}'] ? 'exists' : 'missing')`
1904
- })
1905
- if ((await r2.text()) !== 'missing') return 'Resource still in cache after deletion'
1906
-
1907
- // Verify we can create it again from scratch with same key
1908
- // "new content" is 11 characters, so version should be bob-10 (positions 0-10 inclusive)
1909
- await braid_fetch(`/${key}`, {
1910
- method: 'PUT',
1911
- version: ['bob-10'],
1912
- parents: [],
1913
- body: 'new content'
1914
- })
1915
-
1916
- // Get the new resource and verify it's fresh (not the old one)
1917
- let r = await braid_fetch(`/${key}`)
1918
- let body = await r.text()
1919
-
1920
- if (body !== 'new content') return `Expected 'new content', got '${body}'`
1921
-
1922
- // Verify the version is from scratch (bob-10, not alice-10)
1923
- let version = r.headers.get('version')
1924
- if (!version.includes('bob-10')) return `Expected version to include bob-10, got: ${version}`
1925
- if (version.includes('alice-')) return `Old version alice-10 should not be present, got: ${version}`
1926
-
1927
- return 'ok'
1928
- },
1929
- 'ok'
1930
- )
1931
-
1932
- runTest(
1933
- "test braid_text.delete(url)",
1934
- async () => {
1935
- var key = 'test-' + Math.random().toString(36).slice(2)
1936
-
1937
- // Create a resource first
1938
- await braid_fetch(`/${key}`, {
1939
- method: 'PUT',
1940
- body: 'hello there'
1941
- })
1942
-
1943
- // Verify it exists
1944
- let r1 = await braid_fetch(`/${key}`)
1945
- if ((await r1.text()) !== 'hello there') return 'Resource not created properly'
1946
-
1947
- // Delete using braid_text.delete(url)
1948
- var r = await braid_fetch(`/eval`, {
1949
- method: 'PUT',
1950
- body: `void (async () => {
1951
- await braid_text.delete(new URL('http://localhost:8889/${key}'))
1952
- res.end('deleted')
1953
- })()`
1954
- })
1955
- if (!r.ok) return 'delete failed: ' + r.status
1956
- if ((await r.text()) !== 'deleted') return 'delete did not complete'
1957
-
1958
- // Verify it's deleted (should be empty)
1959
- let r2 = await braid_fetch(`/${key}`)
1960
- return 'got: ' + (await r2.text())
1961
- },
1962
- 'got: '
1963
- )
1964
-
1965
- runTest(
1966
- "test braid_text.get(url) returns null for 404",
1967
- async () => {
1968
- // Use the /404 endpoint that always returns 404
1969
- var r = await braid_fetch(`/eval`, {
1970
- method: 'PUT',
1971
- body: `void (async () => {
1972
- var result = await braid_text.get(new URL('http://localhost:8889/404'))
1973
- res.end(result === null ? 'null' : 'not null: ' + result)
1974
- })()`
1975
- })
1976
- return await r.text()
1977
- },
1978
- 'null'
1979
- )
1980
-
1981
- runTest(
1982
- "test braid_text.sync handles remote not existing yet",
1983
- async () => {
1984
- var local_key = 'test-local-' + Math.random().toString(36).slice(2)
1985
- var remote_key = 'test-remote-' + Math.random().toString(36).slice(2)
1986
-
1987
- // Start sync between a local key and a remote URL that doesn't exist yet
1988
- // The sync should wait for local to create something, then push to remote
1989
- var r = await braid_fetch(`/eval`, {
1990
- method: 'PUT',
1991
- body: `void (async () => {
1992
- var ac = new AbortController()
1993
-
1994
- // Start sync - remote doesn't exist yet
1995
- braid_text.sync('/${local_key}', new URL('http://localhost:8889/${remote_key}'), {
1996
- signal: ac.signal
1997
- })
1998
-
1999
- // Wait a bit then put something locally
2000
- await new Promise(done => setTimeout(done, 100))
2001
- await braid_text.put('/${local_key}', { body: 'created locally' })
2002
-
2003
- // Wait for sync to propagate
2004
- await new Promise(done => setTimeout(done, 200))
2005
-
2006
- // Stop sync
2007
- ac.abort()
2008
-
2009
- res.end('done')
2010
- })()`
2011
- })
2012
- if (!r.ok) return 'eval failed: ' + r.status
2013
-
2014
- // Check that remote now has the content
2015
- var r2 = await braid_fetch(`/${remote_key}`)
2016
- return await r2.text()
2017
- },
2018
- 'created locally'
2019
- )
2020
-
2021
- runTest(
2022
- "test braid_text.sync on_res callback",
2023
- async () => {
2024
- var local_key = 'test-local-' + Math.random().toString(36).slice(2)
2025
- var remote_key = 'test-remote-' + Math.random().toString(36).slice(2)
2026
-
2027
- // Create the remote resource first
2028
- var r = await braid_fetch(`/${remote_key}`, {
2029
- method: 'PUT',
2030
- body: 'remote content'
2031
- })
2032
- if (!r.ok) return 'put failed: ' + r.status
2033
-
2034
- // Start sync with on_res callback and verify it gets called
2035
- var r = await braid_fetch(`/eval`, {
2036
- method: 'PUT',
2037
- body: `void (async () => {
2038
- var ac = new AbortController()
2039
- var got_res = false
2040
-
2041
- braid_text.sync('/${local_key}', new URL('http://localhost:8889/${remote_key}'), {
2042
- signal: ac.signal,
2043
- on_res: (response) => {
2044
- got_res = response && typeof response.headers !== 'undefined'
2045
- }
2046
- })
2047
-
2048
- // Wait for sync to establish and on_res to be called
2049
- await new Promise(done => setTimeout(done, 200))
2050
-
2051
- ac.abort()
2052
- res.end(got_res ? 'on_res called' : 'on_res not called')
2053
- })()`
2054
- })
2055
- if (!r.ok) return 'eval failed: ' + r.status
2056
-
2057
- return await r.text()
2058
- },
2059
- 'on_res called'
2060
- )
2061
-
2062
- runTest(
2063
- "test braid_text.sync uses accept-encoding updates(dt)",
2064
- async () => {
2065
- var remote_key = 'test-remote-' + Math.random().toString(36).slice(2)
2066
- var local_key = 'test-local-' + Math.random().toString(36).slice(2)
2067
-
2068
- // Create the remote resource with some content
2069
- var r = await braid_fetch(`/${remote_key}`, {
2070
- method: 'PUT',
2071
- body: 'remote content here'
2072
- })
2073
- if (!r.ok) return 'put failed: ' + r.status
2074
-
2075
- // Start sync with URL first (like the passing "url to key" test)
2076
- var r = await braid_fetch(`/eval`, {
2077
- method: 'PUT',
2078
- body: `void (async () => {
2079
- braid_text.sync(new URL('http://localhost:8889/${remote_key}'), '/${local_key}')
2080
- res.end('')
2081
- })()`
2082
- })
2083
- if (!r.ok) return 'eval failed: ' + r.status
2084
-
2085
- // Wait for sync to complete
2086
- await new Promise(done => setTimeout(done, 100))
2087
-
2088
- // Check local has remote content
2089
- var r = await braid_fetch(`/${local_key}`)
2090
- return await r.text()
2091
- },
2092
- 'remote content here'
2093
- )
2094
-
2095
- runTest(
2096
- "test braid_text.sync reconnects when inner put fails with non-200 status",
2097
- async () => {
2098
- var key = 'test-' + Math.random().toString(36).slice(2)
2099
-
2100
- // Create a local resource with content
2101
- var r = await braid_fetch(`/${key}`, {
2102
- method: 'PUT',
2103
- body: 'initial'
2104
- })
2105
- if (!r.ok) return 'initial put failed: ' + r.status
2106
-
2107
- var r = await braid_fetch(`/eval`, {
2108
- method: 'PUT',
2109
- body: `void (async () => {
2110
- var connect_count = 0
2111
- var ac = new AbortController()
2112
-
2113
- braid_text.sync('/${key}', new URL('http://localhost:8889/server_error'), {
2114
- signal: ac.signal,
2115
- on_pre_connect: () => {
2116
- connect_count++
2117
- if (connect_count >= 2) {
2118
- ac.abort()
2119
- res.end('reconnected after put failure')
2120
- }
2121
- }
2122
- })
2123
-
2124
- // Trigger a local put which will fail when synced to the error endpoint
2125
- await new Promise(done => setTimeout(done, 100))
2126
- await braid_text.put('/${key}', { body: 'trigger sync' })
2127
-
2128
- // Wait for reconnect attempt
2129
- await new Promise(done => setTimeout(done, 2000))
2130
- ac.abort()
2131
- res.end('did not reconnect')
2132
- })()`
2133
- })
2134
- if (!r.ok) return 'eval failed: ' + r.status
2135
-
2136
- return await r.text()
2137
- },
2138
- 'reconnected after put failure'
2139
- )
2140
-
2141
- runTest(
2142
- "test braid_text.sync on_unauthorized callback for 401",
2143
- async () => {
2144
- var key = 'test-' + Math.random().toString(36).slice(2)
2145
-
2146
- // Create a local resource with content
2147
- var r = await braid_fetch(`/${key}`, {
2148
- method: 'PUT',
2149
- body: 'initial'
2150
- })
2151
- if (!r.ok) return 'initial put failed: ' + r.status
2152
-
2153
- var r = await braid_fetch(`/eval`, {
2154
- method: 'PUT',
2155
- body: `void (async () => {
2156
- var unauthorized_called = false
2157
- var ac = new AbortController()
2158
-
2159
- braid_text.sync('/${key}', new URL('http://localhost:8889/unauthorized'), {
2160
- signal: ac.signal,
2161
- on_unauthorized: () => {
2162
- unauthorized_called = true
2163
- ac.abort()
2164
- res.end('on_unauthorized called')
2165
- }
2166
- })
2167
-
2168
- // Trigger a local put which will get 401 when synced
2169
- await new Promise(done => setTimeout(done, 100))
2170
- await braid_text.put('/${key}', { body: 'trigger sync' })
2171
-
2172
- // Wait for callback
2173
- await new Promise(done => setTimeout(done, 2000))
2174
- ac.abort()
2175
- res.end('on_unauthorized not called')
2176
- })()`
2177
- })
2178
- if (!r.ok) return 'eval failed: ' + r.status
2179
-
2180
- return await r.text()
2181
- },
2182
- 'on_unauthorized called'
2183
- )
2184
-
2185
- runTest(
2186
- "test braid_text.sync on_unauthorized callback for 403",
2187
- async () => {
2188
- var key = 'test-' + Math.random().toString(36).slice(2)
2189
-
2190
- // Create a local resource with content
2191
- var r = await braid_fetch(`/${key}`, {
2192
- method: 'PUT',
2193
- body: 'initial'
2194
- })
2195
- if (!r.ok) return 'initial put failed: ' + r.status
2196
-
2197
- var r = await braid_fetch(`/eval`, {
2198
- method: 'PUT',
2199
- body: `void (async () => {
2200
- var unauthorized_called = false
2201
- var ac = new AbortController()
2202
-
2203
- braid_text.sync('/${key}', new URL('http://localhost:8889/forbidden'), {
2204
- signal: ac.signal,
2205
- on_unauthorized: () => {
2206
- unauthorized_called = true
2207
- ac.abort()
2208
- res.end('on_unauthorized called')
2209
- }
2210
- })
2211
-
2212
- // Trigger a local put which will get 403 when synced
2213
- await new Promise(done => setTimeout(done, 100))
2214
- await braid_text.put('/${key}', { body: 'trigger sync' })
2215
-
2216
- // Wait for callback
2217
- await new Promise(done => setTimeout(done, 2000))
2218
- ac.abort()
2219
- res.end('on_unauthorized not called')
2220
- })()`
2221
- })
2222
- if (!r.ok) return 'eval failed: ' + r.status
2223
-
2224
- return await r.text()
2225
- },
2226
- 'on_unauthorized called'
2227
- )
2228
-
2229
- runTest(
2230
- "test getting a binary update from a subscription",
2231
- async () => {
2232
- return await new Promise(async (done, fail) => {
2233
- let key = 'test-' + Math.random().toString(36).slice(2)
2234
-
2235
- await fetch(`/${key}`, {
2236
- method: 'PUT',
2237
- body: JSON.stringify({a: 5, b: 6}, null, 4)
2238
- })
2239
-
2240
- var a = new AbortController()
2241
- let r = await braid_fetch(`/${key}`, {
2242
- signal: a.signal,
2243
- subscribe: true
2244
- })
2245
-
2246
- r.subscribe(update => {
2247
- done(update.body_text)
2248
- a.abort()
2249
- }, fail)
2250
- })
2251
- },
2252
- JSON.stringify({a: 5, b: 6}, null, 4)
2253
- )
2254
-
2255
- runTest(
2256
- "test sending a json patch to some json-text",
2257
- async () => {
2258
- let key = 'test-' + Math.random().toString(36).slice(2)
2259
-
2260
- await fetch(`/${key}`, {
2261
- method: 'PUT',
2262
- body: JSON.stringify({a: 5, b: 6})
2263
- })
2264
-
2265
- await fetch(`/${key}`, {
2266
- method: 'PUT',
2267
- headers: { 'Content-Range': 'json a' },
2268
- body: '67'
2269
- })
2270
-
2271
- let r = await fetch(`/${key}`)
2272
-
2273
- return await r.text()
2274
- },
2275
- JSON.stringify({a: 67, b: 6}, null, 4)
2276
- )
2277
-
2278
- runTest(
2279
- "test sending multiple json patches to some json-text",
2280
- async () => {
2281
- let key = 'test-' + Math.random().toString(36).slice(2)
2282
-
2283
- await fetch(`/${key}`, {
2284
- method: 'PUT',
2285
- body: JSON.stringify({a: 5, b: 6, c: 7})
2286
- })
2287
-
2288
- await braid_fetch(`/${key}`, {
2289
- method: 'PUT',
2290
- headers: { 'Content-Range': 'json a' },
2291
- patches: [{
2292
- unit: 'json',
2293
- range: 'a',
2294
- content: '55',
2295
- }, {
2296
- unit: 'json',
2297
- range: 'b',
2298
- content: '66',
2299
- }]
2300
- })
2301
-
2302
- let r = await fetch(`/${key}`)
2303
-
2304
- return await r.text()
2305
- },
2306
- JSON.stringify({a: 55, b: 66, c: 7}, null, 4)
2307
- )
2308
-
2309
- runTest(
2310
- "test deleting something using a json patch",
2311
- async () => {
2312
- let key = 'test-' + Math.random().toString(36).slice(2)
2313
-
2314
- await fetch(`/${key}`, {
2315
- method: 'PUT',
2316
- body: JSON.stringify({a: 5, b: 6}, null, 4)
2317
- })
2318
-
2319
- await fetch(`/${key}`, {
2320
- method: 'PUT',
2321
- headers: { 'Content-Range': 'json a' },
2322
- body: ''
2323
- })
2324
-
2325
- let r = await fetch(`/${key}`)
2326
-
2327
- return await r.text()
2328
- },
2329
- JSON.stringify({b: 6}, null, 4)
2330
- )
2331
-
2332
- runTest(
2333
- "test length updating",
2334
- async () => {
2335
- let key = 'test-' + Math.random().toString(36).slice(2)
2336
-
2337
- await fetch(`/${key}`, { method: 'PUT', body: '' })
2338
- await fetch(`/${key}`, { method: 'PUT', body: '0123456789' })
2339
- await fetch(`/${key}`, { method: 'PUT', body: '0123456789' })
2340
- await fetch(`/${key}`, { method: 'PUT', body: '0123456789' })
2341
-
2342
- let r = await fetch(`/${key}`, { method: 'HEAD' })
2343
- return '' + parseInt(r.headers.get('version').split('-')[1])
2344
- },
2345
- '19'
2346
- )
2347
-
2348
- runTest(
2349
- "test retry when parents not there..",
2350
- async () => {
2351
- return await new Promise(done => {
2352
- var count = 0
2353
- var key = 'test-' + Math.random().toString(36).slice(2)
2354
- var a = new AbortController()
2355
- braid_fetch(`/${key}`, {
2356
- signal: a.signal,
2357
- multiplex: false,
2358
- method: 'PUT',
2359
- version: ['hi-3'],
2360
- parents: ['hi-1'],
2361
- body: 'xx',
2362
- onFetch: () => {
2363
- count++
2364
- if (count === 2) {
2365
- done('retried!')
2366
- a.abort()
2367
- }
2368
- },
2369
- retry: true
2370
- })
2371
- })
2372
- },
2373
- 'retried!'
2374
- )
2375
-
2376
- runTest(
2377
- "test asking for a version that should and shouldn't be there",
2378
- async () => {
2379
- var key = 'test-' + Math.random().toString(36).slice(2)
2380
-
2381
- var r = await braid_fetch(`/${key}`, {
2382
- method: 'PUT',
2383
- version: ['hi-10'],
2384
- parents: [],
2385
- body: 'x'
2386
- })
2387
- if (!r.ok) throw 'got: ' + r.statusCode
2388
-
2389
- var r = await braid_fetch(`/${key}`, {
2390
- method: 'HEAD',
2391
- version: ['hi-5']
2392
- })
2393
- if (r.status !== 309) throw 'expected 309, got: ' + r.status
2394
- if (r.statusText !== 'Version Unknown Here') throw 'unexpected status text: ' + r.statusText
2395
- if (r.ok) throw 'found version we should not have found'
2396
-
2397
- var r = await braid_fetch(`/${key}`, {
2398
- method: 'HEAD',
2399
- version: ['hi-10']
2400
- })
2401
- if (!r.ok) throw 'could not find version we should have found'
2402
-
2403
- return 'worked out!'
2404
- },
2405
- 'worked out!'
2406
- )
2407
-
2408
- runTest(
2409
- "test asking for parents that should and shouldn't be there",
2410
- async () => {
2411
- var key = 'test-' + Math.random().toString(36).slice(2)
2412
-
2413
- var r = await braid_fetch(`/${key}`, {
2414
- method: 'PUT',
2415
- version: ['hi-10'],
2416
- parents: [],
2417
- body: 'x'
2418
- })
2419
- if (!r.ok) throw 'got: ' + r.statusCode
2420
-
2421
- var r = await braid_fetch(`/${key}`, {
2422
- method: 'HEAD',
2423
- parents: ['hi-5']
2424
- })
2425
- if (r.status !== 309) throw 'expected 309, got: ' + r.status
2426
- if (r.ok) throw 'found parents we should not have found'
2427
-
2428
- var r = await braid_fetch(`/${key}`, {
2429
- method: 'HEAD',
2430
- parents: ['hi-10']
2431
- })
2432
- if (!r.ok) throw 'could not find parents we should have found'
2433
-
2434
- return 'worked out!'
2435
- },
2436
- 'worked out!'
2437
- )
2438
-
2439
- runTest(
2440
- "test that 309 returns all missing events",
2441
- async () => {
2442
- var key = 'test-' + Math.random().toString(36).slice(2)
2443
-
2444
- var r = await braid_fetch(`/${key}`, {
2445
- method: 'PUT',
2446
- version: ['hi-11'],
2447
- parents: [],
2448
- body: 'xyz'
2449
- })
2450
- if (!r.ok) throw 'got: ' + r.statusCode
2451
-
2452
- var r = await braid_fetch(`/${key}`, {
2453
- method: 'HEAD',
2454
- version: ['yo-1', 'hi-11'],
2455
- parents: ['hi-5', 'hi-8', 'hi-9', 'hi-10']
2456
- })
2457
- if (r.status !== 309) throw 'expected 309, got: ' + r.status
2458
- return r.headers.get('version')
2459
- },
2460
- '"yo-1", "hi-5", "hi-8"'
2461
- )
2462
-
2463
- runTest(
2464
- "test that subscribe returns current-version header",
2465
- async () => {
2466
- var key = 'test-' + Math.random().toString(36).slice(2)
2467
-
2468
- var r = await braid_fetch(`/${key}`, {
2469
- method: 'PUT',
2470
- version: ['hi-11'],
2471
- parents: [],
2472
- body: 'xyz'
2473
- })
2474
- if (!r.ok) throw 'got: ' + r.statusCode
2475
-
2476
- var a = new AbortController()
2477
- var r = await braid_fetch(`/${key}`, {
2478
- signal: a.signal,
2479
- subscribe: true
2480
- })
2481
- var result = r.headers.get('current-version')
2482
- a.abort()
2483
- return result
2484
- },
2485
- '"hi-11"'
2486
- )
2487
-
2488
- runTest(
2489
- "test case-insensitive filesystem handling (/a vs /A)",
2490
- async () => {
2491
- // This test verifies that keys differing only in case are stored
2492
- // in separate files on case-insensitive filesystems (Mac/Windows)
2493
- var key_lower = '/test-case-' + Math.random().toString(36).slice(2)
2494
- var key_upper = key_lower.toUpperCase()
2495
-
2496
- // Store different values for lowercase and uppercase keys
2497
- // Then clear cache and reload from disk to verify filesystem storage
2498
- var r1 = await braid_fetch(`/eval`, {
2499
- method: 'PUT',
2500
- body: `void (async () => {
2501
- await braid_text.put('${key_lower}', {body: 'lowercase-value'})
2502
- await braid_text.put('${key_upper}', {body: 'uppercase-value'})
2503
-
2504
- // Wait for disk write
2505
- await new Promise(done => setTimeout(done, 200))
2506
-
2507
- // Clear the in-memory cache to force reload from disk
2508
- // Note: We keep the key_to_encoded mapping intact since that persists across cache clears
2509
- delete braid_text.cache['${key_lower}']
2510
- delete braid_text.cache['${key_upper}']
2511
-
2512
- // Also need to wait for any async file operations
2513
- await new Promise(done => setTimeout(done, 100))
2514
-
2515
- // Read back from disk - pass empty options to force loading
2516
- var lower = (await braid_text.get('${key_lower}', {})).body
2517
- var upper = (await braid_text.get('${key_upper}', {})).body
2518
- res.end(JSON.stringify({lower, upper}))
2519
- })()`
2520
- })
2521
- if (!r1.ok) return 'eval failed: ' + r1.status
2522
-
2523
- var result = JSON.parse(await r1.text())
2524
- if (result.lower !== 'lowercase-value') {
2525
- return 'lower mismatch: ' + result.lower
2526
- }
2527
- if (result.upper !== 'uppercase-value') {
2528
- return 'upper mismatch: ' + result.upper
2529
- }
2530
- return 'ok'
2531
- },
2532
- 'ok'
2533
- )
2534
-
2535
- runTest(
2536
- "test Merge-Type header per Braid spec",
2537
- async () => {
2538
- let key = 'test-merge-type-' + Math.random().toString(36).slice(2)
2539
-
2540
- // First PUT some content (15 characters, so version is alice-14)
2541
- await braid_fetch(`/${key}`, {
2542
- method: 'PUT',
2543
- version: ['alice-14'],
2544
- parents: [],
2545
- body: 'initial content'
2546
- })
2547
-
2548
- // Test 1: GET with Version header should include Merge-Type
2549
- let r1 = await braid_fetch(`/${key}`, {
2550
- version: ['alice-14']
2551
- })
2552
- if (!r1.headers.get('merge-type')) return 'Missing Merge-Type with Version header'
2553
-
2554
- // Test 2: GET with empty Version header should still include Merge-Type
2555
- let r2 = await braid_fetch(`/${key}`, {
2556
- headers: {
2557
- 'Version': ''
2558
- }
2559
- })
2560
- if (!r2.headers.get('merge-type')) return 'Missing Merge-Type with empty Version header'
2561
-
2562
- // Test 3: GET with Parents header should include Merge-Type
2563
- let r3 = await braid_fetch(`/${key}`, {
2564
- parents: []
2565
- })
2566
- if (!r3.headers.get('merge-type')) return 'Missing Merge-Type with Parents header'
2567
-
2568
- // Test 4: Regular GET without Version/Parents should NOT include Merge-Type
2569
- let r4 = await braid_fetch(`/${key}`)
2570
- if (r4.headers.get('merge-type')) return 'Unexpected Merge-Type without Version/Parents'
2571
-
2572
- // Test 5: GET with Subscribe should include Merge-Type
2573
- let a = new AbortController()
2574
- let r5 = await braid_fetch(`/${key}`, {
2575
- signal: a.signal,
2576
- subscribe: true
2577
- })
2578
- if (!r5.headers.get('merge-type')) return 'Missing Merge-Type with Subscribe'
2579
-
2580
- // Close subscription
2581
- a.abort()
2582
-
2583
- return 'ok'
2584
- },
2585
- 'ok'
2586
- )
2587
-
2588
- runTest(
2589
- "test filename conflict detection (different encodings of same key)",
2590
- async () => {
2591
- // This test creates two files on disk with different URL-encoded names
2592
- // that decode to the same key, then tries to load them, which should
2593
- // throw "filename conflict detected"
2594
-
2595
- // Use a unique test subdirectory to avoid affecting other tests
2596
- var testId = Math.random().toString(36).slice(2)
2597
-
2598
- var r = await braid_fetch(`/eval`, {
2599
- method: 'PUT',
2600
- body: `void (async () => {
2601
- var fs = require('fs')
2602
- var path = require('path')
2603
-
2604
- // Create a temporary test db folder
2605
- var testFolder = path.join(braid_text.db_folder, 'conflict-test-${testId}')
2606
- await fs.promises.mkdir(testFolder, { recursive: true })
2607
-
2608
- // Create two files that decode to the same key "/hello"
2609
- // File 1: Using the standard encoding with ! swapped for /
2610
- // !hello -> decodes to /hello (after !/swap)
2611
- await fs.promises.writeFile(path.join(testFolder, '!hello.0'), 'content1')
2612
-
2613
- // File 2: Using %2F encoding for /
2614
- // %2Fhello -> decodes to /hello (via URL decoding, then !/swap on /)
2615
- // Actually, let's trace through decode_filename:
2616
- // 1. decodeURIComponent('%21hello') = '!hello'
2617
- // 2. swap !/: '!hello' -> '/hello'
2618
- // So %21hello decodes to /hello
2619
- await fs.promises.writeFile(path.join(testFolder, '%21hello.0'), 'content2')
2620
-
2621
- // Now try to initialize filename mapping with these files
2622
- // We need to call the internal init_filename_mapping function
2623
- // through a resource load that reads the test directory
2624
-
2625
- try {
2626
- // Read the files from the test folder
2627
- var files = await fs.promises.readdir(testFolder)
2628
-
2629
- // Simulate what init_filename_mapping does
2630
- var key_to_filename = new Map()
2631
- for (var file of files) {
2632
- var encoded = file.replace(/\\\.\\d+$/, '')
2633
- var key = braid_text.decode_filename(encoded)
2634
-
2635
- if (!key_to_filename.has(key)) {
2636
- key_to_filename.set(key, encoded)
2637
- } else {
2638
- throw new Error('filename conflict detected')
2639
- }
2640
- }
2641
- res.end('no error thrown')
2642
- } catch (e) {
2643
- res.end(e.message)
2644
- } finally {
2645
- // Clean up test folder
2646
- try {
2647
- for (var f of await fs.promises.readdir(testFolder)) {
2648
- await fs.promises.unlink(path.join(testFolder, f))
2649
- }
2650
- await fs.promises.rmdir(testFolder)
2651
- } catch (e) {}
2652
- }
2653
- })()`
2654
- })
2655
-
2656
- return await r.text()
2657
- },
2658
- 'filename conflict detected'
2659
- )
2660
-
2661
- runTest(
2662
- "test wal-intent recovery after simulated crash during append",
2663
- async () => {
2664
- var r = await braid_fetch(`/eval`, {
2665
- method: 'PUT',
2666
- body: `void (async () => {
2667
- var fs = require('fs')
2668
- var test_db = __dirname + '/test_wal_recovery_' + Math.random().toString(36).slice(2)
2669
- try {
2670
- // Create a fresh braid_text instance with its own db_folder
2671
- var bt = braid_text.create_braid_text()
2672
- bt.db_folder = test_db
2673
-
2674
- // Do initial PUT (5 chars = 'hello', version ends at 4)
2675
- await bt.put('/test', {
2676
- version: ['a-4'],
2677
- parents: [],
2678
- body: 'hello'
2679
- })
2680
-
2681
- // Do second PUT to trigger an append (6 more chars = ' world', version ends at 10)
2682
- await bt.put('/test', {
2683
- version: ['a-10'],
2684
- parents: ['a-4'],
2685
- body: 'hello world'
2686
- })
2687
-
2688
- var encoded = bt.encode_filename('/test')
2689
- var db_file = test_db + '/' + encoded + '.1'
2690
- var intent_file = test_db + '/.wal-intent/' + encoded + '.1'
2691
-
2692
- // Read the db file
2693
- var data = await fs.promises.readFile(db_file)
2694
-
2695
- // Parse chunks to find the last one
2696
- var cursor = 0
2697
- var chunks = []
2698
- while (cursor < data.length) {
2699
- var chunk_start = cursor
2700
- var chunk_size = data.readUInt32LE(cursor)
2701
- cursor += 4 + chunk_size
2702
- chunks.push({ start: chunk_start, size: chunk_size, end: cursor })
2703
- }
2704
-
2705
- if (chunks.length < 2) {
2706
- res.end('expected at least 2 chunks, got ' + chunks.length)
2707
- return
2708
- }
2709
-
2710
- var last_chunk = chunks[chunks.length - 1]
2711
- var prev_end = chunks[chunks.length - 2].end
2712
-
2713
- // Create the wal-intent file: 8-byte size + the last chunk data
2714
- var size_buf = Buffer.allocUnsafe(8)
2715
- size_buf.writeBigUInt64LE(BigInt(prev_end), 0)
2716
- var last_chunk_data = data.subarray(last_chunk.start, last_chunk.end)
2717
- var intent_data = Buffer.concat([size_buf, last_chunk_data])
2718
- await fs.promises.writeFile(intent_file, intent_data)
2719
-
2720
- // Truncate the db file partway through the last chunk (keep ~half)
2721
- var truncate_point = last_chunk.start + Math.floor((last_chunk.end - last_chunk.start) / 2)
2722
- await fs.promises.truncate(db_file, truncate_point)
2723
-
2724
- // Create a new braid_text instance to simulate restart
2725
- var bt2 = braid_text.create_braid_text()
2726
- bt2.db_folder = test_db
2727
-
2728
- // Get the resource - this should trigger wal-intent replay
2729
- var resource = await bt2.get_resource('/test')
2730
- var text = resource.doc.get()
2731
-
2732
- // Verify intent file was cleaned up
2733
- var intent_exists = true
2734
- try {
2735
- await fs.promises.access(intent_file)
2736
- } catch (e) {
2737
- intent_exists = false
2738
- }
2739
-
2740
- await fs.promises.rm(test_db, { recursive: true, force: true })
2741
-
2742
- if (intent_exists) {
2743
- res.end('intent file still exists')
2744
- return
2745
- }
2746
-
2747
- res.end(text)
2748
- } catch (e) {
2749
- await fs.promises.rm(test_db, { recursive: true, force: true }).catch(() => {})
2750
- res.end('error: ' + e.message + ' ' + e.stack)
2751
- }
2752
- })()`
2753
- })
2754
-
2755
- return await r.text()
2756
- },
2757
- 'hello world'
2758
- )
2759
-
2760
- runTest(
2761
- "test wal-intent throws error when db file is too large",
2762
- async () => {
2763
- var r = await braid_fetch(`/eval`, {
2764
- method: 'PUT',
2765
- body: `void (async () => {
2766
- var fs = require('fs')
2767
- var test_db = __dirname + '/test_wal_error_' + Math.random().toString(36).slice(2)
2768
- try {
2769
- // Create a fresh braid_text instance with its own db_folder
2770
- var bt = braid_text.create_braid_text()
2771
- bt.db_folder = test_db
2772
-
2773
- // Do initial PUT (5 chars = 'hello', version ends at 4)
2774
- await bt.put('/test', {
2775
- version: ['a-4'],
2776
- parents: [],
2777
- body: 'hello'
2778
- })
2779
-
2780
- // Do second PUT to trigger an append (6 more chars = ' world', version ends at 10)
2781
- await bt.put('/test', {
2782
- version: ['a-10'],
2783
- parents: ['a-4'],
2784
- body: 'hello world'
2785
- })
2786
-
2787
- var encoded = bt.encode_filename('/test')
2788
- var db_file = test_db + '/' + encoded + '.1'
2789
- var intent_file = test_db + '/.wal-intent/' + encoded + '.1'
2790
-
2791
- // Read the db file
2792
- var data = await fs.promises.readFile(db_file)
2793
-
2794
- // Parse chunks to find the last one
2795
- var cursor = 0
2796
- var chunks = []
2797
- while (cursor < data.length) {
2798
- var chunk_start = cursor
2799
- var chunk_size = data.readUInt32LE(cursor)
2800
- cursor += 4 + chunk_size
2801
- chunks.push({ start: chunk_start, size: chunk_size, end: cursor })
2802
- }
2803
-
2804
- if (chunks.length < 2) {
2805
- res.end('expected at least 2 chunks, got ' + chunks.length)
2806
- return
2807
- }
2808
-
2809
- var last_chunk = chunks[chunks.length - 1]
2810
- var prev_end = chunks[chunks.length - 2].end
2811
-
2812
- // Create the wal-intent file: 8-byte size + the last chunk data
2813
- var size_buf = Buffer.allocUnsafe(8)
2814
- size_buf.writeBigUInt64LE(BigInt(prev_end), 0)
2815
- var last_chunk_data = data.subarray(last_chunk.start, last_chunk.end)
2816
- var intent_data = Buffer.concat([size_buf, last_chunk_data])
2817
- await fs.promises.writeFile(intent_file, intent_data)
2818
-
2819
- // Append extra garbage to the db file (making it too large)
2820
- await fs.promises.appendFile(db_file, Buffer.from('extra garbage data'))
2821
-
2822
- // Create a new braid_text instance to simulate restart
2823
- var bt2 = braid_text.create_braid_text()
2824
- bt2.db_folder = test_db
2825
-
2826
- // Now try to init - this should throw an error
2827
- var result
2828
- try {
2829
- await bt2.db_folder_init()
2830
- result = 'should have thrown an error'
2831
- } catch (e) {
2832
- if (e.message.includes('wal-intent replay failed')) {
2833
- result = 'correctly threw error'
2834
- } else {
2835
- result = 'wrong error: ' + e.message
2836
- }
2837
- }
2838
- await fs.promises.rm(test_db, { recursive: true, force: true })
2839
- res.end(result)
2840
- } catch (e) {
2841
- await fs.promises.rm(test_db, { recursive: true, force: true }).catch(() => {})
2842
- res.end('error: ' + e.message)
2843
- }
2844
- })()`
2845
- })
2846
-
2847
- return await r.text()
2848
- },
2849
- 'correctly threw error'
2850
- )
2851
-
2852
- // Tests for reconnector/sync edge cases
2853
-
2854
- runTest(
2855
- "test braid_text.sync remote null triggers local_first_put_promise path",
2856
- async () => {
2857
- var local_key = 'test-local-' + Math.random().toString(36).slice(2)
2858
-
2859
- // Use the /404 endpoint which always returns 404 (null from braid_text.get)
2860
- var r = await braid_fetch(`/eval`, {
2861
- method: 'PUT',
2862
- body: `void (async () => {
2863
- var ac = new AbortController()
2864
- var reconnect_count = 0
2865
-
2866
- braid_text.sync('/${local_key}', new URL('http://localhost:8889/404'), {
2867
- signal: ac.signal,
2868
- on_pre_connect: () => {
2869
- reconnect_count++
2870
- if (reconnect_count >= 2) {
2871
- ac.abort()
2872
- res.end('reconnected after local put')
2873
- }
2874
- }
2875
- })
2876
-
2877
- // Wait a bit then put something locally - this should trigger
2878
- // the local_first_put_promise to resolve and cause reconnect
2879
- await new Promise(done => setTimeout(done, 100))
2880
- await braid_text.put('/${local_key}', { body: 'local data' })
2881
-
2882
- // Wait for reconnect
2883
- await new Promise(done => setTimeout(done, 2000))
2884
- res.end('did not reconnect')
2885
- })()`
2886
- })
2887
- if (!r.ok) return 'eval failed: ' + r.status
2888
-
2889
- return await r.text()
2890
- },
2891
- 'reconnected after local put'
2892
- )
2893
-
2894
- }
2895
-
2896
- // Export for both Node.js and browser environments
2897
- if (typeof module !== 'undefined' && module.exports) {
2898
- module.exports = defineTests
2899
- }