braid-text 0.2.114 → 0.2.115
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 +254 -113
- package/package.json +4 -1
package/index.js
CHANGED
|
@@ -850,8 +850,9 @@ function create_braid_text() {
|
|
|
850
850
|
return await braid_fetch(key.href, params)
|
|
851
851
|
}
|
|
852
852
|
|
|
853
|
-
|
|
854
|
-
|
|
853
|
+
let resource = (typeof key == 'string') ? await get_resource(key) : key
|
|
854
|
+
|
|
855
|
+
return await within_fiber('put:' + resource.key, async () => {
|
|
855
856
|
|
|
856
857
|
// support for json patch puts..
|
|
857
858
|
if (options.patches && options.patches.length &&
|
|
@@ -862,7 +863,7 @@ function create_braid_text() {
|
|
|
862
863
|
options = { body: JSON.stringify(x, null, 4) }
|
|
863
864
|
}
|
|
864
865
|
|
|
865
|
-
let { version, patches, body, peer } = options
|
|
866
|
+
let { version, parents, patches, body, peer } = options
|
|
866
867
|
|
|
867
868
|
if (options.transfer_encoding === 'dt') {
|
|
868
869
|
var start_i = 1 + resource.doc.getLocalVersion().reduce((a, b) => Math.max(a, b), -1)
|
|
@@ -896,30 +897,24 @@ function create_braid_text() {
|
|
|
896
897
|
if (version && version.length > 1)
|
|
897
898
|
throw new Error(`cannot put a version with multiple ids`)
|
|
898
899
|
|
|
899
|
-
// translate a single parent of "root" to the empty array (same meaning)
|
|
900
|
-
let options_parents = options.parents
|
|
901
|
-
if (options_parents?.length === 1 && options_parents[0] === 'root')
|
|
902
|
-
options_parents = []
|
|
903
|
-
|
|
904
900
|
if (body != null && patches) throw new Error(`cannot have a body and patches`)
|
|
905
901
|
if (body != null && (typeof body !== 'string')) throw new Error(`body must be a string`)
|
|
906
902
|
if (patches) validate_patches(patches)
|
|
907
903
|
|
|
908
|
-
if (
|
|
904
|
+
if (parents) {
|
|
909
905
|
// make sure we have all these parents
|
|
910
|
-
for (let p of
|
|
906
|
+
for (let p of parents) {
|
|
911
907
|
let P = decode_version(p)
|
|
912
908
|
if (!resource.actor_seqs[P[0]]?.has(P[1]))
|
|
913
909
|
throw new Error(`missing parent version: ${p}`)
|
|
914
910
|
}
|
|
915
911
|
}
|
|
916
912
|
|
|
917
|
-
|
|
918
|
-
|
|
913
|
+
if (!parents) parents = resource.version
|
|
914
|
+
|
|
915
|
+
let max_pos = resource.length_cache.get('' + parents) ??
|
|
916
|
+
(v_eq(resource.version, parents) ? resource.doc.len() : dt_len(resource.doc, parents))
|
|
919
917
|
|
|
920
|
-
let max_pos = resource.length_cache.get('' + og_parents) ??
|
|
921
|
-
(v_eq(parents, og_parents) ? resource.doc.len() : dt_len(resource.doc, og_parents))
|
|
922
|
-
|
|
923
918
|
if (body != null) {
|
|
924
919
|
patches = [{
|
|
925
920
|
unit: 'text',
|
|
@@ -928,7 +923,6 @@ function create_braid_text() {
|
|
|
928
923
|
}]
|
|
929
924
|
}
|
|
930
925
|
|
|
931
|
-
let og_patches = patches
|
|
932
926
|
patches = patches.map((p) => ({
|
|
933
927
|
...p,
|
|
934
928
|
range: p.range.match(/-?\d+/g).map((x) => {
|
|
@@ -940,94 +934,66 @@ function create_braid_text() {
|
|
|
940
934
|
content_codepoints: [...p.content],
|
|
941
935
|
})).sort((a, b) => a.range[0] - b.range[0])
|
|
942
936
|
|
|
943
|
-
// validate patch positions
|
|
944
|
-
let must_be_at_least = 0
|
|
945
|
-
for (let p of patches) {
|
|
946
|
-
if (p.range[0] < must_be_at_least || p.range[0] > max_pos) throw new Error(`invalid patch range position: ${p.range[0]}`)
|
|
947
|
-
if (p.range[1] < p.range[0] || p.range[1] > max_pos) throw new Error(`invalid patch range position: ${p.range[1]}`)
|
|
948
|
-
must_be_at_least = p.range[1]
|
|
949
|
-
}
|
|
950
|
-
|
|
951
937
|
let change_count = patches.reduce((a, b) => a + b.content_codepoints.length + (b.range[1] - b.range[0]), 0)
|
|
952
938
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
let v = decode_version(og_v)
|
|
939
|
+
version = version?.[0] || `${(is_valid_actor(peer) && peer) || Math.random().toString(36).slice(2, 7)}-${change_count - 1}`
|
|
956
940
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
max_pos))
|
|
941
|
+
let v = decode_version(version)
|
|
942
|
+
var low_seq = v[1] + 1 - change_count
|
|
960
943
|
|
|
961
|
-
//
|
|
962
|
-
|
|
944
|
+
// make sure we haven't seen this already
|
|
945
|
+
var intersects_range = resource.actor_seqs[v[0]]?.has(low_seq, v[1])
|
|
946
|
+
if (intersects_range) {
|
|
947
|
+
// if low_seq is below the range min,
|
|
948
|
+
// then the intersection has gaps,
|
|
949
|
+
// which is bad, meaning the prior versions must be different,
|
|
950
|
+
// because what we're inserting is contiguous
|
|
951
|
+
if (low_seq < intersects_range[0])
|
|
952
|
+
throw new Error('invalid update: different from previous update with same version')
|
|
963
953
|
|
|
964
|
-
if
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
let seen = {}
|
|
970
|
-
for (let u of updates) {
|
|
971
|
-
u.version = decode_version(u.version)
|
|
972
|
-
|
|
973
|
-
if (!u.content) {
|
|
974
|
-
// delete
|
|
975
|
-
let v = u.version
|
|
976
|
-
for (let i = 0; i < u.end - u.start; i++) {
|
|
977
|
-
let ps = (i < u.end - u.start - 1) ? [`${v[0]}-${v[1] - i - 1}`] : u.parents
|
|
978
|
-
seen[JSON.stringify([v[0], v[1] - i, ps, u.start + i])] = true
|
|
979
|
-
}
|
|
980
|
-
} else {
|
|
981
|
-
// insert
|
|
982
|
-
let v = u.version
|
|
983
|
-
let content = [...u.content]
|
|
984
|
-
for (let i = 0; i < content.length; i++) {
|
|
985
|
-
let ps = (i > 0) ? [`${v[0]}-${v[1] - content.length + i}`] : u.parents
|
|
986
|
-
seen[JSON.stringify([v[0], v[1] + 1 - content.length + i, ps, u.start + i, content[i]])] = true
|
|
987
|
-
}
|
|
988
|
-
}
|
|
954
|
+
// see if we only have *some* of the versions
|
|
955
|
+
var new_count = v[1] - intersects_range[1]
|
|
956
|
+
if (new_count > 0) {
|
|
957
|
+
// divide the patches between old and new..
|
|
958
|
+
var new_patches = split_patches(patches, change_count - new_count)
|
|
989
959
|
}
|
|
990
960
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
let offset = 0
|
|
994
|
-
for (let p of patches) {
|
|
995
|
-
// delete
|
|
996
|
-
for (let i = p.range[0]; i < p.range[1]; i++) {
|
|
997
|
-
let vv = decode_version(v)
|
|
961
|
+
if (options.validate_already_seen_versions)
|
|
962
|
+
validate_old_patches(resource, `${v[0]}-${low_seq}`, parents, patches)
|
|
998
963
|
|
|
999
|
-
|
|
964
|
+
if (new_count <= 0) return { change_count }
|
|
1000
965
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
let vv = decode_version(v)
|
|
1009
|
-
let c = p.content_codepoints[i]
|
|
966
|
+
change_count = new_count
|
|
967
|
+
low_seq = v[1] + 1 - change_count
|
|
968
|
+
parents = [`${v[0]}-${low_seq - 1}`]
|
|
969
|
+
max_pos = resource.length_cache.get('' + parents) ??
|
|
970
|
+
(v_eq(resource.version, parents) ? resource.doc.len() : dt_len(resource.doc, parents))
|
|
971
|
+
patches = new_patches
|
|
972
|
+
}
|
|
1010
973
|
|
|
1011
|
-
|
|
974
|
+
// validate patch positions
|
|
975
|
+
let must_be_at_least = 0
|
|
976
|
+
for (let p of patches) {
|
|
977
|
+
if (p.range[0] < must_be_at_least || p.range[0] > max_pos)
|
|
978
|
+
throw new Error(`invalid patch range position: ${p.range[0]}`)
|
|
979
|
+
if (p.range[1] < p.range[0] || p.range[1] > max_pos)
|
|
980
|
+
throw new Error(`invalid patch range position: ${p.range[1]}`)
|
|
981
|
+
must_be_at_least = p.range[1]
|
|
982
|
+
}
|
|
1012
983
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
v = `${v[0]}-${v[1] + 1}`
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
984
|
+
resource.length_cache.put(`${v[0]}-${v[1]}`, patches.reduce((a, b) =>
|
|
985
|
+
a + (b.content_codepoints?.length ?? 0) - (b.range[1] - b.range[0]),
|
|
986
|
+
max_pos))
|
|
1019
987
|
|
|
1020
|
-
// we already have this version, so nothing left to do
|
|
1021
|
-
return { change_count: change_count }
|
|
1022
|
-
}
|
|
1023
988
|
if (!resource.actor_seqs[v[0]]) resource.actor_seqs[v[0]] = new RangeSet()
|
|
1024
|
-
resource.actor_seqs[v[0]].add_range(
|
|
989
|
+
resource.actor_seqs[v[0]].add_range(low_seq, v[1])
|
|
1025
990
|
|
|
1026
|
-
//
|
|
1027
|
-
v = `${v[0]}-${
|
|
991
|
+
// get the version of the first character-wise edit
|
|
992
|
+
v = `${v[0]}-${low_seq}`
|
|
1028
993
|
|
|
1029
|
-
let ps =
|
|
994
|
+
let ps = parents
|
|
1030
995
|
|
|
996
|
+
let version_before = resource.version
|
|
1031
997
|
let v_before = resource.doc.getLocalVersion()
|
|
1032
998
|
|
|
1033
999
|
let bytes = []
|
|
@@ -1060,14 +1026,12 @@ function create_braid_text() {
|
|
|
1060
1026
|
var post_commit_updates = []
|
|
1061
1027
|
|
|
1062
1028
|
if (options.merge_type != "dt") {
|
|
1063
|
-
patches = get_xf_patches(resource.doc, v_before)
|
|
1029
|
+
let patches = get_xf_patches(resource.doc, v_before)
|
|
1064
1030
|
if (braid_text.verbose) console.log(JSON.stringify({ patches }))
|
|
1065
1031
|
|
|
1066
|
-
let version = resource.version
|
|
1067
|
-
|
|
1068
1032
|
for (let client of resource.simpleton_clients) {
|
|
1069
1033
|
if (peer && client.peer === peer) {
|
|
1070
|
-
client.my_last_seen_version = [
|
|
1034
|
+
client.my_last_seen_version = [version]
|
|
1071
1035
|
}
|
|
1072
1036
|
|
|
1073
1037
|
function set_timeout(time_override) {
|
|
@@ -1076,10 +1040,10 @@ function create_braid_text() {
|
|
|
1076
1040
|
// if the doc has been freed, exit early
|
|
1077
1041
|
if (resource.doc.__wbg_ptr === 0) return
|
|
1078
1042
|
|
|
1079
|
-
let
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1043
|
+
let x = {
|
|
1044
|
+
version: resource.version,
|
|
1045
|
+
parents: client.my_last_seen_version
|
|
1046
|
+
}
|
|
1083
1047
|
if (braid_text.verbose) console.log("rebasing after timeout.. ")
|
|
1084
1048
|
if (braid_text.verbose) console.log(" client.my_unused_version_count = " + client.my_unused_version_count)
|
|
1085
1049
|
x.patches = get_xf_patches(resource.doc, OpLog_remote_to_local(resource.doc, client.my_last_seen_version))
|
|
@@ -1094,7 +1058,7 @@ function create_braid_text() {
|
|
|
1094
1058
|
|
|
1095
1059
|
if (client.my_timeout) {
|
|
1096
1060
|
if (peer && client.peer === peer) {
|
|
1097
|
-
if (!v_eq(client.my_last_sent_version,
|
|
1061
|
+
if (!v_eq(client.my_last_sent_version, parents)) {
|
|
1098
1062
|
// note: we don't add to client.my_unused_version_count,
|
|
1099
1063
|
// because we're already in a timeout;
|
|
1100
1064
|
// we'll just extend it here..
|
|
@@ -1108,9 +1072,9 @@ function create_braid_text() {
|
|
|
1108
1072
|
continue
|
|
1109
1073
|
}
|
|
1110
1074
|
|
|
1111
|
-
let x = { version }
|
|
1075
|
+
let x = { version: resource.version }
|
|
1112
1076
|
if (peer && client.peer === peer) {
|
|
1113
|
-
if (!v_eq(client.my_last_sent_version,
|
|
1077
|
+
if (!v_eq(client.my_last_sent_version, parents)) {
|
|
1114
1078
|
client.my_unused_version_count = (client.my_unused_version_count ?? 0) + 1
|
|
1115
1079
|
set_timeout()
|
|
1116
1080
|
continue
|
|
@@ -1118,10 +1082,10 @@ function create_braid_text() {
|
|
|
1118
1082
|
delete client.my_unused_version_count
|
|
1119
1083
|
}
|
|
1120
1084
|
|
|
1121
|
-
x.parents =
|
|
1122
|
-
if (!v_eq(version,
|
|
1085
|
+
x.parents = [version]
|
|
1086
|
+
if (!v_eq(x.version, x.parents)) {
|
|
1123
1087
|
if (braid_text.verbose) console.log("rebasing..")
|
|
1124
|
-
x.patches = get_xf_patches(resource.doc, OpLog_remote_to_local(resource.doc,
|
|
1088
|
+
x.patches = get_xf_patches(resource.doc, OpLog_remote_to_local(resource.doc, x.parents))
|
|
1125
1089
|
} else {
|
|
1126
1090
|
// this client already has this version,
|
|
1127
1091
|
// so let's pretend to send it back, but not
|
|
@@ -1130,7 +1094,7 @@ function create_braid_text() {
|
|
|
1130
1094
|
continue
|
|
1131
1095
|
}
|
|
1132
1096
|
} else {
|
|
1133
|
-
x.parents =
|
|
1097
|
+
x.parents = version_before
|
|
1134
1098
|
x.patches = patches
|
|
1135
1099
|
}
|
|
1136
1100
|
if (braid_text.verbose) console.log(`sending: ${JSON.stringify(x)}`)
|
|
@@ -1139,9 +1103,11 @@ function create_braid_text() {
|
|
|
1139
1103
|
}
|
|
1140
1104
|
} else {
|
|
1141
1105
|
if (resource.simpleton_clients.size) {
|
|
1142
|
-
let
|
|
1143
|
-
|
|
1144
|
-
|
|
1106
|
+
let x = {
|
|
1107
|
+
version: resource.version,
|
|
1108
|
+
parents: version_before,
|
|
1109
|
+
patches: get_xf_patches(resource.doc, v_before)
|
|
1110
|
+
}
|
|
1145
1111
|
if (braid_text.verbose) console.log(`sending: ${JSON.stringify(x)}`)
|
|
1146
1112
|
for (let client of resource.simpleton_clients) {
|
|
1147
1113
|
if (client.my_timeout) continue
|
|
@@ -1152,9 +1118,13 @@ function create_braid_text() {
|
|
|
1152
1118
|
}
|
|
1153
1119
|
|
|
1154
1120
|
var x = {
|
|
1155
|
-
version: [
|
|
1156
|
-
parents
|
|
1157
|
-
patches:
|
|
1121
|
+
version: [version],
|
|
1122
|
+
parents,
|
|
1123
|
+
patches: patches.map(p => ({
|
|
1124
|
+
unit: p.unit,
|
|
1125
|
+
range: `[${p.range.join(':')}]`,
|
|
1126
|
+
content: p.content
|
|
1127
|
+
})),
|
|
1158
1128
|
}
|
|
1159
1129
|
for (let client of resource.clients) {
|
|
1160
1130
|
if (!peer || client.peer !== peer)
|
|
@@ -1540,6 +1510,62 @@ function create_braid_text() {
|
|
|
1540
1510
|
if (!seqs.length) delete ns.actor_seqs[actor]
|
|
1541
1511
|
}
|
|
1542
1512
|
|
|
1513
|
+
function validate_old_patches(resource, base_v, parents, patches) {
|
|
1514
|
+
// if we have seen it already, make sure it's the same as before
|
|
1515
|
+
let updates = dt_get_patches(resource.doc, parents)
|
|
1516
|
+
|
|
1517
|
+
let seen = {}
|
|
1518
|
+
for (let u of updates) {
|
|
1519
|
+
u.version = decode_version(u.version)
|
|
1520
|
+
|
|
1521
|
+
if (!u.content) {
|
|
1522
|
+
// delete
|
|
1523
|
+
let v = u.version
|
|
1524
|
+
for (let i = 0; i < u.end - u.start; i++) {
|
|
1525
|
+
let ps = (i < u.end - u.start - 1) ? [`${v[0]}-${v[1] - i - 1}`] : u.parents
|
|
1526
|
+
seen[JSON.stringify([v[0], v[1] - i, ps, u.start + i])] = true
|
|
1527
|
+
}
|
|
1528
|
+
} else {
|
|
1529
|
+
// insert
|
|
1530
|
+
let v = u.version
|
|
1531
|
+
let content = [...u.content]
|
|
1532
|
+
for (let i = 0; i < content.length; i++) {
|
|
1533
|
+
let ps = (i > 0) ? [`${v[0]}-${v[1] - content.length + i}`] : u.parents
|
|
1534
|
+
seen[JSON.stringify([v[0], v[1] + 1 - content.length + i, ps, u.start + i, content[i]])] = true
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
let v = base_v
|
|
1540
|
+
let ps = parents
|
|
1541
|
+
let offset = 0
|
|
1542
|
+
for (let p of patches) {
|
|
1543
|
+
// delete
|
|
1544
|
+
for (let i = p.range[0]; i < p.range[1]; i++) {
|
|
1545
|
+
let vv = decode_version(v)
|
|
1546
|
+
|
|
1547
|
+
if (!seen[JSON.stringify([vv[0], vv[1], ps, p.range[1] - 1 + offset])]) throw new Error('invalid update: different from previous update with same version')
|
|
1548
|
+
|
|
1549
|
+
offset--
|
|
1550
|
+
ps = [v]
|
|
1551
|
+
v = vv
|
|
1552
|
+
v = `${v[0]}-${v[1] + 1}`
|
|
1553
|
+
}
|
|
1554
|
+
// insert
|
|
1555
|
+
for (let i = 0; i < p.content_codepoints?.length ?? 0; i++) {
|
|
1556
|
+
let vv = decode_version(v)
|
|
1557
|
+
let c = p.content_codepoints[i]
|
|
1558
|
+
|
|
1559
|
+
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')
|
|
1560
|
+
|
|
1561
|
+
offset++
|
|
1562
|
+
ps = [v]
|
|
1563
|
+
v = vv
|
|
1564
|
+
v = `${v[0]}-${v[1] + 1}`
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1543
1569
|
//////////////////////////////////////////////////////////////////
|
|
1544
1570
|
//////////////////////////////////////////////////////////////////
|
|
1545
1571
|
//////////////////////////////////////////////////////////////////
|
|
@@ -2543,6 +2569,120 @@ function create_braid_text() {
|
|
|
2543
2569
|
if (typeof x.content !== 'string') throw new Error(`invalid patch content: must be a string`)
|
|
2544
2570
|
}
|
|
2545
2571
|
|
|
2572
|
+
// Splits an array of patches at a given character position within the
|
|
2573
|
+
// combined delete+insert sequence.
|
|
2574
|
+
//
|
|
2575
|
+
// Patches are objects with:
|
|
2576
|
+
// - unit: string (e.g., 'text')
|
|
2577
|
+
// - range: [start, end] - character positions for deletion
|
|
2578
|
+
// - content: string - the content to insert
|
|
2579
|
+
// - content_codepoints: array of single characters
|
|
2580
|
+
//
|
|
2581
|
+
// Each patch represents a "replace" operation: delete then insert.
|
|
2582
|
+
// The combined sequence for patches is:
|
|
2583
|
+
// del(patch1), ins(patch1), del(patch2), ins(patch2), ...
|
|
2584
|
+
//
|
|
2585
|
+
// The split_point is an index into this combined sequence.
|
|
2586
|
+
//
|
|
2587
|
+
// Example: patches with del(3),ins(4),del(2),ins(5)
|
|
2588
|
+
// - split_point 1 falls in first del(3)
|
|
2589
|
+
// - split_point 5 falls in first ins(4) (positions 3-6)
|
|
2590
|
+
// - split_point 7 falls in second del(2) (positions 7-8)
|
|
2591
|
+
//
|
|
2592
|
+
// First patches: operations up to split_point
|
|
2593
|
+
// Second patches: operations from split_point onward (ranges adjusted)
|
|
2594
|
+
function split_patches(patches, split_point) {
|
|
2595
|
+
let second_patches = []
|
|
2596
|
+
|
|
2597
|
+
let position = 0 // current position in the combined sequence
|
|
2598
|
+
let adjustment = 0 // how much to adjust second patches' ranges
|
|
2599
|
+
let first_len = 0 // how many patches stay in first (modified in place)
|
|
2600
|
+
|
|
2601
|
+
for (let i = 0; i < patches.length; i++) {
|
|
2602
|
+
let p = patches[i]
|
|
2603
|
+
let delete_length = p.range[1] - p.range[0]
|
|
2604
|
+
let insert_length = p.content_codepoints.length
|
|
2605
|
+
|
|
2606
|
+
let del_start = position
|
|
2607
|
+
let del_end = position + delete_length
|
|
2608
|
+
let ins_start = del_end
|
|
2609
|
+
let ins_end = ins_start + insert_length
|
|
2610
|
+
|
|
2611
|
+
if (split_point >= ins_end) {
|
|
2612
|
+
// Entire patch is before split point - stays in first (unchanged)
|
|
2613
|
+
first_len++
|
|
2614
|
+
// Adjustment: this patch removes delete_length and adds insert_length
|
|
2615
|
+
adjustment += insert_length - delete_length
|
|
2616
|
+
} else if (split_point <= del_start) {
|
|
2617
|
+
// Entire patch is after split point - goes to second (adjusted)
|
|
2618
|
+
second_patches.push({
|
|
2619
|
+
unit: p.unit,
|
|
2620
|
+
range: [p.range[0] + adjustment, p.range[1] + adjustment],
|
|
2621
|
+
content: p.content,
|
|
2622
|
+
content_codepoints: p.content_codepoints
|
|
2623
|
+
})
|
|
2624
|
+
} else if (split_point <= del_end) {
|
|
2625
|
+
// Split point is within the delete portion
|
|
2626
|
+
let del_chars_before = split_point - del_start
|
|
2627
|
+
|
|
2628
|
+
// Save original values before modifying
|
|
2629
|
+
let original_range_end = p.range[1]
|
|
2630
|
+
let original_content = p.content
|
|
2631
|
+
let original_content_codepoints = p.content_codepoints
|
|
2632
|
+
|
|
2633
|
+
// First patches: partial delete, no insert (modify in place)
|
|
2634
|
+
p.range[1] = p.range[0] + del_chars_before
|
|
2635
|
+
p.content = ''
|
|
2636
|
+
p.content_codepoints = []
|
|
2637
|
+
first_len++
|
|
2638
|
+
|
|
2639
|
+
// Adjustment from partial delete
|
|
2640
|
+
adjustment -= del_chars_before
|
|
2641
|
+
|
|
2642
|
+
// Second patches: remaining delete + full insert (adjusted)
|
|
2643
|
+
second_patches.push({
|
|
2644
|
+
unit: p.unit,
|
|
2645
|
+
range: [p.range[1] + adjustment, original_range_end + adjustment],
|
|
2646
|
+
content: original_content,
|
|
2647
|
+
content_codepoints: original_content_codepoints
|
|
2648
|
+
})
|
|
2649
|
+
} else {
|
|
2650
|
+
// Split point is within the insert portion (split_point > del_end && split_point < ins_end)
|
|
2651
|
+
let ins_chars_before = split_point - ins_start
|
|
2652
|
+
let original_content_codepoints = p.content_codepoints
|
|
2653
|
+
|
|
2654
|
+
// First patches: full delete + partial insert (modify in place)
|
|
2655
|
+
p.content_codepoints = p.content_codepoints.slice(0, ins_chars_before)
|
|
2656
|
+
p.content = p.content_codepoints.join('')
|
|
2657
|
+
first_len++
|
|
2658
|
+
|
|
2659
|
+
// After first patches applied, the position for remaining insert is:
|
|
2660
|
+
// p.range[0] (original position)
|
|
2661
|
+
// + adjustment (net change from all prior first_patches)
|
|
2662
|
+
// + ins_chars_before (what this patch's first part inserted)
|
|
2663
|
+
let adjusted_pos = p.range[0] + adjustment + ins_chars_before
|
|
2664
|
+
|
|
2665
|
+
let content_codepoints = original_content_codepoints.slice(ins_chars_before)
|
|
2666
|
+
second_patches.push({
|
|
2667
|
+
unit: p.unit,
|
|
2668
|
+
range: [adjusted_pos, adjusted_pos],
|
|
2669
|
+
content: content_codepoints.join(''),
|
|
2670
|
+
content_codepoints
|
|
2671
|
+
})
|
|
2672
|
+
|
|
2673
|
+
// Update adjustment: full delete removed, partial insert added
|
|
2674
|
+
adjustment += ins_chars_before - delete_length
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
position = ins_end
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// Truncate patches array to only contain first_patches
|
|
2681
|
+
patches.length = first_len
|
|
2682
|
+
|
|
2683
|
+
return second_patches
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2546
2686
|
function createSimpleCache(size) {
|
|
2547
2687
|
const maxSize = size
|
|
2548
2688
|
const cache = new Map()
|
|
@@ -2661,7 +2801,7 @@ function create_braid_text() {
|
|
|
2661
2801
|
}
|
|
2662
2802
|
|
|
2663
2803
|
add_range(low_inclusive, high_inclusive) {
|
|
2664
|
-
if (low_inclusive > high_inclusive)
|
|
2804
|
+
if (low_inclusive > high_inclusive) throw new Error('invalid range')
|
|
2665
2805
|
|
|
2666
2806
|
const startIndex = this._bs(mid => this.ranges[mid][1] >= low_inclusive - 1, this.ranges.length, true)
|
|
2667
2807
|
const endIndex = this._bs(mid => this.ranges[mid][0] <= high_inclusive + 1, -1, false)
|
|
@@ -2676,9 +2816,10 @@ function create_braid_text() {
|
|
|
2676
2816
|
}
|
|
2677
2817
|
}
|
|
2678
2818
|
|
|
2679
|
-
has(x) {
|
|
2680
|
-
|
|
2681
|
-
|
|
2819
|
+
has(x, high) {
|
|
2820
|
+
if (high === undefined) high = x
|
|
2821
|
+
var index = this._bs(mid => this.ranges[mid][0] <= high, -1, false)
|
|
2822
|
+
return index !== -1 && x <= this.ranges[index][1] && this.ranges[index]
|
|
2682
2823
|
}
|
|
2683
2824
|
|
|
2684
2825
|
_bs(condition, defaultR, moveLeft) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braid-text",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.115",
|
|
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
|
+
"scripts": {
|
|
9
|
+
"test": "node test/test.js"
|
|
10
|
+
},
|
|
8
11
|
"files": [
|
|
9
12
|
"index.js",
|
|
10
13
|
"simpleton-client.js",
|