braid-text 0.2.23 → 0.2.25

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
@@ -76,6 +76,16 @@ braid_text.serve = async (req, res, options = {}) => {
76
76
  if (!req.subscribe) {
77
77
  res.setHeader("Accept-Subscribe", "true")
78
78
 
79
+ // special case for HEAD asking for version/parents,
80
+ // to be faster by not reconstructing body
81
+ if (req.method === "HEAD" && (req.version || req.parents)) {
82
+ if ((req.version || req.parents).every(event => {
83
+ var [actor, seq] = decode_version(event)
84
+ return resource.actor_seqs[actor]?.has(seq)
85
+ })) return my_end(200)
86
+ else return my_end(500, "Unknown Version")
87
+ }
88
+
79
89
  let x = null
80
90
  try {
81
91
  x = await braid_text.get(resource, { version: req.version, parents: req.parents })
@@ -800,20 +810,8 @@ function dt_len(doc, version) {
800
810
  function dt_get_string(doc, version) {
801
811
  var bytes = doc.toBytes()
802
812
  var oplog = OpLog.fromBytes(bytes)
803
- var [_agents, versions, _parentss] = dt_parse([...bytes])
804
-
805
- var frontier = new Set(version)
806
-
807
- var local_version = []
808
- for (var i = 0; i < versions.length; i++) {
809
- var v = versions[i].join("-")
810
- if (frontier.has(v)) {
811
- local_version.push(i)
812
- frontier.delete(v)
813
- }
814
- }
815
813
 
816
- if (frontier.size) throw new Error(`version not found: ${version}`)
814
+ var local_version = dt_get_local_version(bytes, version)
817
815
 
818
816
  var b = new Branch()
819
817
  b.merge(oplog, new Uint32Array(local_version))
@@ -1008,25 +1006,25 @@ function dt_parse(byte_array) {
1008
1006
 
1009
1007
  while (byte_array.length) {
1010
1008
  let id = byte_array.shift()
1011
- let len = read_varint(byte_array)
1009
+ let len = dt_read_varint(byte_array)
1012
1010
  if (id == 1) {
1013
1011
  } else if (id == 3) {
1014
1012
  let goal = byte_array.length - len
1015
1013
  while (byte_array.length > goal) {
1016
- agents.push(read_string(byte_array))
1014
+ agents.push(dt_read_string(byte_array))
1017
1015
  }
1018
1016
  } else if (id == 20) {
1019
1017
  } else if (id == 21) {
1020
1018
  let seqs = {}
1021
1019
  let goal = byte_array.length - len
1022
1020
  while (byte_array.length > goal) {
1023
- let part0 = read_varint(byte_array)
1021
+ let part0 = dt_read_varint(byte_array)
1024
1022
  let has_jump = part0 & 1
1025
1023
  let agent_i = (part0 >> 1) - 1
1026
- let run_length = read_varint(byte_array)
1024
+ let run_length = dt_read_varint(byte_array)
1027
1025
  let jump = 0
1028
1026
  if (has_jump) {
1029
- let part2 = read_varint(byte_array)
1027
+ let part2 = dt_read_varint(byte_array)
1030
1028
  jump = part2 >> 1
1031
1029
  if (part2 & 1) jump *= -1
1032
1030
  }
@@ -1041,12 +1039,12 @@ function dt_parse(byte_array) {
1041
1039
  let count = 0
1042
1040
  let goal = byte_array.length - len
1043
1041
  while (byte_array.length > goal) {
1044
- let run_len = read_varint(byte_array)
1042
+ let run_len = dt_read_varint(byte_array)
1045
1043
 
1046
1044
  let parents = []
1047
1045
  let has_more = 1
1048
1046
  while (has_more) {
1049
- let x = read_varint(byte_array)
1047
+ let x = dt_read_varint(byte_array)
1050
1048
  let is_foreign = 0x1 & x
1051
1049
  has_more = 0x2 & x
1052
1050
  let num = x >> 2
@@ -1056,7 +1054,7 @@ function dt_parse(byte_array) {
1056
1054
  } else if (!is_foreign) {
1057
1055
  parents.push(versions[count - num])
1058
1056
  } else {
1059
- parents.push([agents[num - 1], read_varint(byte_array)])
1057
+ parents.push([agents[num - 1], dt_read_varint(byte_array)])
1060
1058
  }
1061
1059
  }
1062
1060
  parentss.push(parents)
@@ -1072,24 +1070,107 @@ function dt_parse(byte_array) {
1072
1070
  }
1073
1071
  }
1074
1072
 
1075
- function read_string(byte_array) {
1076
- return new TextDecoder().decode(new Uint8Array(byte_array.splice(0, read_varint(byte_array))))
1073
+ return [agents, versions, parentss]
1074
+ }
1075
+
1076
+ function dt_get_local_version(bytes, version) {
1077
+ var looking_for = new Map()
1078
+ for (var event of version) {
1079
+ var [agent, seq] = decode_version(event)
1080
+ if (!looking_for.has(agent)) looking_for.set(agent, [])
1081
+ looking_for.get(agent).push(seq)
1077
1082
  }
1083
+ for (var seqs of looking_for.values())
1084
+ seqs.sort((a, b) => a - b)
1078
1085
 
1079
- function read_varint(byte_array) {
1080
- let result = 0
1081
- let shift = 0
1082
- while (true) {
1083
- if (byte_array.length === 0) throw new Error("byte array does not contain varint")
1086
+ var byte_array = [...bytes]
1087
+ var local_version = []
1088
+ var local_version_base = 0
1089
+
1090
+ if (new TextDecoder().decode(new Uint8Array(byte_array.splice(0, 8))) !== "DMNDTYPS") throw new Error("dt parse error, expected DMNDTYPS")
1091
+
1092
+ if (byte_array.shift() != 0) throw new Error("dt parse error, expected version 0")
1093
+
1094
+ let agents = []
1095
+
1096
+ while (byte_array.length && looking_for.size) {
1097
+ let id = byte_array.shift()
1098
+ let len = dt_read_varint(byte_array)
1099
+ if (id == 1) {
1100
+ } else if (id == 3) {
1101
+ let goal = byte_array.length - len
1102
+ while (byte_array.length > goal) {
1103
+ agents.push(dt_read_string(byte_array))
1104
+ }
1105
+ } else if (id == 20) {
1106
+ } else if (id == 21) {
1107
+ let seqs = {}
1108
+ let goal = byte_array.length - len
1109
+ while (byte_array.length > goal && looking_for.size) {
1110
+ let part0 = dt_read_varint(byte_array)
1111
+ let has_jump = part0 & 1
1112
+ let agent_i = (part0 >> 1) - 1
1113
+ let run_length = dt_read_varint(byte_array)
1114
+ let jump = 0
1115
+ if (has_jump) {
1116
+ let part2 = dt_read_varint(byte_array)
1117
+ jump = part2 >> 1
1118
+ if (part2 & 1) jump *= -1
1119
+ }
1120
+ let base = (seqs[agent_i] || 0) + jump
1121
+
1122
+ var agent = agents[agent_i]
1123
+ looking_for_seqs = looking_for.get(agent)
1124
+ if (looking_for_seqs) {
1125
+ for (var seq of splice_out_range(
1126
+ looking_for_seqs, base, base + run_length - 1))
1127
+ local_version.push(local_version_base + (seq - base))
1128
+ if (!looking_for_seqs.length) looking_for.delete(agent)
1129
+ }
1130
+ local_version_base += run_length
1131
+
1132
+ seqs[agent_i] = base + run_length
1133
+ }
1134
+ } else {
1135
+ byte_array.splice(0, len)
1136
+ }
1137
+ }
1138
+
1139
+ if (looking_for.size) throw new Error(`version not found: ${version}`)
1140
+ return local_version
1084
1141
 
1085
- let byte_val = byte_array.shift()
1086
- result |= (byte_val & 0x7f) << shift
1087
- if ((byte_val & 0x80) == 0) return result
1088
- shift += 7
1142
+ function splice_out_range(a, s, e) {
1143
+ if (!a?.length) return [];
1144
+ let l = 0, r = a.length;
1145
+ while (l < r) {
1146
+ const m = Math.floor((l + r) / 2);
1147
+ if (a[m] < s) l = m + 1; else r = m;
1148
+ }
1149
+ const i = l;
1150
+ l = i; r = a.length;
1151
+ while (l < r) {
1152
+ const m = Math.floor((l + r) / 2);
1153
+ if (a[m] <= e) l = m + 1; else r = m;
1089
1154
  }
1155
+ return a.splice(i, l - i);
1090
1156
  }
1157
+ }
1091
1158
 
1092
- return [agents, versions, parentss]
1159
+ function dt_read_string(byte_array) {
1160
+ return new TextDecoder().decode(new Uint8Array(byte_array.splice(0, dt_read_varint(byte_array))))
1161
+ }
1162
+
1163
+ function dt_read_varint(byte_array) {
1164
+ let result = 0
1165
+ let shift = 0
1166
+ while (true) {
1167
+ if (byte_array.length === 0) throw new Error("byte array does not contain varint")
1168
+
1169
+ let byte_val = byte_array.shift()
1170
+ result |= (byte_val & 0x7f) << shift
1171
+ if ((byte_val & 0x80) == 0) return result
1172
+ shift += 7
1173
+ }
1093
1174
  }
1094
1175
 
1095
1176
  function dt_create_bytes(version, parents, pos, del, ins) {
@@ -1808,8 +1889,10 @@ braid_text.get_files_for_key = get_files_for_key
1808
1889
  braid_text.dt_get = dt_get
1809
1890
  braid_text.dt_get_patches = dt_get_patches
1810
1891
  braid_text.dt_parse = dt_parse
1892
+ braid_text.dt_get_local_version = dt_get_local_version
1811
1893
  braid_text.dt_create_bytes = dt_create_bytes
1812
1894
 
1813
1895
  braid_text.decode_version = decode_version
1896
+ braid_text.RangeSet = RangeSet
1814
1897
 
1815
1898
  module.exports = braid_text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
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
@@ -266,4 +266,34 @@ runTest(
266
266
  'retried!'
267
267
  )
268
268
 
269
+ runTest(
270
+ "test asking for a version that should and shouldn't be there",
271
+ async () => {
272
+ var key = 'test-' + Math.random().toString(36).slice(2)
273
+
274
+ var r = await braid_fetch(`/${key}`, {
275
+ method: 'PUT',
276
+ version: ['hi-10'],
277
+ parents: [],
278
+ body: 'x'
279
+ })
280
+ if (!r.ok) throw 'got: ' + r.statusCode
281
+
282
+ var r = await braid_fetch(`/${key}`, {
283
+ method: 'HEAD',
284
+ version: ['hi-5']
285
+ })
286
+ if (r.ok) throw 'found version we should not have found'
287
+
288
+ var r = await braid_fetch(`/${key}`, {
289
+ method: 'HEAD',
290
+ version: ['hi-10']
291
+ })
292
+ if (!r.ok) throw 'could not find version we should have found'
293
+
294
+ return 'worked out!'
295
+ },
296
+ 'worked out!'
297
+ )
298
+
269
299
  </script>
package/test/test.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  let { Doc } = require("diamond-types-node")
3
3
  let braid_text = require('../index.js')
4
- let {dt_get, dt_get_patches, dt_parse, dt_create_bytes} = braid_text
4
+ let {dt_get, dt_get_patches, dt_parse, dt_create_bytes, dt_get_local_version, RangeSet} = braid_text
5
5
 
6
6
  process.on("unhandledRejection", (x) =>
7
7
  console.log(`unhandledRejection: ${x.stack ?? x}`)
@@ -25,12 +25,117 @@ async function test_db() {
25
25
  braid_text.db_folder = null
26
26
  }
27
27
 
28
+ async function test_dt_get_local_version() {
29
+ for (var tt = 0; tt < 100; tt++) {
30
+ var st = Date.now()
31
+
32
+ var doc = new Doc('x')
33
+ var n = 100
34
+ var v_array = []
35
+ var p_array = []
36
+ var agents = ['hi']
37
+ var agent_rangesets = {}
38
+
39
+ for (var i = 0; i < n; i++) {
40
+ // console.log(`i = ${i}`)
41
+
42
+ // Math.randomSeed('seed::::' + i)
43
+
44
+ var agent = agents[0]
45
+
46
+ var seq = null
47
+ if (!agent_rangesets[agent]) agent_rangesets[agent] = new RangeSet()
48
+ var ranges = agent_rangesets[agent].ranges
49
+ if (!ranges.length) {
50
+ seq = Math.floor(Math.random() * 10)
51
+ } else {
52
+ var ii
53
+ if (ranges[0]?.[0] === 0)
54
+ ii = Math.floor(Math.random() * ranges.length)
55
+ else
56
+ ii = Math.floor(Math.random() * (ranges.length + 1)) - 1
57
+ var low = ii < 0 ? 0 : ranges[ii][1] + 1
58
+ var high = ii + 1 < ranges.length ? ranges[ii + 1][0] - 1 : ranges[ii][1] + 10
59
+ seq = Math.random() < 0.8 ? low : low + Math.floor(Math.random() * (high - low + 1))
60
+ }
61
+ agent_rangesets[agent].add_range(seq, seq)
62
+ v_array.push(`${agent}-${seq}`)
63
+
64
+ var parents = []
65
+ var shadow = new Set()
66
+ if (p_array.length) {
67
+ var starting_point = Math.floor(Math.random() * p_array.length)
68
+ parents.push(starting_point)
69
+ for (var p of p_array[starting_point]) shadow.add(p)
70
+ for (var ii = starting_point - 1; ii >= 0 && Math.random() < 0.9; ii--) {
71
+ if (!shadow.has(ii) && Math.random() < 0.5) {
72
+ parents.push(ii)
73
+ shadow.add(ii)
74
+ }
75
+ if (shadow.has(ii))
76
+ for (var p of p_array[ii])
77
+ shadow.add(p)
78
+ }
79
+ }
80
+ p_array.push(parents)
81
+ parents = parents.map(p => v_array[p])
82
+
83
+ let args = [`${agent}-${seq}`, parents, 0, 0, 'x']
84
+ // console.log(args)
85
+
86
+ doc.mergeBytes(dt_create_bytes(...args))
87
+ }
88
+
89
+ var bytes = doc.toBytes()
90
+
91
+ for (var t = 0; t < 100; t++) {
92
+ // if (t % 10 === 0) console.log(` t = ${t}`)
93
+
94
+ var version = []
95
+ var vv_array = [...v_array]
96
+ while (!version.length || (vv_array.length && Math.random() < 0.9)) {
97
+ version.push(vv_array.splice(Math.floor(Math.random() * vv_array.length), 1)[0])
98
+ }
99
+
100
+ var local_new = dt_get_local_version(bytes, version)
101
+ var local_old = old_dt_get_local_version(bytes, version)
102
+
103
+ if (local_new.some((x, i) => x != local_old[i])) {
104
+ throw new Error('bad!')
105
+ }
106
+ }
107
+
108
+ console.log(`[tt=${tt}] total T = ${Date.now() - st}`)
109
+ }
110
+ }
111
+
112
+ function old_dt_get_local_version(bytes, version) {
113
+ var [_agents, versions, _parentss] = dt_parse([...bytes])
114
+
115
+ var frontier = new Set(version)
116
+
117
+ var local_version = []
118
+ for (var i = 0; i < versions.length; i++) {
119
+ var v = versions[i].join("-")
120
+ if (frontier.has(v)) {
121
+ local_version.push(i)
122
+ frontier.delete(v)
123
+ }
124
+ }
125
+
126
+ if (frontier.size) throw new Error(`version not found: ${version}`)
127
+
128
+ return local_version
129
+ }
130
+
28
131
  async function main() {
29
132
  let best_seed = NaN
30
133
  let best_n = Infinity
31
134
  let base = Math.floor(Math.random() * 10000000)
32
135
  let st = Date.now()
33
136
 
137
+ await test_dt_get_local_version()
138
+
34
139
  await test_db()
35
140
 
36
141
  let og_log = console.log
@@ -38,7 +143,7 @@ async function main() {
38
143
  for (let t = 0; t < 10000; t++) {
39
144
  let seed = base + t
40
145
  // for (let t = 0; t < 1; t++) {
41
- // let seed = 2746153
146
+ // let seed = 403279
42
147
 
43
148
  og_log(`t = ${t}, seed = ${seed}, best_n = ${best_n} @ ${best_seed}`)
44
149
  Math.randomSeed(seed)