braid-text 0.2.44 → 0.2.46

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.
Files changed (3) hide show
  1. package/index.js +55 -27
  2. package/package.json +1 -1
  3. package/test/test.html +151 -0
package/index.js CHANGED
@@ -291,11 +291,6 @@ braid_text.get = async (key, options) => {
291
291
  options.my_last_sent_version = x.version
292
292
  resource.simpleton_clients.add(options)
293
293
  } else {
294
- if (resource.need_defrag) {
295
- if (braid_text.verbose) console.log(`doing defrag..`)
296
- resource.need_defrag = false
297
- resource.doc = defrag_dt(resource.doc)
298
- }
299
294
 
300
295
  if (options.accept_encoding?.match(/updates\s*\((.*)\)/)?.[1].split(',').map(x=>x.trim()).includes('dt')) {
301
296
  var bytes = resource.doc.toBytes()
@@ -431,7 +426,7 @@ braid_text.put = async (key, options) => {
431
426
  // validate version: make sure we haven't seen it already
432
427
  if (resource.actor_seqs[v[0]]?.has(v[1])) {
433
428
 
434
- if (!options.validate_already_seen_versions) return
429
+ if (!options.validate_already_seen_versions) return { change_count }
435
430
 
436
431
  // if we have seen it already, make sure it's the same as before
437
432
  let updates = dt_get_patches(resource.doc, og_parents)
@@ -488,7 +483,7 @@ braid_text.put = async (key, options) => {
488
483
  }
489
484
 
490
485
  // we already have this version, so nothing left to do
491
- return
486
+ return { change_count: change_count }
492
487
  }
493
488
  if (!resource.actor_seqs[v[0]]) resource.actor_seqs[v[0]] = new RangeSet()
494
489
  resource.actor_seqs[v[0]].add_range(v[1] + 1 - change_count, v[1])
@@ -526,8 +521,6 @@ braid_text.put = async (key, options) => {
526
521
  for (let b of bytes) resource.doc.mergeBytes(b)
527
522
  resource.val = resource.doc.get()
528
523
 
529
- resource.need_defrag = true
530
-
531
524
  var post_commit_updates = []
532
525
 
533
526
  if (options.merge_type != "dt") {
@@ -674,17 +667,12 @@ async function get_resource(key) {
674
667
 
675
668
  resource.db_delta = change
676
669
 
677
- resource.doc = defrag_dt(resource.doc)
678
- resource.need_defrag = false
679
-
680
670
  resource.actor_seqs = {}
681
671
 
682
- let max_version = resource.doc.getLocalVersion().reduce((a, b) => Math.max(a, b), -1)
683
- for (let i = 0; i <= max_version; i++) {
684
- let v = resource.doc.localToRemoteVersion([i])[0]
685
- if (!resource.actor_seqs[v[0]]) resource.actor_seqs[v[0]] = new RangeSet()
686
- resource.actor_seqs[v[0]].add_range(v[1], v[1])
687
- }
672
+ dt_get_actor_seq_runs([...resource.doc.toBytes()], (actor, base, len) => {
673
+ if (!resource.actor_seqs[actor]) resource.actor_seqs[actor] = new RangeSet()
674
+ resource.actor_seqs[actor].add_range(base, base + len - 1)
675
+ })
688
676
 
689
677
  resource.val = resource.doc.get()
690
678
 
@@ -1069,16 +1057,19 @@ function dt_get(doc, version, agent = null, anti_version = null) {
1069
1057
  }
1070
1058
 
1071
1059
  function dt_get_patches(doc, version = null) {
1060
+ if (version && v_eq(version,
1061
+ doc.getRemoteVersion().map((x) => x.join("-")).sort())) {
1062
+ // they want everything past the end, which is nothing
1063
+ return []
1064
+ }
1065
+
1072
1066
  let bytes = doc.toBytes()
1073
1067
  doc = Doc.fromBytes(bytes)
1074
1068
 
1075
1069
  let [_agents, versions, parentss] = dt_parse([...bytes])
1076
1070
 
1077
1071
  let op_runs = []
1078
- if (version && v_eq(version,
1079
- doc.getRemoteVersion().map((x) => x.join("-")).sort())) {
1080
- // they want everything past the end, which is nothing
1081
- } else if (version?.length) {
1072
+ if (version?.length) {
1082
1073
  let frontier = {}
1083
1074
  version.forEach((x) => frontier[x] = true)
1084
1075
  let local_version = []
@@ -1223,6 +1214,48 @@ function dt_parse(byte_array) {
1223
1214
  return [agents, versions, parentss]
1224
1215
  }
1225
1216
 
1217
+ function dt_get_actor_seq_runs(byte_array, cb) {
1218
+ if (new TextDecoder().decode(new Uint8Array(byte_array.splice(0, 8))) !== "DMNDTYPS") throw new Error("dt parse error, expected DMNDTYPS")
1219
+
1220
+ if (byte_array.shift() != 0) throw new Error("dt parse error, expected version 0")
1221
+
1222
+ let agents = []
1223
+
1224
+ while (byte_array.length) {
1225
+ let id = byte_array.shift()
1226
+ let len = dt_read_varint(byte_array)
1227
+ if (id == 1) {
1228
+ } else if (id == 3) {
1229
+ let goal = byte_array.length - len
1230
+ while (byte_array.length > goal) {
1231
+ agents.push(dt_read_string(byte_array))
1232
+ }
1233
+ } else if (id == 20) {
1234
+ } else if (id == 21) {
1235
+ let seqs = {}
1236
+ let goal = byte_array.length - len
1237
+ while (byte_array.length > goal) {
1238
+ let part0 = dt_read_varint(byte_array)
1239
+ let has_jump = part0 & 1
1240
+ let agent_i = (part0 >> 1) - 1
1241
+ let run_length = dt_read_varint(byte_array)
1242
+ let jump = 0
1243
+ if (has_jump) {
1244
+ let part2 = dt_read_varint(byte_array)
1245
+ jump = part2 >> 1
1246
+ if (part2 & 1) jump *= -1
1247
+ }
1248
+ let base = (seqs[agent_i] || 0) + jump
1249
+
1250
+ cb(agents[agent_i], base, run_length)
1251
+ seqs[agent_i] = base + run_length
1252
+ }
1253
+ } else {
1254
+ byte_array.splice(0, len)
1255
+ }
1256
+ }
1257
+ }
1258
+
1226
1259
  function dt_get_local_version(bytes, version) {
1227
1260
  var looking_for = new Map()
1228
1261
  for (var event of version) {
@@ -1492,11 +1525,6 @@ function dt_create_bytes(version, parents, pos, del, ins) {
1492
1525
  return bytes
1493
1526
  }
1494
1527
 
1495
- function defrag_dt(doc) {
1496
- let bytes = doc.toBytes()
1497
- doc.free()
1498
- return Doc.fromBytes(bytes, 'server')
1499
- }
1500
1528
 
1501
1529
  function OpLog_remote_to_local(doc, frontier) {
1502
1530
  let map = Object.fromEntries(frontier.map((x) => [x, true]))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.44",
3
+ "version": "0.2.46",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",
package/test/test.html CHANGED
@@ -96,6 +96,157 @@ async function runTest(testName, testFunction, expectedResult) {
96
96
  }
97
97
  }
98
98
 
99
+ runTest(
100
+ "test PUTing a version that the server already has",
101
+ async () => {
102
+ var key = 'test-' + Math.random().toString(36).slice(2)
103
+
104
+ var r1 = await braid_fetch(`/${key}`, {
105
+ method: 'PUT',
106
+ version: ['hi-0'],
107
+ parents: [],
108
+ body: 'x'
109
+ })
110
+
111
+ var r2 = await braid_fetch(`/${key}`, {
112
+ method: 'PUT',
113
+ version: ['hi-0'],
114
+ parents: [],
115
+ body: 'x'
116
+ })
117
+
118
+ return r1.status + " " + r2.status
119
+ },
120
+ '200 200'
121
+ )
122
+
123
+ runTest(
124
+ "test validate_already_seen_versions with same version",
125
+ async () => {
126
+ var key = 'test-' + Math.random().toString(36).slice(2)
127
+
128
+ var r1 = await braid_fetch(`/eval`, {
129
+ method: 'PUT',
130
+ body: `void (async () => {
131
+ var resource = await braid_text.get_resource('/${key}')
132
+
133
+ 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" })
134
+
135
+ res.end('' + change_count)
136
+ })()`
137
+ })
138
+
139
+ var r2 = await braid_fetch(`/eval`, {
140
+ method: 'PUT',
141
+ body: `void (async () => {
142
+ var resource = await braid_text.get_resource('/${key}')
143
+
144
+ 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 })
145
+
146
+ res.end('' + change_count)
147
+ })()`
148
+ })
149
+
150
+ return (await r1.text()) + " " + (await r2.text())
151
+ },
152
+ '3 3'
153
+ )
154
+
155
+ runTest(
156
+ "test validate_already_seen_versions with modified version",
157
+ async () => {
158
+ var key = 'test-' + Math.random().toString(36).slice(2)
159
+
160
+ var r1 = await braid_fetch(`/eval`, {
161
+ method: 'PUT',
162
+ body: `void (async () => {
163
+ var resource = await braid_text.get_resource('/${key}')
164
+
165
+ 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" })
166
+
167
+ res.end('' + change_count)
168
+ })()`
169
+ })
170
+
171
+ var r2 = await braid_fetch(`/eval`, {
172
+ method: 'PUT',
173
+ body: `void (async () => {
174
+ var resource = await braid_text.get_resource('/${key}')
175
+
176
+ try {
177
+ 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 })
178
+
179
+ res.end('' + change_count)
180
+ } catch (e) {
181
+ res.end(e.message)
182
+ }
183
+ })()`
184
+ })
185
+
186
+ return await r2.text()
187
+ },
188
+ 'invalid update: different from previous update with same version'
189
+ )
190
+
191
+ runTest(
192
+ "test loading a previously saved resource",
193
+ async () => {
194
+ var key = 'test-' + Math.random().toString(36).slice(2)
195
+
196
+ var f1 = await braid_fetch(`/${key}`, {
197
+ method: 'PUT',
198
+ version: ['hi-2'],
199
+ parents: [],
200
+ body: 'abc'
201
+ })
202
+
203
+ var f1 = await braid_fetch(`/eval`, {
204
+ method: 'PUT',
205
+ body: `
206
+ delete braid_text.cache['/${key}']
207
+ res.end()
208
+ `
209
+ })
210
+
211
+ var r = await braid_fetch(`/${key}`)
212
+ return await r.text()
213
+ },
214
+ 'abc'
215
+ )
216
+
217
+ runTest(
218
+ "test non-contigous ids",
219
+ async () => {
220
+ var key = 'test-' + Math.random().toString(36).slice(2)
221
+
222
+ var r = await braid_fetch(`/${key}`, {
223
+ method: 'PUT',
224
+ version: ['hi-10'],
225
+ parents: [],
226
+ body: 'abc'
227
+ })
228
+
229
+ var r = await braid_fetch(`/${key}`, {
230
+ method: 'PUT',
231
+ version: ['hi-20'],
232
+ parents: ['hi-10'],
233
+ body: 'ABC'
234
+ })
235
+
236
+ var f1 = await braid_fetch(`/eval`, {
237
+ method: 'PUT',
238
+ body: `
239
+ delete braid_text.cache['/${key}']
240
+ res.end()
241
+ `
242
+ })
243
+
244
+ var r = await braid_fetch(`/${key}`)
245
+ return await r.text()
246
+ },
247
+ 'ABC'
248
+ )
249
+
99
250
  runTest(
100
251
  "test when PUT cache/buffer size fails",
101
252
  async () => {