braid-text 0.2.62 → 0.2.64

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 CHANGED
@@ -40,6 +40,52 @@ braid_text.serve = async (req, res, options = {}) => {
40
40
 
41
41
  let peer = req.headers["peer"]
42
42
 
43
+ // selection sharing prototype
44
+ if (req.headers['selection-sharing-prototype']) {
45
+ res.setHeader('Content-Type', 'application/json')
46
+
47
+ if (!resource.selections) resource.selections = {}
48
+ if (!resource.selection_clients) resource.selection_clients = new Set()
49
+
50
+ if (req.method === "GET" || req.method === "HEAD") {
51
+ if (!req.subscribe) {
52
+ return my_end(200, JSON.stringify(resource.selections))
53
+ } else {
54
+ var client = {peer, res}
55
+ resource.selection_clients.add(client)
56
+ res.startSubscription({
57
+ onClose: () => resource.selection_clients.delete(client)
58
+ })
59
+ res.sendUpdate({ body: JSON.stringify(resource.selections) })
60
+ return
61
+ }
62
+ } else if (req.method == "PUT" || req.method == "POST" || req.method == "PATCH") {
63
+ var body = (await req.patches())[0].content_text
64
+ var json = JSON.parse(body)
65
+
66
+ // only keep new selections if they are newer
67
+ for (var [user, selection] of Object.entries(json)) {
68
+ if (resource.selections[user] && resource.selections[user].time > selection.time) delete json[user]
69
+ else resource.selections[user] = selection
70
+ }
71
+
72
+ // remove old selections that are too old
73
+ var long_ago = Date.now() - 1000 * 60 * 5
74
+ for (var [user, selection] of Object.entries(resource.selections))
75
+ if (selection.time < long_ago) {
76
+ delete resource.selections[user]
77
+ delete json[user]
78
+ }
79
+
80
+ body = JSON.stringify(json)
81
+ if (body.length > 2)
82
+ for (let client of resource.selection_clients)
83
+ if (client.peer !== peer) client.res.sendUpdate({ body })
84
+
85
+ return my_end(200)
86
+ }
87
+ }
88
+
43
89
  let merge_type = req.headers["merge-type"]
44
90
  if (!merge_type) merge_type = 'simpleton'
45
91
  if (merge_type !== 'simpleton' && merge_type !== 'dt') return my_end(400, `Unknown merge type: ${merge_type}`)
@@ -387,6 +433,26 @@ braid_text.put = async (key, options) => {
387
433
  })
388
434
  }
389
435
 
436
+ let resource = (typeof key == 'string') ? await get_resource(key) : key
437
+
438
+ if (options.transfer_encoding === 'dt') {
439
+ var start_i = 1 + resource.doc.getLocalVersion().reduce((a, b) => Math.max(a, b), -1)
440
+
441
+ resource.doc.mergeBytes(body)
442
+
443
+ var end_i = resource.doc.getLocalVersion().reduce((a, b) => Math.max(a, b), -1)
444
+ for (var i = start_i; i <= end_i; i++) {
445
+ let v = resource.doc.localToRemoteVersion([i])[0]
446
+ if (!resource.actor_seqs[v[0]]) resource.actor_seqs[v[0]] = new braid_text.RangeSet()
447
+ resource.actor_seqs[v[0]].add_range(v[1], v[1])
448
+ }
449
+ resource.val = resource.doc.get()
450
+ resource.version = resource.doc.getRemoteVersion().map(x => x.join("-")).sort()
451
+
452
+ await resource.db_delta(body)
453
+ return { change_count: end_i - start_i + 1 }
454
+ }
455
+
390
456
  if (version) validate_version_array(version)
391
457
 
392
458
  // translate a single parent of "root" to the empty array (same meaning)
@@ -399,8 +465,6 @@ braid_text.put = async (key, options) => {
399
465
  if (body != null && (typeof body !== 'string')) throw new Error(`body must be a string`)
400
466
  if (patches) validate_patches(patches)
401
467
 
402
- let resource = (typeof key == 'string') ? await get_resource(key) : key
403
-
404
468
  if (options_parents) {
405
469
  // make sure we have all these parents
406
470
  for (let p of options_parents) {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.62",
3
+ "version": "0.2.64",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",
7
7
  "homepage": "https://braid.org",
8
8
  "dependencies": {
9
9
  "@braid.org/diamond-types-node": "^2.0.0",
10
- "braid-http": "~1.3.79"
10
+ "braid-http": "~1.3.80"
11
11
  }
12
12
  }
package/server-demo.js CHANGED
@@ -1,6 +1,4 @@
1
-
2
1
  var port = process.argv[2] || 8888
3
-
4
2
  var braid_text = require("./index.js")
5
3
 
6
4
  // TODO: set a custom database folder
package/test/test.html CHANGED
@@ -96,6 +96,174 @@ async function runTest(testName, testFunction, expectedResult) {
96
96
  }
97
97
  }
98
98
 
99
+ runTest(
100
+ "test selection-sharing-prototype PUT and GET",
101
+ async () => {
102
+ let key = 'test-' + Math.random().toString(36).slice(2)
103
+
104
+ let time = Date.now()
105
+
106
+ let r = await braid_fetch(`/${key}`, {
107
+ method: 'PUT',
108
+ body: JSON.stringify({
109
+ hello: {
110
+ yo: 'hi',
111
+ time
112
+ }
113
+ }),
114
+ headers: {
115
+ 'selection-sharing-prototype': 'true'
116
+ }
117
+ })
118
+ if (!r.ok) return 'got: ' + r.status
119
+
120
+ let r2 = await braid_fetch(`/${key}`, {
121
+ method: 'GET',
122
+ headers: {
123
+ 'selection-sharing-prototype': 'true'
124
+ }
125
+ })
126
+ if (!r2.ok) return 'got: ' + r2.status
127
+
128
+ let o = await r2.json()
129
+ return o.hello.time === time ? 'times match' : 'bad'
130
+ },
131
+ 'times match'
132
+ )
133
+
134
+ runTest(
135
+ "test selection-sharing-prototype GET/subscribe",
136
+ async () => {
137
+ let key = 'test-' + Math.random().toString(36).slice(2)
138
+
139
+ var a = new AbortController()
140
+ let r = await braid_fetch(`/${key}`, {
141
+ method: 'GET',
142
+ signal: a.signal,
143
+ subscribe: true,
144
+ peer: 'abc',
145
+ headers: {
146
+ 'selection-sharing-prototype': 'true'
147
+ }
148
+ })
149
+ if (!r.ok) return 'got: ' + r.status
150
+ var p = new Promise(done => {
151
+ r.subscribe(update => {
152
+ var body = update.body_text
153
+ if (body.length > 2) done(body)
154
+ })
155
+ })
156
+
157
+ var time = Date.now()
158
+
159
+ let r2 = await braid_fetch(`/${key}`, {
160
+ method: 'PUT',
161
+ peer: 'xyz',
162
+ body: JSON.stringify({
163
+ hello: {
164
+ yo: 'hi',
165
+ time
166
+ }
167
+ }),
168
+ headers: {
169
+ 'selection-sharing-prototype': 'true'
170
+ }
171
+ })
172
+ if (!r2.ok) return 'got: ' + r2.status
173
+
174
+ var ret_val = JSON.parse(await p).hello.time === time ? 'times match' : 'bad'
175
+
176
+ a.abort()
177
+
178
+ return ret_val
179
+ },
180
+ 'times match'
181
+ )
182
+
183
+ runTest(
184
+ "test selection-sharing-prototype PUT old cursor",
185
+ async () => {
186
+ let key = 'test-' + Math.random().toString(36).slice(2)
187
+
188
+ let time = Date.now()
189
+
190
+ let r = await braid_fetch(`/${key}`, {
191
+ method: 'PUT',
192
+ body: JSON.stringify({
193
+ hello: {
194
+ yo: 'hi',
195
+ time
196
+ }
197
+ }),
198
+ headers: {
199
+ 'selection-sharing-prototype': 'true'
200
+ }
201
+ })
202
+ if (!r.ok) return 'got: ' + r.status
203
+
204
+ let r3 = await braid_fetch(`/${key}`, {
205
+ method: 'PUT',
206
+ body: JSON.stringify({
207
+ hello: {
208
+ yo: 'hoop',
209
+ time: time - 5
210
+ }
211
+ }),
212
+ headers: {
213
+ 'selection-sharing-prototype': 'true'
214
+ }
215
+ })
216
+ if (!r3.ok) return 'got: ' + r3.status
217
+
218
+ let r2 = await braid_fetch(`/${key}`, {
219
+ method: 'GET',
220
+ headers: {
221
+ 'selection-sharing-prototype': 'true'
222
+ }
223
+ })
224
+ if (!r2.ok) return 'got: ' + r2.status
225
+
226
+ let o = await r2.json()
227
+ return o.hello.yo
228
+ },
229
+ 'hi'
230
+ )
231
+
232
+ runTest(
233
+ "test selection-sharing-prototype PUT really old cursor",
234
+ async () => {
235
+ let key = 'test-' + Math.random().toString(36).slice(2)
236
+
237
+ let time = Date.now() - 1000 * 60 * 60 * 24
238
+
239
+ let r = await braid_fetch(`/${key}`, {
240
+ method: 'PUT',
241
+ body: JSON.stringify({
242
+ hello: {
243
+ yo: 'hi',
244
+ time
245
+ }
246
+ }),
247
+ headers: {
248
+ 'selection-sharing-prototype': 'true'
249
+ }
250
+ })
251
+ if (!r.ok) return 'got: ' + r.status
252
+
253
+ let r2 = await braid_fetch(`/${key}`, {
254
+ method: 'GET',
255
+ headers: {
256
+ 'selection-sharing-prototype': 'true'
257
+ }
258
+ })
259
+ if (!r2.ok) return 'got: ' + r2.status
260
+
261
+ let o = await r2.json()
262
+ return JSON.stringify(o)
263
+ },
264
+ '{}'
265
+ )
266
+
99
267
  runTest(
100
268
  "test PUT digest (good)",
101
269
  async () => {
@@ -564,6 +732,35 @@ runTest(
564
732
  'xy'
565
733
  )
566
734
 
735
+ runTest(
736
+ "test put with transfer-encoding: dt",
737
+ async () => {
738
+ var key = 'test-' + Math.random().toString(36).slice(2)
739
+ var doc = new Doc('hi')
740
+ doc.ins(0, 'xy')
741
+
742
+ var bytes = doc.toBytes()
743
+
744
+ var r1 = await braid_fetch(`/eval`, {
745
+ method: 'PUT',
746
+ body: `void (async () => {
747
+ var key = '/${key}'
748
+
749
+ var {change_count} = await braid_text.put(key, {
750
+ body: new Uint8Array([${'' + bytes}]),
751
+ transfer_encoding: "dt"
752
+ })
753
+ var {body, version} = await braid_text.get(key, {})
754
+
755
+ res.end('' + change_count + " " + body + " " + version)
756
+ })()`
757
+ })
758
+
759
+ return await r1.text()
760
+ },
761
+ '2 xy hi-1'
762
+ )
763
+
567
764
  runTest(
568
765
  "test transfer-encoding dt (with parents)",
569
766
  async () => {