lib0 1.0.0-rc.2 → 1.0.0-rc.21

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 (141) hide show
  1. package/README.md +0 -1315
  2. package/{types → dist}/buffer.d.ts +2 -10
  3. package/{types → dist}/delta/delta.d.ts +582 -144
  4. package/dist/delta/position.d.ts +65 -0
  5. package/dist/delta/rdt/delta.d.ts +57 -0
  6. package/dist/delta/rdt/dom.d.ts +102 -0
  7. package/dist/delta/rdt.d.ts +180 -0
  8. package/dist/delta/transformer/attr.d.ts +64 -0
  9. package/dist/delta/transformer/attribution-to-format.d.ts +52 -0
  10. package/dist/delta/transformer/children.d.ts +69 -0
  11. package/dist/delta/transformer/conform.d.ts +83 -0
  12. package/dist/delta/transformer/core.d.ts +189 -0
  13. package/dist/delta/transformer/full-attributions.d.ts +37 -0
  14. package/dist/delta/transformer/id.d.ts +2 -0
  15. package/dist/delta/transformer/inline.d.ts +89 -0
  16. package/dist/delta/transformer/pipe.d.ts +182 -0
  17. package/dist/delta/transformer/project.d.ts +223 -0
  18. package/dist/delta/transformer/rename-attrs.d.ts +75 -0
  19. package/dist/delta/transformer/rename.d.ts +41 -0
  20. package/dist/delta/transformer/value.d.ts +92 -0
  21. package/dist/delta/transformer.d.ts +14 -0
  22. package/dist/diff/patience.d.ts +25 -0
  23. package/{types → dist}/environment.d.ts +10 -0
  24. package/dist/hash/fnv1a.d.ts +11 -0
  25. package/{types → dist}/schema.d.ts +49 -38
  26. package/{types → dist}/ts.d.ts +4 -0
  27. package/dist/webcrypto.d.ts +3 -0
  28. package/package.json +148 -82
  29. package/src/bin/0serve.js +11 -3
  30. package/src/broadcastchannel.js +6 -1
  31. package/src/buffer.js +35 -25
  32. package/src/decoding.js +1 -1
  33. package/src/delta/delta.js +2531 -564
  34. package/src/delta/position.js +165 -0
  35. package/src/delta/rdt/delta.js +105 -0
  36. package/src/delta/rdt/dom.js +307 -0
  37. package/src/delta/rdt.js +255 -0
  38. package/src/delta/transformer/attr.js +126 -0
  39. package/src/delta/transformer/attribution-to-format.js +335 -0
  40. package/src/delta/transformer/children.js +219 -0
  41. package/src/delta/transformer/conform.js +401 -0
  42. package/src/delta/transformer/core.js +323 -0
  43. package/src/delta/transformer/full-attributions.js +282 -0
  44. package/src/delta/transformer/id.js +13 -0
  45. package/src/delta/transformer/inline.js +818 -0
  46. package/src/delta/transformer/pipe.js +272 -0
  47. package/src/delta/transformer/project.js +526 -0
  48. package/src/delta/transformer/rename-attrs.js +152 -0
  49. package/src/delta/transformer/rename.js +84 -0
  50. package/src/delta/transformer/value.js +214 -0
  51. package/src/delta/transformer.js +38 -0
  52. package/src/diff/patience.js +22 -11
  53. package/src/dom.js +2 -2
  54. package/src/encoding.js +1 -1
  55. package/src/environment.js +15 -0
  56. package/src/hash/fnv1a.js +82 -0
  57. package/src/number.js +1 -3
  58. package/src/performance.node.js +3 -3
  59. package/src/schema.js +231 -177
  60. package/src/string.js +3 -5
  61. package/src/testing.js +1 -1
  62. package/src/trait/equality.js +1 -1
  63. package/src/trait/fingerprint.js +1 -1
  64. package/src/ts.js +11 -0
  65. package/src/bin/gendocs.js +0 -117
  66. package/src/component.js +0 -414
  67. package/src/delta/binding.js +0 -372
  68. package/src/tree.js +0 -546
  69. package/types/bin/gendocs.d.ts +0 -3
  70. package/types/component.d.ts +0 -86
  71. package/types/delta/binding.d.ts +0 -106
  72. package/types/diff/patience.d.ts +0 -16
  73. package/types/tree.d.ts +0 -96
  74. package/types/webcrypto.d.ts +0 -3
  75. /package/{types → dist}/array.d.ts +0 -0
  76. /package/{types → dist}/bin/0ecdsa-generate-keypair.d.ts +0 -0
  77. /package/{types → dist}/bin/0serve.d.ts +0 -0
  78. /package/{types → dist}/bin/gentesthtml.d.ts +0 -0
  79. /package/{types → dist}/binary.d.ts +0 -0
  80. /package/{types → dist}/broadcastchannel.d.ts +0 -0
  81. /package/{types → dist}/cache.d.ts +0 -0
  82. /package/{types → dist}/conditions.d.ts +0 -0
  83. /package/{types → dist}/crypto/aes-gcm.d.ts +0 -0
  84. /package/{types → dist}/crypto/common.d.ts +0 -0
  85. /package/{types → dist}/crypto/ecdsa.d.ts +0 -0
  86. /package/{types → dist}/crypto/jwt.d.ts +0 -0
  87. /package/{types → dist}/crypto/rsa-oaep.d.ts +0 -0
  88. /package/{types → dist}/decoding.d.ts +0 -0
  89. /package/{types → dist}/diff.d.ts +0 -0
  90. /package/{types → dist}/dom.d.ts +0 -0
  91. /package/{types → dist}/encoding.d.ts +0 -0
  92. /package/{types → dist}/error.d.ts +0 -0
  93. /package/{types → dist}/eventloop.d.ts +0 -0
  94. /package/{types → dist}/function.d.ts +0 -0
  95. /package/{types → dist}/hash/rabin-gf2-polynomial.d.ts +0 -0
  96. /package/{types → dist}/hash/rabin-uncached.d.ts +0 -0
  97. /package/{types → dist}/hash/rabin.d.ts +0 -0
  98. /package/{types → dist}/hash/sha256.d.ts +0 -0
  99. /package/{types → dist}/hash/sha256.node.d.ts +0 -0
  100. /package/{types → dist}/indexeddb.d.ts +0 -0
  101. /package/{types → dist}/indexeddbV2.d.ts +0 -0
  102. /package/{types → dist}/iterator.d.ts +0 -0
  103. /package/{types → dist}/json.d.ts +0 -0
  104. /package/{types → dist}/list.d.ts +0 -0
  105. /package/{types → dist}/logging.common.d.ts +0 -0
  106. /package/{types → dist}/logging.d.ts +0 -0
  107. /package/{types → dist}/logging.node.d.ts +0 -0
  108. /package/{types → dist}/map.d.ts +0 -0
  109. /package/{types → dist}/math.d.ts +0 -0
  110. /package/{types → dist}/metric.d.ts +0 -0
  111. /package/{types → dist}/mutex.d.ts +0 -0
  112. /package/{types → dist}/number.d.ts +0 -0
  113. /package/{types → dist}/object.d.ts +0 -0
  114. /package/{types → dist}/observable.d.ts +0 -0
  115. /package/{types → dist}/pair.d.ts +0 -0
  116. /package/{types → dist}/performance.d.ts +0 -0
  117. /package/{types → dist}/performance.node.d.ts +0 -0
  118. /package/{types → dist}/pledge.d.ts +0 -0
  119. /package/{types → dist}/prng/Mt19937.d.ts +0 -0
  120. /package/{types → dist}/prng/Xoroshiro128plus.d.ts +0 -0
  121. /package/{types → dist}/prng/Xorshift32.d.ts +0 -0
  122. /package/{types → dist}/prng.d.ts +0 -0
  123. /package/{types → dist}/promise.d.ts +0 -0
  124. /package/{types → dist}/queue.d.ts +0 -0
  125. /package/{types → dist}/random.d.ts +0 -0
  126. /package/{types → dist}/set.d.ts +0 -0
  127. /package/{types → dist}/sort.d.ts +0 -0
  128. /package/{types → dist}/statistics.d.ts +0 -0
  129. /package/{types → dist}/storage.d.ts +0 -0
  130. /package/{types → dist}/string.d.ts +0 -0
  131. /package/{types → dist}/symbol.d.ts +0 -0
  132. /package/{types → dist}/testing.d.ts +0 -0
  133. /package/{types → dist}/time.d.ts +0 -0
  134. /package/{types → dist}/trait/equality.d.ts +0 -0
  135. /package/{types → dist}/trait/fingerprint.d.ts +0 -0
  136. /package/{types → dist}/trait/traits.d.ts +0 -0
  137. /package/{types → dist}/url.d.ts +0 -0
  138. /package/{types → dist}/webcrypto.deno.d.ts +0 -0
  139. /package/{types → dist}/webcrypto.node.d.ts +0 -0
  140. /package/{types → dist}/webcrypto.react-native.d.ts +0 -0
  141. /package/{types → dist}/websocket.d.ts +0 -0
@@ -0,0 +1,84 @@
1
+ import * as delta from '../delta.js'
2
+ import { Transformer, Template, createTransformResult, withName } from './core.js'
3
+
4
+ /**
5
+ * Relabels a node: side B carries the configured `name`, side A keeps the source node's original
6
+ * name. Otherwise identity. Useful to mark a `children`-mapped collection as `lib0:inline` (so a
7
+ * downstream {@link import('./inline.js').inline}`(['lib0:inline'])` splices it into its parent) or
8
+ * simply to rename the container (e.g. a data collection projected as a `<ul>`).
9
+ *
10
+ * @extends {Transformer<any,any>}
11
+ */
12
+ export class RenameTransformer extends Transformer {
13
+ /**
14
+ * @param {import('../../schema.js').Schema<delta.Delta<any>>} $in
15
+ * @param {import('../../schema.js').Schema<delta.Delta<any>>} $out
16
+ * @param {string} name
17
+ */
18
+ constructor ($in, $out, name) {
19
+ super($in, $out)
20
+ this.target = name
21
+ /** @type {string|undefined} */
22
+ this.srcName = undefined
23
+ }
24
+
25
+ /**
26
+ * @param {delta.DeltaBuilderAny} d
27
+ * @return {import('./core.js').TransformResultAny}
28
+ */
29
+ applyA (d) {
30
+ if (d.name != null) this.srcName = d.name
31
+ const out = delta.clone(d)
32
+ out.name = /** @type {any} */ (this.target)
33
+ return createTransformResult(null, out)
34
+ }
35
+
36
+ /**
37
+ * @param {delta.DeltaBuilderAny} d
38
+ * @return {import('./core.js').TransformResultAny}
39
+ */
40
+ applyB (d) {
41
+ const out = delta.clone(d)
42
+ out.name = /** @type {any} */ (this.srcName)
43
+ return createTransformResult(out, null)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Template for {@link RenameTransformer}.
49
+ *
50
+ * @template {string} Name
51
+ * @template {delta.DeltaConf} [IN=any]
52
+ * @extends {Template<IN, delta.DeltaConfOverwrite<IN, { name: Name }>>}
53
+ */
54
+ export class Rename extends Template {
55
+ /**
56
+ * @param {import('../../schema.js').Schema<delta.Delta<IN>>} $d
57
+ * @param {Name} name
58
+ */
59
+ constructor ($d, name) {
60
+ super($d, /** @type {any} */ (withName($d, name)))
61
+ this.target = name
62
+ }
63
+
64
+ get name () { return 'lib0:rename:' + this.target }
65
+
66
+ /**
67
+ * @return {Transformer<IN, delta.DeltaConfOverwrite<IN, { name: Name }>>}
68
+ */
69
+ init () {
70
+ return /** @type {any} */ (new RenameTransformer(this.$in, this.$out, this.target))
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Relabel a node to `name` (side B) and back (side A). Returns a reusable {@link Rename} template
76
+ * (a `project` hole, or `.init()` for a standalone transformer).
77
+ *
78
+ * @template {delta.DeltaConf} IN
79
+ * @template {string} Name
80
+ * @param {import('../../schema.js').Schema<delta.Delta<IN>>} $d
81
+ * @param {Name} name
82
+ * @return {Rename<Name, IN>}
83
+ */
84
+ export const rename = ($d, name) => new Rename($d, name)
@@ -0,0 +1,214 @@
1
+ import * as delta from '../delta.js'
2
+ import * as math from '../../math.js'
3
+ import { Transformer, Template, createTransformResult } from './core.js'
4
+
5
+ /**
6
+ * A `lib0:value` carrier node holds a single scalar in its `value` attribute (the convention
7
+ * established by the {@link import('./attr.js').attr} transformer). At a *child* position such a
8
+ * node is resolved to a one-position **embed** of that scalar; at an *attr* position carriers are
9
+ * resolved by {@link import('./project.js').project} itself (it owns the position knowledge).
10
+ *
11
+ * @param {any} el
12
+ * @return {boolean}
13
+ */
14
+ const isValueNode = el => delta.$deltaAny.check(el) && el.name === 'lib0:value'
15
+
16
+ /**
17
+ * The scalar carried by a `lib0:value` delta (its `value` attribute op's value), or `undefined`.
18
+ *
19
+ * @param {delta.DeltaAny} el
20
+ */
21
+ const valueOf = el => /** @type {any} */ (el.attrs).value?.value
22
+
23
+ /**
24
+ * Stateful transformer that resolves `lib0:value` carrier *children* of a node: a `lib0:value` node
25
+ * (1 position) becomes an embed of its scalar value (1 position), and back. Count-preserving, so a
26
+ * single sparse positional map (`insert([1])` at carrier positions, coalesced `retain(n)` over
27
+ * gaps) indexes both directions - the same `childTs` technique as {@link import('./children.js')}.
28
+ *
29
+ * A standalone, *composable* one-level resolver (the `lib0:value` counterpart of
30
+ * {@link import('./inline.js').inline}`(['lib0:inline'])`): place it after any transformer that emits
31
+ * `lib0:value` carriers, or compose it with {@link import('./children.js').children} to descend into
32
+ * deeper levels. `project` lifts the carriers in its *own* structure, so `unwrapValue` is only needed
33
+ * for carriers emitted by a non-`project` transformer.
34
+ *
35
+ * Editing a resolved scalar from side B (the view) is not round-tripped in v1: a value *update*
36
+ * driven from side A (a `modify` on the carrier) maps to `delete(1).insert([newValue])`, but a side
37
+ * B `delete`+`insert` of the embed is treated structurally.
38
+ *
39
+ * @extends {Transformer<any,any>}
40
+ */
41
+ export class UnwrapValueTransformer extends Transformer {
42
+ /**
43
+ * @param {import('../../schema.js').Schema<delta.Delta<any>>} $in
44
+ * @param {import('../../schema.js').Schema<delta.Delta<any>>} $out
45
+ */
46
+ constructor ($in, $out) {
47
+ super($in, $out)
48
+ /**
49
+ * Sparse positional map of carriers: `insert([1])` at each `lib0:value` child, `retain(n)` over
50
+ * gaps (text and pass-through nodes).
51
+ *
52
+ * @type {delta.DeltaBuilderAny}
53
+ */
54
+ this.map = delta.create()
55
+ }
56
+
57
+ /**
58
+ * @param {delta.DeltaBuilderAny} d the change to map
59
+ * @param {boolean} fwd `true` maps side A (carriers present) -> B (resolved), `false` maps back
60
+ * @return {import('./core.js').TransformResultAny}
61
+ */
62
+ transform (d, fwd) {
63
+ // `cloneShallow` carries the node's name, attribute ops, and own (root) marks; only child nodes are
64
+ // (re)built below, and nested children's marks ride on those rebuilt children. It is the shared
65
+ // mark-carrying primitive, so this node's marks cannot be silently dropped. `any` keeps `out` within
66
+ // the shared TS instantiation budget (see {@link import('./inline.js')}).
67
+ const out = /** @type {any} */ (delta.cloneShallow(d))
68
+ const newMap = delta.create()
69
+ let srcNode = this.map.children.start
70
+ let srcOff = 0
71
+ /**
72
+ * Copy `n` positions from the old map to `newMap`, advancing the cursor.
73
+ *
74
+ * @param {number} n
75
+ */
76
+ const carry = n => {
77
+ let rem = n
78
+ while (rem > 0 && srcNode != null) {
79
+ const take = math.min(srcNode.length - srcOff, rem)
80
+ if (delta.$insertOp.check(srcNode)) {
81
+ newMap.insert(srcNode.insert.slice(srcOff, srcOff + take))
82
+ } else {
83
+ newMap.retain(take)
84
+ }
85
+ srcOff += take
86
+ rem -= take
87
+ if (srcOff >= srcNode.length) { srcNode = srcNode.next; srcOff = 0 }
88
+ }
89
+ }
90
+ /**
91
+ * Advance the cursor `n` positions without copying.
92
+ *
93
+ * @param {number} n
94
+ */
95
+ const drop = n => {
96
+ let rem = n
97
+ while (rem > 0 && srcNode != null) {
98
+ const take = math.min(srcNode.length - srcOff, rem)
99
+ srcOff += take
100
+ rem -= take
101
+ if (srcOff >= srcNode.length) { srcNode = srcNode.next; srcOff = 0 }
102
+ }
103
+ }
104
+ // whether the cursor currently sits on a carrier position
105
+ const peekCarrier = () => srcNode != null && delta.$insertOp.check(srcNode)
106
+ for (const op of d.children) {
107
+ if (delta.$retainOp.check(op)) {
108
+ out.retain(op.retain, op.format, op.attribution)
109
+ carry(op.retain)
110
+ } else if (delta.$textOp.check(op)) {
111
+ out.insert(op.insert, op.format, op.attribution)
112
+ newMap.retain(op.insert.length) // text is a gap
113
+ } else if (delta.$insertOp.check(op)) {
114
+ for (const el of op.insert) {
115
+ if (fwd && isValueNode(el)) {
116
+ out.insert([valueOf(el)], op.format, op.attribution)
117
+ newMap.insert([1]) // mark carrier
118
+ } else {
119
+ out.insert([el], op.format, op.attribution)
120
+ newMap.retain(1) // pass-through / literal
121
+ }
122
+ }
123
+ } else if (delta.$deleteOp.check(op)) {
124
+ out.delete(op.delete)
125
+ drop(op.delete)
126
+ } else if (delta.$modifyOp.check(op)) {
127
+ if (fwd && peekCarrier()) {
128
+ // a value update arrives as a `modify` on the carrier node setting its `value` attr
129
+ const newV = /** @type {any} */ (op.value.attrs).value?.value
130
+ if (newV !== undefined) {
131
+ out.delete(1)
132
+ out.insert([newV], op.format, op.attribution)
133
+ } else {
134
+ out.retain(1, op.format, op.attribution)
135
+ }
136
+ } else {
137
+ out.modify(delta.clone(op.value), op.format, op.attribution)
138
+ }
139
+ carry(1)
140
+ }
141
+ }
142
+ carry(Infinity) // trailing untouched positions
143
+ out.done(false)
144
+ this.map = newMap
145
+ return fwd ? createTransformResult(null, out) : createTransformResult(out, null)
146
+ }
147
+
148
+ /**
149
+ * @param {delta.DeltaBuilderAny} da
150
+ */
151
+ applyA (da) {
152
+ return this.transform(da, true)
153
+ }
154
+
155
+ /**
156
+ * @param {delta.DeltaBuilderAny} db
157
+ */
158
+ applyB (db) {
159
+ return this.transform(db, false)
160
+ }
161
+ }
162
+
163
+ /**
164
+ * A `lib0:value` carrier child lifts to its scalar `value` type; any other child passes through.
165
+ * Naked-param conditional, so it distributes over a children union.
166
+ *
167
+ * @template C
168
+ * @typedef {C extends delta.Delta<{ name: 'lib0:value', attrs: { value: infer V extends import('../../trait/fingerprint.js').Fingerprintable } }> ? V : C} UnwrapValueChild
169
+ */
170
+
171
+ /**
172
+ * `unwrapValue`'s output conf: `IN` with every `lib0:value` carrier child replaced by its lifted
173
+ * scalar (see {@link UnwrapValueChild}); name/attrs/text unchanged.
174
+ *
175
+ * @template {delta.DeltaConf} IN
176
+ * @typedef {import('./core.js').ResolveOut<delta.DeltaConfOverwrite<IN, { children: UnwrapValueChild<delta.DeltaConfGetChildren<IN>> }>>} UnwrapValueOut
177
+ */
178
+
179
+ /**
180
+ * Template for {@link UnwrapValueTransformer}.
181
+ *
182
+ * @template {delta.DeltaConf} [IN=any]
183
+ * @extends {Template<IN, UnwrapValueOut<IN>>}
184
+ */
185
+ export class UnwrapValue extends Template {
186
+ /**
187
+ * @param {import('../../schema.js').Schema<delta.Delta<IN>>} $d
188
+ */
189
+ constructor ($d) {
190
+ super($d, /** @type {any} */ (delta.$deltaAny))
191
+ }
192
+
193
+ get name () { return 'lib0:unwrapValue' }
194
+
195
+ /**
196
+ * @return {Transformer<IN, UnwrapValueOut<IN>>}
197
+ */
198
+ init () {
199
+ return /** @type {any} */ (new UnwrapValueTransformer(this.$in, this.$out))
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Resolve `lib0:value` carrier *children* to embeds of their scalar value (and back). One level
205
+ * only; compose with {@link import('./children.js').children} to descend. A flat, composable resolver
206
+ * for carriers emitted *outside* a `project` (which lifts its own); symmetric with
207
+ * {@link import('./inline.js').inline}`(['lib0:inline'])`. Returns a reusable {@link UnwrapValue}
208
+ * template (`.init()` for a standalone transformer).
209
+ *
210
+ * @template {delta.DeltaConf} IN
211
+ * @param {import('../../schema.js').Schema<delta.Delta<IN>>} $d
212
+ * @return {UnwrapValue<IN>}
213
+ */
214
+ export const unwrapValue = $d => new UnwrapValue($d)
@@ -0,0 +1,38 @@
1
+ /**
2
+ * # Delta transformers
3
+ *
4
+ * A {@link Template} is a composable transformer factory; its `init($schema)` produces a stateful
5
+ * {@link Transformer} that maps changes ("deltas") of one delta shape (side A) onto another (side B)
6
+ * and back. Transformers compose with {@link pipe}.
7
+ *
8
+ * Each concrete transformer lives in `./transformer/` and is re-exported here so the whole set is
9
+ * available from a single module (`lib0/delta/transformer`):
10
+ * - {@link id} — identity (both sides stay equal)
11
+ * - {@link renameAttrs} — rename node attributes
12
+ * - {@link conform} — drop everything the target schema does not recognize
13
+ * - {@link pipe} — chain templates
14
+ * - {@link attr} — project a single attribute into a `lib0:value` node
15
+ * - {@link inline} — inline child nodes whose name is in a configured set
16
+ * - {@link children} — descend into child nodes and apply a per-child sub-transformer
17
+ * - {@link project} — project the data onto a fixed structure (spec) with embedded "holes"
18
+ * - {@link unwrapValue} — composable resolver for `lib0:value` carrier children (scalar lift)
19
+ * - {@link rename} — rename a node's name (e.g. mark a `children`-map as `lib0:inline`)
20
+ * - {@link attributionToFormat} — render the `attribution` dimension as `y-attributed-*` formats
21
+ * - {@link fullAttributions} — re-emit the complete accumulated attribution on every attribution change
22
+ *
23
+ * @module delta/transformer
24
+ */
25
+
26
+ export * from './transformer/core.js'
27
+ export * from './transformer/id.js'
28
+ export * from './transformer/rename.js'
29
+ export * from './transformer/conform.js'
30
+ export * from './transformer/pipe.js'
31
+ export * from './transformer/attr.js'
32
+ export * from './transformer/inline.js'
33
+ export * from './transformer/children.js'
34
+ export * from './transformer/project.js'
35
+ export * from './transformer/value.js'
36
+ export * from './transformer/rename-attrs.js'
37
+ export * from './transformer/attribution-to-format.js'
38
+ export * from './transformer/full-attributions.js'
@@ -16,7 +16,7 @@ import * as array from '../array.js'
16
16
  *
17
17
  * @param {Array<string>} as
18
18
  * @param {Array<string>} bs
19
- * @return {Array<{ index: number, remove: Array<string>, insert: Array<string>}>} changeset @todo should use delta instead
19
+ * @return {Array<{ index: number, remove: Array<string>, insert: Array<string>}>} changeset
20
20
  */
21
21
  export const diff = (as, bs) => {
22
22
  const {
@@ -30,14 +30,16 @@ export const diff = (as, bs) => {
30
30
  /**
31
31
  * @param {string} a
32
32
  * @param {string} b
33
- * @param {RegExp|string} _regexp
33
+ * @param {RegExp|string} regexp
34
34
  */
35
- export const diffSplitBy = (a, b, _regexp) => {
36
- const isStringSeparator = typeof _regexp === 'string'
37
- const separator = isStringSeparator ? _regexp : ''
38
- const regexp = isStringSeparator ? new RegExp(_regexp, 'g') : _regexp
39
- const as = splitByRegexp(a, regexp, !isStringSeparator)
40
- const bs = splitByRegexp(b, regexp, !isStringSeparator)
35
+ export const diffSplitBy = (a, b, regexp) => {
36
+ const isStringSeparator = typeof regexp === 'string'
37
+ const separator = /** @type {string} */ (isStringSeparator ? regexp : '')
38
+ if (isStringSeparator) {
39
+ regexp = new RegExp(regexp, 'g')
40
+ }
41
+ const as = splitByRegexp(a, /** @type {RegExp} */ (regexp), !isStringSeparator)
42
+ const bs = splitByRegexp(b, /** @type {RegExp} */ (regexp), !isStringSeparator)
41
43
  const changes = diff(as, bs)
42
44
  let prevSplitIndex = 0
43
45
  let prevStringIndex = 0
@@ -53,6 +55,15 @@ export const diffSplitBy = (a, b, _regexp) => {
53
55
  })
54
56
  }
55
57
 
58
+ /**
59
+ * Idea: match all "non-word" characters and keep them separate (spaces, comma, exclamation marks, ..).
60
+ * Anything that can be classivied as a word (emoji, char, number) will be bundled together. All other tokens are kept
61
+ * individually. "hello, world!!!!" => ['hello', ',', ' ', 'world', '!', '!']
62
+ *
63
+ * @experimental
64
+ */
65
+ export const smartSplitRegex = /[^\p{L}\p{N}\p{Extended_Pictographic}\p{Emoji_Modifier}\u200D\uFE0F]/gu // eslint-disable-line no-misleading-character-class
66
+
56
67
  /**
57
68
  * Sensible default for diffing strings using patience (it's fast though).
58
69
  *
@@ -64,12 +75,12 @@ export const diffSplitBy = (a, b, _regexp) => {
64
75
  */
65
76
  export const diffAuto = (a, b) =>
66
77
  diffSplitBy(a, b, '\n').map(d =>
67
- diffSplitBy(d.remove, d.insert, /\. |[a-zA-Z0-9]+|[. ()[\],;{}]/g).map(dd => ({
78
+ diffSplitBy(d.remove, d.insert, smartSplitRegex).map(dd => ({ // old regex: /\. |[a-zA-Z0-9]+|[. ()[\],;{}]/g
68
79
  insert: dd.insert,
69
80
  remove: dd.remove,
70
81
  index: dd.index + d.index
71
82
  }))
72
- ).flat()
83
+ ).flat(1)
73
84
 
74
85
  /**
75
86
  * @param {Array<string>} as
@@ -97,7 +108,7 @@ const removeCommonPrefixAndSuffix = (as, bs) => {
97
108
  * @param {RegExp} regexp
98
109
  * @param {boolean} includeSeparator
99
110
  */
100
- const splitByRegexp = (str, regexp, includeSeparator) => {
111
+ export const splitByRegexp = (str, regexp, includeSeparator) => {
101
112
  const matches = [...str.matchAll(regexp)]
102
113
  let prevIndex = 0
103
114
  /**
package/src/dom.js CHANGED
@@ -14,7 +14,7 @@ import * as $ from './schema.js'
14
14
  /**
15
15
  * @type {Document}
16
16
  */
17
- export const doc = /** @type {Document} */ (typeof document !== 'undefined' ? document : {})
17
+ export const doc = /** @type {Document} */ (/* @__PURE__ */ (() => typeof document !== 'undefined' ? document : {})())
18
18
 
19
19
  /**
20
20
  * @param {string} name
@@ -38,7 +38,7 @@ export const $fragment = $.$custom(el => el.nodeType === DOCUMENT_FRAGMENT_NODE)
38
38
  */
39
39
  export const createTextNode = text => doc.createTextNode(text)
40
40
 
41
- export const domParser = /** @type {DOMParser} */ (typeof DOMParser !== 'undefined' ? new DOMParser() : null)
41
+ export const domParser = /** @type {DOMParser} */ (/* @__PURE__ */ (() => typeof DOMParser !== 'undefined' ? new DOMParser() : null)())
42
42
 
43
43
  /**
44
44
  * @param {HTMLElement} el
package/src/encoding.js CHANGED
@@ -341,7 +341,7 @@ export const _writeVarStringPolyfill = (encoder, str) => {
341
341
  * @param {String} str The string that is to be encoded.
342
342
  */
343
343
  /* c8 ignore next */
344
- export const writeVarString = (string.utf8TextEncoder && /** @type {any} */ (string.utf8TextEncoder).encodeInto) ? _writeVarStringNative : _writeVarStringPolyfill
344
+ export const writeVarString = /* @__PURE__ */(() => (string.utf8TextEncoder && /** @type {any} */ (string.utf8TextEncoder).encodeInto) ? _writeVarStringNative : _writeVarStringPolyfill)()
345
345
 
346
346
  /**
347
347
  * Write a string terminated by a special byte sequence. This is not very performant and is
@@ -13,11 +13,26 @@ import * as f from './function.js'
13
13
  /* c8 ignore next */
14
14
  export const isNode = /* @__PURE__ */(() => typeof process !== 'undefined' && process.release && /node|io\.js/.test(process.release.name) && Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]')()
15
15
 
16
+ /**
17
+ * True iff this script is running in deno
18
+ * @type {boolean}
19
+ */
20
+ /* c8 ignore next 2 */
21
+ // @ts-ignore
22
+ export const isDeno = /* @__PURE__ */(() => typeof Deno !== 'undefined')()
23
+
16
24
  /* c8 ignore next */
17
25
  export const isBrowser = /* @__PURE__ */(() => typeof window !== 'undefined' && typeof document !== 'undefined' && !isNode)()
18
26
  /* c8 ignore next */
19
27
  export const isMac = /* @__PURE__ */(() => typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false)()
20
28
 
29
+ /**
30
+ * True iff a DOM is available — either a real browser, or Node with a jsdom shim installed (see
31
+ * `src/test-setup.js`). Gate DOM-dependent tests on this so they run in the browser AND under jsdom.
32
+ */
33
+ /* c8 ignore next */
34
+ export const hasDom = /* @__PURE__ */(() => typeof document !== 'undefined' && typeof MutationObserver !== 'undefined')()
35
+
21
36
  /**
22
37
  * @type {Map<string,string>}
23
38
  */
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @module fnv1a
3
+ * FNV-1a (32bit) - a fast, non-cryptographic hash function.
4
+ * Spec: http://www.isthe.com/chongo/tech/comp/fnv/
5
+ */
6
+
7
+ import * as math from '../math.js'
8
+
9
+ /**
10
+ * FNV-1a offset basis (32bit).
11
+ */
12
+ export const offsetBasis = 0x811c9dc5
13
+
14
+ /**
15
+ * FNV-1a prime (32bit).
16
+ */
17
+ export const prime = 0x01000193
18
+
19
+ /**
20
+ * Compute the FNV-1a 32bit hash of a byte sequence.
21
+ *
22
+ * Pass the result of a previous call as `hash` to digest a message in chunks:
23
+ * `digest(concat(a, b)) === digest(b, digest(a))`.
24
+ *
25
+ * @param {Uint8Array} data
26
+ * @param {number} hash - continue hashing from a previous result (defaults to the offset basis)
27
+ * @return {number} unsigned 32bit hash
28
+ */
29
+ /* @__NO_SIDE_EFFECTS__ */
30
+ export const digest = (data, hash = offsetBasis) => {
31
+ for (let i = 0; i < data.length; i++) {
32
+ hash = math.imul(hash ^ data[i], prime)
33
+ }
34
+ return hash >>> 0
35
+ }
36
+
37
+ /**
38
+ * Compute the FNV-1a 32bit hash of a string - without allocating the utf8 encoding.
39
+ *
40
+ * Equivalent to `digest(string.encodeUtf8(str))`: the string is hashed as utf8 bytes; lone
41
+ * surrogates are hashed as the replacement character (like TextEncoder encodes them).
42
+ *
43
+ * Prefer this over `digest(string.encodeUtf8(str))` when hashing strings - it skips the
44
+ * TextEncoder call and the Uint8Array allocation, making it ~5x faster for small strings
45
+ * (<=20 chars, e.g. object keys) and ~2x faster for larger ones.
46
+ *
47
+ * @param {string} str
48
+ * @param {number} hash - continue hashing from a previous result (defaults to the offset basis)
49
+ * @return {number} unsigned 32bit hash
50
+ */
51
+ /* @__NO_SIDE_EFFECTS__ */
52
+ export const digestString = (str, hash = offsetBasis) => {
53
+ for (let i = 0; i < str.length; i++) {
54
+ let c = str.charCodeAt(i)
55
+ if (c < 0x80) {
56
+ hash = math.imul(hash ^ c, prime)
57
+ } else {
58
+ if (c >= 0xd800 && c < 0xe000) {
59
+ const lo = str.charCodeAt(i + 1) // NaN when out of bounds ⇒ NaN & x === 0
60
+ if (c < 0xdc00 && (lo & 0xfc00) === 0xdc00) {
61
+ c = 0x10000 + ((c & 0x3ff) << 10) + (lo & 0x3ff)
62
+ i++
63
+ } else {
64
+ c = 0xfffd // lone surrogate ⇒ replacement character
65
+ }
66
+ }
67
+ if (c < 0x800) {
68
+ hash = math.imul(hash ^ (0xc0 | (c >> 6)), prime)
69
+ } else {
70
+ if (c < 0x10000) {
71
+ hash = math.imul(hash ^ (0xe0 | (c >> 12)), prime)
72
+ } else {
73
+ hash = math.imul(hash ^ (0xf0 | (c >> 18)), prime)
74
+ hash = math.imul(hash ^ (0x80 | ((c >> 12) & 0x3f)), prime)
75
+ }
76
+ hash = math.imul(hash ^ (0x80 | ((c >> 6) & 0x3f)), prime)
77
+ }
78
+ hash = math.imul(hash ^ (0x80 | (c & 0x3f)), prime)
79
+ }
80
+ }
81
+ return hash >>> 0
82
+ }
package/src/number.js CHANGED
@@ -4,7 +4,6 @@
4
4
  * @module number
5
5
  */
6
6
 
7
- import * as math from './math.js'
8
7
  import * as binary from './binary.js'
9
8
 
10
9
  export const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER
@@ -14,8 +13,7 @@ export const LOWEST_INT32 = 1 << 31
14
13
  export const HIGHEST_INT32 = binary.BITS31
15
14
  export const HIGHEST_UINT32 = binary.BITS32
16
15
 
17
- /* c8 ignore next */
18
- export const isInteger = Number.isInteger || (num => typeof num === 'number' && isFinite(num) && math.floor(num) === num)
16
+ export const isInteger = Number.isInteger
19
17
  export const isNaN = Number.isNaN
20
18
  export const parseInt = Number.parseInt
21
19
 
@@ -6,16 +6,16 @@ import * as time from './time.js'
6
6
  * @type {typeof performance.measure}
7
7
  */
8
8
  /* c8 ignore next */
9
- export const measure = performance.measure ? performance.measure.bind(performance) : /** @type {any} */ (nop)
9
+ export const measure = /* @__PURE__ */(() => performance.measure ? performance.measure.bind(performance) : /** @type {any} */ (nop))()
10
10
 
11
11
  /**
12
12
  * @type {typeof performance.now}
13
13
  */
14
14
  /* c8 ignore next */
15
- export const now = performance.now ? performance.now.bind(performance) : time.getUnixTime
15
+ export const now = /* @__PURE__ */(() => performance.now ? performance.now.bind(performance) : time.getUnixTime)()
16
16
 
17
17
  /**
18
18
  * @type {typeof performance.mark}
19
19
  */
20
20
  /* c8 ignore next */
21
- export const mark = performance.mark ? performance.mark.bind(performance) : /** @type {any} */ (nop)
21
+ export const mark = /* @__PURE__ */(() => performance.mark ? performance.mark.bind(performance) : /** @type {any} */ (nop))()