braid-text 0.1.1 → 0.1.3

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 +128 -68
  2. package/package.json +2 -2
  3. package/test.js +88 -122
package/index.js CHANGED
@@ -3,6 +3,8 @@ let { Doc } = require("diamond-types-node")
3
3
  let braidify = require("braid-http").http_server
4
4
  let fs = require("fs")
5
5
 
6
+ let MISSING_PARENT_VERSION = 'missing parent version'
7
+
6
8
  let braid_text = {
7
9
  verbose: false,
8
10
  db_folder: './braid-text-db',
@@ -164,32 +166,36 @@ braid_text.serve = async (req, res, options = {}) => {
164
166
 
165
167
  options.put_cb(options.key, resource.val)
166
168
  } catch (e) {
167
- console.log(`EEE= ${e}:${e.stack}`)
168
- // we couldn't apply the version, possibly because we're missing its parents,
169
- // we want to send a 4XX error, so the client will resend this request later,
170
- // hopefully after we've received the necessary parents.
171
-
172
- // here are some 4XX error code options..
173
- //
174
- // - 425 Too Early
175
- // - pros: our message is too early
176
- // - cons: associated with some "Early-Data" http thing, which we're not using
177
- // - 400 Bad Request
178
- // - pros: pretty generic
179
- // - cons: implies client shouldn't resend as-is
180
- // - 409 Conflict
181
- // - pros: doesn't imply modifications needed
182
- // - cons: the message is not conflicting with anything
183
- // - 412 Precondition Failed
184
- // - pros: kindof true.. the precondition of having another version has failed..
185
- // - cons: not strictly true, as this code is associated with http's If-Unmodified-Since stuff
186
- // - 422 Unprocessable Content
187
- // - pros: it's true
188
- // - cons: implies client shouldn't resend as-is (at least, it says that here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422)
189
- // - 428 Precondition Required
190
- // - pros: the name sounds right
191
- // - cons: typically implies that the request was missing an http conditional field like If-Match. that is to say, it implies that the request is missing a precondition, not that the server is missing a precondition
192
- return done_my_turn(425, "The server failed to apply this version. The error generated was: " + e)
169
+ console.log(`${req.method} ERROR: ${e.stack}`)
170
+ if (e.message?.startsWith(MISSING_PARENT_VERSION)) {
171
+ // we couldn't apply the version, because we're missing its parents,
172
+ // we want to send a 4XX error, so the client will resend this request later,
173
+ // hopefully after we've received the necessary parents.
174
+
175
+ // here are some 4XX error code options..
176
+ //
177
+ // - 425 Too Early
178
+ // - pros: our message is too early
179
+ // - cons: associated with some "Early-Data" http thing, which we're not using
180
+ // - 400 Bad Request
181
+ // - pros: pretty generic
182
+ // - cons: implies client shouldn't resend as-is
183
+ // - 409 Conflict
184
+ // - pros: doesn't imply modifications needed
185
+ // - cons: the message is not conflicting with anything
186
+ // - 412 Precondition Failed
187
+ // - pros: kindof true.. the precondition of having another version has failed..
188
+ // - cons: not strictly true, as this code is associated with http's If-Unmodified-Since stuff
189
+ // - 422 Unprocessable Content
190
+ // - pros: it's true
191
+ // - cons: implies client shouldn't resend as-is (at least, it says that here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422)
192
+ // - 428 Precondition Required
193
+ // - pros: the name sounds right
194
+ // - cons: typically implies that the request was missing an http conditional field like If-Match. that is to say, it implies that the request is missing a precondition, not that the server is missing a precondition
195
+ return done_my_turn(425, e.message)
196
+ } else {
197
+ return done_my_turn(400, "The server failed to apply this version. The error generated was: " + e)
198
+ }
193
199
  }
194
200
 
195
201
  return done_my_turn(200)
@@ -214,10 +220,14 @@ braid_text.get = async (key, options) => {
214
220
  let doc = resource.doc
215
221
  if (options.version || options.parents) doc = dt_get(doc, options.version || options.parents)
216
222
 
217
- return {
223
+ let ret = {
218
224
  version: doc.getRemoteVersion().map((x) => x.join("-")).sort(),
219
225
  body: doc.get()
220
226
  }
227
+
228
+ if (options.version || options.parents) doc.free()
229
+
230
+ return ret
221
231
  } else {
222
232
  if (options.merge_type != "dt") {
223
233
  let version = resource.doc.getRemoteVersion().map((x) => x.join("-")).sort()
@@ -263,17 +273,12 @@ braid_text.get = async (key, options) => {
263
273
  updates = dt_get_patches(resource.doc, options.parents || options.version)
264
274
  }
265
275
 
266
- for (let u of updates) {
267
- u.version = decode_version(u.version)
268
- u.version[1] += u.end - u.start - 1
269
- u.version = u.version.join("-")
270
-
276
+ for (let u of updates)
271
277
  options.subscribe({
272
278
  version: [u.version],
273
279
  parents: u.parents,
274
280
  patches: [{ unit: u.unit, range: u.range, content: u.content }],
275
281
  })
276
- }
277
282
 
278
283
  // Output at least *some* data, or else chrome gets confused and
279
284
  // thinks the connection failed. This isn't strictly necessary,
@@ -286,6 +291,16 @@ braid_text.get = async (key, options) => {
286
291
  }
287
292
  }
288
293
 
294
+ braid_text.forget = async (key, options) => {
295
+ if (!options) throw new Error('options is required')
296
+
297
+ let resource = (typeof key == 'string') ? await get_resource(key) : key
298
+
299
+ if (options.merge_type != "dt")
300
+ resource.simpleton_clients.delete(options)
301
+ else resource.clients.delete(options)
302
+ }
303
+
289
304
  braid_text.put = async (key, options) => {
290
305
  let { version, patches, body, peer } = options
291
306
 
@@ -303,12 +318,26 @@ braid_text.put = async (key, options) => {
303
318
 
304
319
  let resource = (typeof key == 'string') ? await get_resource(key) : key
305
320
 
321
+ if (options_parents) {
322
+ // make sure we have all these parents
323
+ for (let p of options_parents) {
324
+ let P = decode_version(p)
325
+ if (P[1] > (resource.actor_seqs[P[0]] ?? -1)) throw new Error(`${MISSING_PARENT_VERSION}: ${p}`)
326
+ }
327
+ }
328
+
306
329
  let parents = resource.doc.getRemoteVersion().map((x) => x.join("-")).sort()
307
330
  let og_parents = options_parents || parents
308
331
 
332
+ function get_len() {
333
+ let d = dt_get(resource.doc, og_parents)
334
+ let len = d.len()
335
+ d.free()
336
+ return len
337
+ }
338
+
309
339
  let max_pos = resource.length_cache.get('' + og_parents) ??
310
- (v_eq(parents, og_parents) ? resource.doc.len() :
311
- dt_get(resource.doc, og_parents).len())
340
+ (v_eq(parents, og_parents) ? resource.doc.len() : get_len())
312
341
 
313
342
  if (body != null) {
314
343
  patches = [{
@@ -322,7 +351,7 @@ braid_text.put = async (key, options) => {
322
351
  patches = patches.map((p) => ({
323
352
  ...p,
324
353
  range: p.range.match(/\d+/g).map((x) => parseInt(x)),
325
- content: [...p.content],
354
+ content_codepoints: [...p.content],
326
355
  })).sort((a, b) => a.range[0] - b.range[0])
327
356
 
328
357
  // validate patch positions
@@ -333,7 +362,7 @@ braid_text.put = async (key, options) => {
333
362
  must_be_at_least = p.range[1]
334
363
  }
335
364
 
336
- let change_count = patches.reduce((a, b) => a + b.content.length + (b.range[1] - b.range[0]), 0)
365
+ let change_count = patches.reduce((a, b) => a + b.content_codepoints.length + (b.range[1] - b.range[0]), 0)
337
366
 
338
367
  let og_v = version?.[0] || `${(is_valid_actor(peer) && peer) || Math.random().toString(36).slice(2, 7)}-${change_count - 1}`
339
368
 
@@ -351,7 +380,6 @@ braid_text.put = async (key, options) => {
351
380
  let seen = {}
352
381
  for (let u of updates) {
353
382
  u.version = decode_version(u.version)
354
- u.version[1] += u.end - u.start - 1
355
383
 
356
384
  if (!u.content) {
357
385
  // delete
@@ -387,9 +415,9 @@ braid_text.put = async (key, options) => {
387
415
  v = `${v[0]}-${v[1] + 1}`
388
416
  }
389
417
  // insert
390
- for (let i = 0; i < p.content?.length ?? 0; i++) {
418
+ for (let i = 0; i < p.content_codepoints?.length ?? 0; i++) {
391
419
  let vv = decode_version(v)
392
- let c = p.content[i]
420
+ let c = p.content_codepoints[i]
393
421
 
394
422
  if (!seen[JSON.stringify([vv[0], vv[1], ps, p.range[1] + offset, c])]) throw new Error('invalid update: different from previous update with same version')
395
423
 
@@ -406,7 +434,7 @@ braid_text.put = async (key, options) => {
406
434
  resource.actor_seqs[v[0]] = v[1]
407
435
 
408
436
  resource.length_cache.put(`${v[0]}-${v[1]}`, patches.reduce((a, b) =>
409
- a + (b.content.length ? b.content.length : -(b.range[1] - b.range[0])),
437
+ a + (b.content_codepoints.length ? b.content_codepoints.length : -(b.range[1] - b.range[0])),
410
438
  max_pos))
411
439
 
412
440
  v = `${v[0]}-${v[1] + 1 - change_count}`
@@ -420,21 +448,21 @@ braid_text.put = async (key, options) => {
420
448
  let offset = 0
421
449
  for (let p of patches) {
422
450
  // delete
423
- for (let i = p.range[0]; i < p.range[1]; i++) {
424
- bytes.push(dt_create_bytes(v, ps, p.range[1] - 1 + offset, null))
425
- offset--
426
- ps = [v]
451
+ let del = p.range[1] - p.range[0]
452
+ if (del) {
453
+ bytes.push(dt_create_bytes(v, ps, p.range[0] + offset, del, null))
454
+ offset -= del
427
455
  v = decode_version(v)
428
- v = `${v[0]}-${v[1] + 1}`
456
+ ps = [`${v[0]}-${v[1] + (del - 1)}`]
457
+ v = `${v[0]}-${v[1] + del}`
429
458
  }
430
459
  // insert
431
- for (let i = 0; i < p.content?.length ?? 0; i++) {
432
- let c = p.content[i]
433
- bytes.push(dt_create_bytes(v, ps, p.range[1] + offset, c))
434
- offset++
435
- ps = [v]
460
+ if (p.content?.length) {
461
+ bytes.push(dt_create_bytes(v, ps, p.range[1] + offset, 0, p.content))
462
+ offset += p.content_codepoints.length
436
463
  v = decode_version(v)
437
- v = `${v[0]}-${v[1] + 1}`
464
+ ps = [`${v[0]}-${v[1] + (p.content_codepoints.length - 1)}`]
465
+ v = `${v[0]}-${v[1] + p.content_codepoints.length}`
438
466
  }
439
467
  }
440
468
 
@@ -577,7 +605,7 @@ async function get_resource(key) {
577
605
  resource.need_defrag = false
578
606
 
579
607
  resource.actor_seqs = {}
580
- let max_version = resource.doc.getLocalVersion()[0] ?? -1
608
+ let max_version = Math.max(...resource.doc.getLocalVersion()) ?? -1
581
609
  for (let i = 0; i <= max_version; i++) {
582
610
  let v = resource.doc.localToRemoteVersion([i])[0]
583
611
  resource.actor_seqs[v[0]] = Math.max(v[1], resource.actor_seqs[v[0]] ?? -1)
@@ -773,6 +801,8 @@ async function file_sync(key, process_delta, get_init) {
773
801
  //////////////////////////////////////////////////////////////////
774
802
 
775
803
  function dt_get(doc, version, agent = null) {
804
+ if (dt_get.last_doc) dt_get.last_doc.free()
805
+
776
806
  let bytes = doc.toBytes()
777
807
  dt_get.last_doc = doc = Doc.fromBytes(bytes, agent)
778
808
 
@@ -820,6 +850,7 @@ function dt_get(doc, version, agent = null) {
820
850
  op_run.start + (i - base_i) :
821
851
  op_run.start) :
822
852
  op_run.end - 1 - (i - base_i),
853
+ op_run.content?.[i - base_i] != null ? 0 : 1,
823
854
  op_run.content?.[i - base_i]
824
855
  )
825
856
  )
@@ -852,12 +883,16 @@ function dt_get_patches(doc, version = null) {
852
883
 
853
884
  before_doc.mergeBytes(after_bytes)
854
885
  op_runs = before_doc.getOpsSince(before_doc_frontier)
886
+
887
+ before_doc.free()
855
888
  } else op_runs = doc.getOpsSince([])
856
889
 
890
+ doc.free()
891
+
857
892
  let i = 0
858
893
  let patches = []
859
894
  op_runs.forEach((op_run) => {
860
- let version = versions[i].join("-")
895
+ let version = versions[i]
861
896
  let parents = parentss[i].map((x) => x.join("-")).sort()
862
897
  let start = op_run.start
863
898
  let end = start + 1
@@ -885,7 +920,7 @@ function dt_get_patches(doc, version = null) {
885
920
  op_run.start + (end - start)) :
886
921
  (op_run.end - (start - op_run.start))
887
922
  patches.push({
888
- version,
923
+ version: `${version[0]}-${version[1] + e - s - 1}`,
889
924
  parents,
890
925
  unit: "text",
891
926
  range: op_run.content ? `[${s}:${s}]` : `[${s}:${e}]`,
@@ -894,7 +929,7 @@ function dt_get_patches(doc, version = null) {
894
929
  end: e,
895
930
  })
896
931
  if (j == len) break
897
- version = versions[I].join("-")
932
+ version = versions[I]
898
933
  parents = parentss[I].map((x) => x.join("-")).sort()
899
934
  start = op_run.start + j
900
935
  }
@@ -1000,7 +1035,8 @@ function dt_parse(byte_array) {
1000
1035
  return [agents, versions, parentss]
1001
1036
  }
1002
1037
 
1003
- function dt_create_bytes(version, parents, pos, ins) {
1038
+ function dt_create_bytes(version, parents, pos, del, ins) {
1039
+ if (del) pos += del - 1
1004
1040
 
1005
1041
  function write_varint(bytes, value) {
1006
1042
  while (value >= 0x80) {
@@ -1071,6 +1107,8 @@ function dt_create_bytes(version, parents, pos, ins) {
1071
1107
 
1072
1108
  let patches = []
1073
1109
 
1110
+ let unicode_chars = ins ? [...ins] : []
1111
+
1074
1112
  if (ins) {
1075
1113
  let inserted_content_bytes = []
1076
1114
 
@@ -1081,18 +1119,21 @@ function dt_create_bytes(version, parents, pos, ins) {
1081
1119
  let encoder = new TextEncoder()
1082
1120
  let utf8Bytes = encoder.encode(ins)
1083
1121
 
1084
- inserted_content_bytes.push(1 + utf8Bytes.length) // length of content chunk
1122
+ write_varint(inserted_content_bytes, 1 + utf8Bytes.length)
1123
+ // inserted_content_bytes.push(1 + utf8Bytes.length) // length of content chunk
1085
1124
  inserted_content_bytes.push(4) // "plain text" enum
1086
1125
 
1087
1126
  for (let b of utf8Bytes) inserted_content_bytes.push(b) // actual text
1088
1127
 
1089
1128
  inserted_content_bytes.push(25) // "known" enum
1090
- inserted_content_bytes.push(1) // length of "known" chunk
1091
- inserted_content_bytes.push(3) // content of length 1, and we "know" it
1129
+ let known_chunk = []
1130
+ write_varint(known_chunk, unicode_chars.length * 2 + 1)
1131
+ write_varint(inserted_content_bytes, known_chunk.length)
1132
+ inserted_content_bytes.push(...known_chunk)
1092
1133
 
1093
1134
  patches.push(24)
1094
1135
  write_varint(patches, inserted_content_bytes.length)
1095
- patches.push(...inserted_content_bytes)
1136
+ for (let b of inserted_content_bytes) patches.push(b)
1096
1137
  }
1097
1138
 
1098
1139
  // write in the version
@@ -1103,26 +1144,43 @@ function dt_create_bytes(version, parents, pos, ins) {
1103
1144
  let jump = seq
1104
1145
 
1105
1146
  write_varint(version_bytes, ((agent_i + 1) << 1) | (jump != 0 ? 1 : 0))
1106
- write_varint(version_bytes, 1)
1147
+ write_varint(version_bytes, ins ? unicode_chars.length : del)
1107
1148
  if (jump) write_varint(version_bytes, jump << 1)
1108
1149
 
1109
1150
  patches.push(21)
1110
1151
  write_varint(patches, version_bytes.length)
1111
- patches.push(...version_bytes)
1152
+ for (let b of version_bytes) patches.push(b)
1112
1153
 
1113
1154
  // write in "op" bytes (some encoding of position)
1114
1155
  let op_bytes = []
1115
1156
 
1116
- write_varint(op_bytes, (pos << 4) | (pos ? 2 : 0) | (ins ? 0 : 4))
1157
+ if (del) {
1158
+ if (pos == 0) {
1159
+ write_varint(op_bytes, 4)
1160
+ } else if (del == 1) {
1161
+ write_varint(op_bytes, pos * 16 + 6)
1162
+ } else {
1163
+ write_varint(op_bytes, del * 16 + 7)
1164
+ write_varint(op_bytes, pos * 2 + 2)
1165
+ }
1166
+ } else if (unicode_chars.length == 1) {
1167
+ if (pos == 0) write_varint(op_bytes, 0)
1168
+ else write_varint(op_bytes, pos * 16 + 2)
1169
+ } else if (pos == 0) {
1170
+ write_varint(op_bytes, unicode_chars.length * 8 + 1)
1171
+ } else {
1172
+ write_varint(op_bytes, unicode_chars.length * 8 + 3)
1173
+ write_varint(op_bytes, pos * 2)
1174
+ }
1117
1175
 
1118
1176
  patches.push(22)
1119
1177
  write_varint(patches, op_bytes.length)
1120
- patches.push(...op_bytes)
1178
+ for (let b of op_bytes) patches.push(b)
1121
1179
 
1122
1180
  // write in parents
1123
1181
  let parents_bytes = []
1124
1182
 
1125
- write_varint(parents_bytes, 1)
1183
+ write_varint(parents_bytes, ins ? unicode_chars.length : del)
1126
1184
 
1127
1185
  if (parents.length) {
1128
1186
  for (let [i, [agent, seq]] of parents.entries()) {
@@ -1140,14 +1198,16 @@ function dt_create_bytes(version, parents, pos, ins) {
1140
1198
  // write in patches
1141
1199
  bytes.push(20)
1142
1200
  write_varint(bytes, patches.length)
1143
- bytes.push(...patches)
1201
+ for (let b of patches) bytes.push(b)
1144
1202
 
1145
1203
  // console.log(bytes);
1146
1204
  return bytes
1147
1205
  }
1148
1206
 
1149
1207
  function defrag_dt(doc) {
1150
- return Doc.fromBytes(doc.toBytes(), 'server')
1208
+ let bytes = doc.toBytes()
1209
+ doc.free()
1210
+ return Doc.fromBytes(bytes, 'server')
1151
1211
  }
1152
1212
 
1153
1213
  function OpLog_remote_to_local(doc, frontier) {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braidjs",
7
7
  "homepage": "https://braid.org",
8
8
  "dependencies": {
9
9
  "diamond-types-node": "^1.0.2",
10
- "braid-http": "^0.3.18"
10
+ "braid-http": "^1.0.7"
11
11
  }
12
12
  }
package/test.js CHANGED
@@ -1,9 +1,19 @@
1
1
 
2
2
  let { Doc } = require("diamond-types-node")
3
- let {dt_get, dt_get_patches, dt_parse, dt_create_bytes} = require('./index.js')
3
+ let braid_text = require('./index.js')
4
+ let {dt_get, dt_get_patches, dt_parse, dt_create_bytes} = braid_text
5
+
6
+ process.on("unhandledRejection", (x) =>
7
+ console.log(`unhandledRejection: ${x.stack}`)
8
+ )
9
+ process.on("uncaughtException", (x) =>
10
+ console.log(`uncaughtException: ${x.stack}`)
11
+ )
12
+
13
+ braid_text.db_folder = null
4
14
 
5
15
  async function main() {
6
- let best_seed = 0
16
+ let best_seed = NaN
7
17
  let best_n = Infinity
8
18
  let base = Math.floor(Math.random() * 10000000)
9
19
 
@@ -11,89 +21,89 @@ async function main() {
11
21
  console.log = () => {}
12
22
  for (let t = 0; t < 10000; t++) {
13
23
  let seed = base + t
14
- // for (let t = 0; t < 10; t++) {
15
- // let seed = 1188661 + t
24
+ // for (let t = 0; t < 1; t++) {
25
+ // let seed = 7572861
16
26
 
17
27
  og_log(`t = ${t}, seed = ${seed}, best_n = ${best_n} @ ${best_seed}`)
18
28
  Math.randomSeed(seed)
19
29
 
20
- let n = Math.floor(Math.random() * 15)
30
+ let n = Math.floor(Math.random() * 15) + 5
21
31
  console.log(`n = ${n}`)
22
32
 
23
33
  try {
24
- // 1. create a bunch of edits to a dt
34
+ // create a bunch of edits called doc,
35
+ // and remember a point along the way of adding all these edits,
36
+ // called middle_doc
25
37
  let doc = new Doc('server')
26
-
27
38
  let middle_doc = null
28
39
 
29
- if (!middle_doc && (Math.random() < 1/n || n == 0)) {
40
+ if (!middle_doc && (Math.random() < 1/n || n == 0))
30
41
  middle_doc = Doc.fromBytes(doc.toBytes())
31
- }
32
42
  for (let i = 0; i < n; i++) {
33
- make_random_edit(doc)
43
+ console.log(`edit ${i}`)
34
44
 
35
- if (!middle_doc && (Math.random() < 1/n || i == n - 1)) {
45
+ make_random_edit(doc)
46
+ if (!middle_doc && (Math.random() < 1/n || i == n - 1))
36
47
  middle_doc = Doc.fromBytes(doc.toBytes())
37
- }
38
- }
39
- if (!middle_doc) throw 'bad'
40
-
41
- // 2. let x = the resulting string
42
- let x = doc.get()
43
- console.log('x = ' + x)
44
-
45
- // // 3. use the code for sending these edits over the wire to create a new dt
46
- let updates = dt_get_patches(doc)
47
- console.log(updates)
48
-
49
- let new_doc = new Doc('server')
50
- apply_updates(new_doc, updates)
51
- let y = new_doc.get()
52
- console.log('y = ' + y)
53
-
54
- // 4. is the resulting string == x?
55
- console.log(x == y)
56
- if (x != y && n < best_n) {
57
- best_n = n
58
- best_seed = seed
59
- }
60
-
61
- // 5. test dt_get
62
- let middle_v = middle_doc.getRemoteVersion().map(x => x.join('-'))
63
- let new_middle_doc = dt_get(doc, middle_v)
64
- console.log('new_middle_doc = ' + new_middle_doc.get())
65
- if (middle_doc.get() != new_middle_doc.get() && n < best_n) {
66
- best_n = n
67
- best_seed = seed
68
48
  }
69
-
70
- // 6. test dt_get_patches(doc, version)
71
- if (true) {
72
- let updates = dt_get_patches(doc, middle_v)
73
- console.log(`updates:`, updates)
74
-
75
- apply_updates(middle_doc, updates)
76
- console.log(`middle_doc2:${middle_doc.get()}`)
77
- if (middle_doc.get() != doc.get() && n < best_n) {
78
- best_n = n
79
- best_seed = seed
49
+ if (!middle_doc) throw new Error('bad')
50
+
51
+ // put them into braid-text
52
+ let dt_to_braid = async (doc, key) => {
53
+ await braid_text.get(key, {})
54
+ for (let x of dt_get_patches(doc)) {
55
+ console.log(`x = `, x)
56
+ let y = {
57
+ merge_type: 'dt',
58
+ version: [x.version],
59
+ parents: x.parents,
60
+ patches: [{
61
+ unit: x.unit,
62
+ range: x.range,
63
+ content: x.content
64
+ }]
65
+ }
66
+ await braid_text.put(key, y)
67
+ y.validate_already_seen_versions = true
68
+ await braid_text.put(key, y)
80
69
  }
81
70
  }
82
-
83
- // 7. try applying a patch that's out of range..
84
- // if (true) {
85
- // let agent = Math.random().toString(36).slice(2)
86
- // let parents = doc.getRemoteVersion().map(x => x.join('-'))
87
- // let len = doc.len()
88
- // let args = [`${agent}-0`, parents, len + 1, 'c']
89
- // console.log('ARGS:', args)
90
- // try {
91
- // doc.mergeBytes(dt_create_bytes(...args))
92
- // } catch (e) {
93
- // console.log(`EEEE = ${e}`)
94
- // }
95
- // console.log('did that..')
96
- // }
71
+ await dt_to_braid(doc, 'doc')
72
+ await dt_to_braid(middle_doc, 'middle_doc')
73
+ console.log(`doc dt = ${doc.get()}`)
74
+ console.log(`middle_doc dt = ${middle_doc.get()}`)
75
+ console.log(`doc = ${await braid_text.get('doc')}`)
76
+ console.log(`middle_doc = ${await braid_text.get('middle_doc')}`)
77
+
78
+ // ensure they look right
79
+ if (doc.get() != await braid_text.get('doc')) throw new Error('bad')
80
+ if (middle_doc.get() != await braid_text.get('middle_doc')) throw new Error('bad')
81
+
82
+ // test getting old version
83
+ let middle_v = middle_doc.getRemoteVersion().map(x => x.join('-'))
84
+ console.log(`middle_doc = ${await braid_text.get('middle_doc')}`)
85
+ console.log(`middle_v = `, middle_v)
86
+
87
+ let doc_v = doc.getRemoteVersion().map(x => x.join('-'))
88
+ console.log(`doc_v = `, doc_v)
89
+
90
+ console.log(`doc = `, await braid_text.get('doc', {version: middle_v}))
91
+ if (await braid_text.get('middle_doc') != (await braid_text.get('doc', {version: middle_v})).body) throw new Error('bad')
92
+
93
+ // try getting updates from middle_doc to doc
94
+ let o = {merge_type: 'dt', parents: middle_v, subscribe: update => {
95
+ braid_text.put('middle_doc', update)
96
+ }}
97
+ await braid_text.get('doc', o)
98
+ await braid_text.forget('doc', o)
99
+
100
+ if (await braid_text.get('middle_doc') != await braid_text.get('doc')) throw new Error('bad')
101
+
102
+ doc.free()
103
+ middle_doc.free()
104
+ for (let p of Object.values(braid_text.cache))
105
+ (await p).doc.free()
106
+ braid_text.cache = {}
97
107
  } catch (e) {
98
108
  if (console.log == og_log) throw e
99
109
  if (n < best_n) {
@@ -132,74 +142,30 @@ function make_random_edit(doc) {
132
142
  let len = parent_doc.len()
133
143
  console.log(`len = ${len}`)
134
144
 
145
+ parent_doc.free()
146
+
135
147
  if (len && Math.random() > 0.5) {
136
148
  // delete
137
149
  let start = Math.floor(Math.random() * len)
138
150
  let del_len = Math.floor(Math.random() * (len - start - 1)) + 1
139
151
 
140
- for (let i = 0; i < del_len; i++) {
141
- let v = `${agent}-${base_seq++}`
142
- let args = [v, parents, start + del_len - 1 - i, null]
143
- console.log(args)
144
- doc.mergeBytes(dt_create_bytes(...args))
145
- parents = [v]
146
- }
152
+ let args = [`${agent}-${base_seq}`, parents, start, del_len, null]
153
+ console.log(args)
154
+ doc.mergeBytes(dt_create_bytes(...args))
147
155
  } else {
148
156
  // insert
149
157
  let start = Math.floor(Math.random() * (len + 1))
150
- let ins_len = Math.floor(Math.random() * 10) + 1
151
-
152
- for (let i = 0; i < ins_len; i++) {
153
- let v = `${agent}-${base_seq++}`
154
- let args = [v, parents, start++, getRandomCharacter()]
155
- console.log(args)
156
- doc.mergeBytes(dt_create_bytes(...args))
157
- parents = [v]
158
- }
158
+ let ins = Array(Math.floor(Math.random() * 10) + 1).fill(0).map(() => getRandomCharacter()).join('')
159
+
160
+ let args = [`${agent}-${base_seq}`, parents, start, 0, ins]
161
+ console.log(args)
162
+ doc.mergeBytes(dt_create_bytes(...args))
159
163
  }
160
164
 
161
165
  // work here
162
166
  console.log(`doc => ${doc.get()}`)
163
167
  }
164
168
 
165
- function apply_updates(doc, updates) {
166
- for (let u of updates) {
167
- u.range = u.range.match(/\d+/g).map((x) => parseInt(x))
168
- u.content = [...u.content]
169
-
170
- let v = u.version
171
- let ps = u.parents
172
-
173
- console.log('UPDATE:', u)
174
-
175
- // delete
176
- for (let i = u.range[1] - 1; i >= u.range[0]; i--) {
177
-
178
- // work here
179
- let args = [v, ps, i, null]
180
- console.log(`args`, args)
181
-
182
- doc.mergeBytes(dt_create_bytes(...args))
183
- ps = [v]
184
- v = decode_version(v)
185
- v = `${v[0]}-${v[1] + 1}`
186
- }
187
- // insert
188
- for (let i = 0; i < u.content?.length ?? 0; i++) {
189
- let c = u.content[i]
190
-
191
- // work here
192
- let args = [v, ps, u.range[0] + i, c]
193
- console.log(`args`, args)
194
-
195
- doc.mergeBytes(dt_create_bytes(...args))
196
- ps = [v]
197
- v = decode_version(v)
198
- v = `${v[0]}-${v[1] + 1}`
199
- }
200
- }
201
- }
202
-
203
169
  //////////////////////////////////////////////////////////////////
204
170
  //////////////////////////////////////////////////////////////////
205
171
  //////////////////////////////////////////////////////////////////