lib0 1.0.0-rc.26 → 1.0.0-rc.28
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/dist/delta/delta.d.ts +23 -6
- package/dist/delta/position.d.ts +2 -0
- package/dist/random.d.ts +5 -1
- package/package.json +1 -1
- package/src/delta/delta.js +33 -12
- package/src/delta/position.js +77 -5
- package/src/function.js +9 -5
- package/src/random.js +16 -2
package/dist/delta/delta.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export const $attribution: s.Schema<Attribution>;
|
|
|
39
39
|
* @typedef {{ [key: string]: any }} Formats
|
|
40
40
|
*/
|
|
41
41
|
/**
|
|
42
|
-
* @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object }} MarkJSON
|
|
42
|
+
* @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object, transient?: boolean }} MarkJSON
|
|
43
43
|
*/
|
|
44
44
|
/**
|
|
45
45
|
* @typedef {{
|
|
@@ -423,7 +423,7 @@ export class SetAttrOp<V extends unknown = any, K extends string | number = any>
|
|
|
423
423
|
get fingerprint(): string;
|
|
424
424
|
toJSON(): {
|
|
425
425
|
type: "insert";
|
|
426
|
-
value:
|
|
426
|
+
value: DeltaJSON | V;
|
|
427
427
|
} & ({
|
|
428
428
|
attribution: Attribution;
|
|
429
429
|
} | {
|
|
@@ -549,13 +549,13 @@ export const $anyOp: s.Schema<DeleteOp<any> | TextOp | InsertOp<any> | ModifyOp<
|
|
|
549
549
|
export const $setAttrOp: s.Schema<SetAttrOp<any>>;
|
|
550
550
|
export const $modifyAttrOp: s.Schema<ModifyAttrOp<any>>;
|
|
551
551
|
export const $deleteAttrOp: s.Schema<DeleteAttrOp<any>>;
|
|
552
|
-
export const $anyAttrOp: s.Schema<
|
|
552
|
+
export const $anyAttrOp: s.Schema<SetAttrOp<any, any> | DeleteAttrOp<any, string | number> | ModifyAttrOp<any, string>>;
|
|
553
553
|
export function $setAttrOpWith<Content extends fingerprintTrait.Fingerprintable>($content: s.Schema<Content>): s.Schema<SetAttrOp<Content>>;
|
|
554
554
|
export function $insertOpWith<Content extends fingerprintTrait.Fingerprintable>($content: s.Schema<Content>): s.Schema<InsertOp<Content>>;
|
|
555
555
|
export function $modifyOpWith<Modify extends DeltaAny>($content: s.Schema<Modify>): s.Schema<ModifyOp<Modify>>;
|
|
556
556
|
export function $modifyAttrOpWith<Modify extends DeltaAny>($content: s.Schema<Modify>): s.Schema<ModifyAttrOp<Modify>>;
|
|
557
557
|
export const $mark: s.Schema<Mark>;
|
|
558
|
-
export function createMark(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null): Mark;
|
|
558
|
+
export function createMark(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null, transient?: boolean): Mark;
|
|
559
559
|
/**
|
|
560
560
|
* @template {DeltaConf} [Conf={}]
|
|
561
561
|
* @extends {DeltaData<
|
|
@@ -745,9 +745,11 @@ export class DeltaBuilder<Conf extends DeltaConf = {}, FixedConf extends boolean
|
|
|
745
745
|
*
|
|
746
746
|
* @param {import('./position.js').Pos} pos
|
|
747
747
|
* @param {string} [id]
|
|
748
|
+
* @param {boolean} [transient] flag the mark as never-to-be-stored (a one-shot mapping probe, see
|
|
749
|
+
* {@link Mark#transient} and {@link import('./position.js').mapPositionsA})
|
|
748
750
|
* @return {this}
|
|
749
751
|
*/
|
|
750
|
-
addMark(pos: import("./position.js").Pos, id?: string): this;
|
|
752
|
+
addMark(pos: import("./position.js").Pos, id?: string, transient?: boolean): this;
|
|
751
753
|
/**
|
|
752
754
|
* Remove the mark with `id`, located via `pos` (the position it was added at). On a settled document
|
|
753
755
|
* that holds the mark this removes it in place; on a fresh/markless builder it records a transmittable
|
|
@@ -1125,6 +1127,7 @@ export type MarkJSON = {
|
|
|
1125
1127
|
key: number | string;
|
|
1126
1128
|
assoc: 1 | -1;
|
|
1127
1129
|
attrs?: object;
|
|
1130
|
+
transient?: boolean;
|
|
1128
1131
|
};
|
|
1129
1132
|
export type DeltaJSON = {
|
|
1130
1133
|
type: "delta";
|
|
@@ -1307,6 +1310,7 @@ import * as fingerprintTrait from '../trait/fingerprint.js';
|
|
|
1307
1310
|
* - `attrs` — optional user-supplied data carried with the mark **by reference**: the same
|
|
1308
1311
|
* object is shared across the mark, its `copy`/`clone`s, `toJSON`, and every `MarkPos` from
|
|
1309
1312
|
* `marksToPositions`, so the caller must treat it as immutable (do not mutate it after attaching).
|
|
1313
|
+
* - `transient` — the mark only rides through a mapping and must never be stored (see the field doc).
|
|
1310
1314
|
*
|
|
1311
1315
|
* A `Mark` is **immutable** — never mutate one in place; to "move" a mark, replace it with a fresh
|
|
1312
1316
|
* `Mark` via {@link Mark#copy} (the {@link Marks} set keys by id, so re-adding the same id replaces
|
|
@@ -1319,8 +1323,9 @@ declare class Mark {
|
|
|
1319
1323
|
* @param {string} [id] unique id; defaults to a fresh GUID
|
|
1320
1324
|
* @param {1|-1} [assoc] gravity at a boundary; defaults to right (`1`)
|
|
1321
1325
|
* @param {object?} [attrs] optional user data, stored by reference; treat as immutable
|
|
1326
|
+
* @param {boolean} [transient] the mark must never be stored; see the field doc
|
|
1322
1327
|
*/
|
|
1323
|
-
constructor(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null);
|
|
1328
|
+
constructor(key: number | string, id?: string, assoc?: 1 | -1, attrs?: object | null, transient?: boolean);
|
|
1324
1329
|
/**
|
|
1325
1330
|
* @readonly
|
|
1326
1331
|
* @type {number|string}
|
|
@@ -1341,6 +1346,18 @@ declare class Mark {
|
|
|
1341
1346
|
* @type {object?}
|
|
1342
1347
|
*/
|
|
1343
1348
|
readonly attrs: object | null;
|
|
1349
|
+
/**
|
|
1350
|
+
* A transient mark exists only to ride through a mapping (see
|
|
1351
|
+
* {@link import('./position.js').mapPositionsA}) and must never be stored: a consumer that
|
|
1352
|
+
* maintains persistent state (a document, a transformer's internal model of one) either filters
|
|
1353
|
+
* transient marks before applying a change to that state, or removes them when found on
|
|
1354
|
+
* final/settled state. How that is achieved is left to the implementation — the core does not
|
|
1355
|
+
* enforce it (today nothing in lib0 stores marks outside a document, so nothing needs to).
|
|
1356
|
+
*
|
|
1357
|
+
* @readonly
|
|
1358
|
+
* @type {boolean}
|
|
1359
|
+
*/
|
|
1360
|
+
readonly transient: boolean;
|
|
1344
1361
|
/**
|
|
1345
1362
|
* A copy of this mark, optionally at a different `key` (used to "move" an otherwise-immutable mark).
|
|
1346
1363
|
*
|
package/dist/delta/position.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export function equals(a: Pos | MarkPos, b: Pos | MarkPos): boolean;
|
|
|
35
35
|
*/
|
|
36
36
|
export const $markPos: s.Schema<MarkPos>;
|
|
37
37
|
export function marksToPositions(d: delta.DeltaAny): Array<MarkPos>;
|
|
38
|
+
export function mapPositionsA(tr: import("./transformer/core.js").Transformer<any, any>, positions: Array<Pos>): Array<Pos | null>;
|
|
39
|
+
export function mapPositionsB(tr: import("./transformer/core.js").Transformer<any, any>, positions: Array<Pos>): Array<Pos | null>;
|
|
38
40
|
/**
|
|
39
41
|
* A single step of a {@link Pos} path: a `string` attribute key, or a `number` content index.
|
|
40
42
|
*/
|
package/dist/random.d.ts
CHANGED
|
@@ -2,5 +2,9 @@ export const rand: () => number;
|
|
|
2
2
|
export function uint32(): number;
|
|
3
3
|
export function uint53(): number;
|
|
4
4
|
export function oneOf<T>(arr: Array<T>): T;
|
|
5
|
-
export function
|
|
5
|
+
export function _uuidv4Polyfill(): string;
|
|
6
|
+
/**
|
|
7
|
+
* @type {() => string}
|
|
8
|
+
*/
|
|
9
|
+
export const uuidv4: () => string;
|
|
6
10
|
//# sourceMappingURL=random.d.ts.map
|
package/package.json
CHANGED
package/src/delta/delta.js
CHANGED
|
@@ -90,7 +90,7 @@ export const $attribution = /* @__PURE__ */(() => s.$object({
|
|
|
90
90
|
*/
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
* @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object }} MarkJSON
|
|
93
|
+
* @typedef {{ id: string, key: number|string, assoc: 1|-1, attrs?: object, transient?: boolean }} MarkJSON
|
|
94
94
|
*/
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -1136,6 +1136,7 @@ export const $modifyAttrOpWith = $content => s.$custom(o => $modifyAttrOp.check(
|
|
|
1136
1136
|
* - `attrs` — optional user-supplied data carried with the mark **by reference**: the same
|
|
1137
1137
|
* object is shared across the mark, its `copy`/`clone`s, `toJSON`, and every `MarkPos` from
|
|
1138
1138
|
* `marksToPositions`, so the caller must treat it as immutable (do not mutate it after attaching).
|
|
1139
|
+
* - `transient` — the mark only rides through a mapping and must never be stored (see the field doc).
|
|
1139
1140
|
*
|
|
1140
1141
|
* A `Mark` is **immutable** — never mutate one in place; to "move" a mark, replace it with a fresh
|
|
1141
1142
|
* `Mark` via {@link Mark#copy} (the {@link Marks} set keys by id, so re-adding the same id replaces
|
|
@@ -1148,8 +1149,9 @@ class Mark {
|
|
|
1148
1149
|
* @param {string} [id] unique id; defaults to a fresh GUID
|
|
1149
1150
|
* @param {1|-1} [assoc] gravity at a boundary; defaults to right (`1`)
|
|
1150
1151
|
* @param {object?} [attrs] optional user data, stored by reference; treat as immutable
|
|
1152
|
+
* @param {boolean} [transient] the mark must never be stored; see the field doc
|
|
1151
1153
|
*/
|
|
1152
|
-
constructor (key, id = rand.uuidv4(), assoc = 1, attrs = null) {
|
|
1154
|
+
constructor (key, id = rand.uuidv4(), assoc = 1, attrs = null, transient = false) {
|
|
1153
1155
|
/**
|
|
1154
1156
|
* @readonly
|
|
1155
1157
|
* @type {number|string}
|
|
@@ -1170,6 +1172,18 @@ class Mark {
|
|
|
1170
1172
|
* @type {object?}
|
|
1171
1173
|
*/
|
|
1172
1174
|
this.attrs = attrs
|
|
1175
|
+
/**
|
|
1176
|
+
* A transient mark exists only to ride through a mapping (see
|
|
1177
|
+
* {@link import('./position.js').mapPositionsA}) and must never be stored: a consumer that
|
|
1178
|
+
* maintains persistent state (a document, a transformer's internal model of one) either filters
|
|
1179
|
+
* transient marks before applying a change to that state, or removes them when found on
|
|
1180
|
+
* final/settled state. How that is achieved is left to the implementation — the core does not
|
|
1181
|
+
* enforce it (today nothing in lib0 stores marks outside a document, so nothing needs to).
|
|
1182
|
+
*
|
|
1183
|
+
* @readonly
|
|
1184
|
+
* @type {boolean}
|
|
1185
|
+
*/
|
|
1186
|
+
this.transient = transient
|
|
1173
1187
|
}
|
|
1174
1188
|
|
|
1175
1189
|
/**
|
|
@@ -1179,23 +1193,26 @@ class Mark {
|
|
|
1179
1193
|
* @return {Mark}
|
|
1180
1194
|
*/
|
|
1181
1195
|
copy (key = this.key) {
|
|
1182
|
-
return new Mark(key, this.id, this.assoc, this.attrs)
|
|
1196
|
+
return new Mark(key, this.id, this.assoc, this.attrs, this.transient)
|
|
1183
1197
|
}
|
|
1184
1198
|
|
|
1185
1199
|
/**
|
|
1186
1200
|
* @return {MarkJSON}
|
|
1187
1201
|
*/
|
|
1188
1202
|
toJSON () {
|
|
1189
|
-
|
|
1203
|
+
/** @type {MarkJSON} */
|
|
1204
|
+
const json = this.attrs === null
|
|
1190
1205
|
? { id: this.id, key: this.key, assoc: this.assoc }
|
|
1191
1206
|
: { id: this.id, key: this.key, assoc: this.assoc, attrs: this.attrs }
|
|
1207
|
+
if (this.transient) json.transient = true
|
|
1208
|
+
return json
|
|
1192
1209
|
}
|
|
1193
1210
|
|
|
1194
1211
|
/**
|
|
1195
1212
|
* @param {Mark} other
|
|
1196
1213
|
*/
|
|
1197
1214
|
[equalityTrait.EqualityTraitSymbol] (other) {
|
|
1198
|
-
return $mark.check(other) && this.id === other.id && this.key === other.key && this.assoc === other.assoc && fun.equalityDeep(this.attrs, other.attrs)
|
|
1215
|
+
return $mark.check(other) && this.id === other.id && this.key === other.key && this.assoc === other.assoc && this.transient === other.transient && fun.equalityDeep(this.attrs, other.attrs)
|
|
1199
1216
|
}
|
|
1200
1217
|
}
|
|
1201
1218
|
|
|
@@ -1203,16 +1220,17 @@ export const $mark = /** @type {s.Schema<Mark>} */ (Mark.prototype.$type = s.$ty
|
|
|
1203
1220
|
|
|
1204
1221
|
/**
|
|
1205
1222
|
* Create a {@link Mark} (use this instead of `new Mark(...)`). `id` defaults to a fresh GUID, `assoc`
|
|
1206
|
-
* to right gravity (`1`), `attrs` to `null`. A `Mark` is stored on a delta
|
|
1207
|
-
* {@link Marks} set (see {@link DeltaBuilder#addMark}).
|
|
1223
|
+
* to right gravity (`1`), `attrs` to `null`, `transient` to `false`. A `Mark` is stored on a delta
|
|
1224
|
+
* node's own {@link Marks} set (see {@link DeltaBuilder#addMark}).
|
|
1208
1225
|
*
|
|
1209
1226
|
* @param {number|string} key
|
|
1210
1227
|
* @param {string} [id]
|
|
1211
1228
|
* @param {1|-1} [assoc]
|
|
1212
1229
|
* @param {object?} [attrs]
|
|
1230
|
+
* @param {boolean} [transient] never to be stored (see {@link Mark#transient})
|
|
1213
1231
|
* @return {Mark}
|
|
1214
1232
|
*/
|
|
1215
|
-
export const createMark = (key, id, assoc, attrs) => new Mark(key, id, assoc, attrs)
|
|
1233
|
+
export const createMark = (key, id, assoc, attrs, transient) => new Mark(key, id, assoc, attrs, transient)
|
|
1216
1234
|
|
|
1217
1235
|
/**
|
|
1218
1236
|
* @typedef {Delta<any>} DeltaAny
|
|
@@ -2135,12 +2153,14 @@ export class DeltaBuilder extends Delta {
|
|
|
2135
2153
|
*
|
|
2136
2154
|
* @param {import('./position.js').Pos} pos
|
|
2137
2155
|
* @param {string} [id]
|
|
2156
|
+
* @param {boolean} [transient] flag the mark as never-to-be-stored (a one-shot mapping probe, see
|
|
2157
|
+
* {@link Mark#transient} and {@link import('./position.js').mapPositionsA})
|
|
2138
2158
|
* @return {this}
|
|
2139
2159
|
*/
|
|
2140
|
-
addMark (pos, id = rand.uuidv4()) {
|
|
2160
|
+
addMark (pos, id = rand.uuidv4(), transient = false) {
|
|
2141
2161
|
// apply with the default `final = this.isFinal` (do NOT force `{ final: true }`): a fresh change
|
|
2142
2162
|
// builder is non-final so a sibling `removeMark` stays transmittable; a final doc collects nothing
|
|
2143
|
-
this.apply(/** @type {Delta<Conf>} */ (markChange(pos, id, false)))
|
|
2163
|
+
this.apply(/** @type {Delta<Conf>} */ (markChange(pos, id, false, transient)))
|
|
2144
2164
|
return this
|
|
2145
2165
|
}
|
|
2146
2166
|
|
|
@@ -3363,14 +3383,15 @@ const rebaseRootMarks = (node, other, priority) => {
|
|
|
3363
3383
|
* @param {import('./position.js').Pos} pos
|
|
3364
3384
|
* @param {string} id
|
|
3365
3385
|
* @param {boolean} isDelete
|
|
3386
|
+
* @param {boolean} [transient] never to be stored (see {@link Mark#transient})
|
|
3366
3387
|
* @return {DeltaBuilderAny}
|
|
3367
3388
|
*/
|
|
3368
|
-
const markChange = (pos, id, isDelete) => {
|
|
3389
|
+
const markChange = (pos, id, isDelete, transient = false) => {
|
|
3369
3390
|
const path = pos.path
|
|
3370
3391
|
// a mark anchors at the terminal step of its path (a content offset or attribute key); the root
|
|
3371
3392
|
// position `[]` has no terminal and cannot carry a mark - reject it instead of recursing forever
|
|
3372
3393
|
if (path.length === 0) throw error.create('cannot place a mark at the root position (empty path): a mark needs a terminal content-offset or attribute-key step')
|
|
3373
|
-
const mark = isDelete ? null : createMark(path[path.length - 1], id, pos.assoc, pos.attrs ?? null)
|
|
3394
|
+
const mark = isDelete ? null : createMark(path[path.length - 1], id, pos.assoc, pos.attrs ?? null, transient)
|
|
3374
3395
|
/**
|
|
3375
3396
|
* @param {number} i
|
|
3376
3397
|
* @return {DeltaBuilderAny}
|
package/src/delta/position.js
CHANGED
|
@@ -104,9 +104,12 @@ export const $markPos = /* @__PURE__ */ s.$object({
|
|
|
104
104
|
})
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* Reconstruct every {@link MarkPos} stored as a {@link import('./delta.js').Mark mark} inside `d`
|
|
108
|
-
* settled delta
|
|
109
|
-
* mark's
|
|
107
|
+
* Reconstruct every {@link MarkPos} stored as a {@link import('./delta.js').Mark mark} inside `d` — a
|
|
108
|
+
* settled delta or a change delta (e.g. a raw transformer output, or the change `addMark` builds). A
|
|
109
|
+
* mark's `path` is the content indices / attribute keys walked to reach it, plus the mark's own `key`;
|
|
110
|
+
* the walk descends insert embeds, `modify` values, and `setAttr`/`modifyAttr` values. In a change
|
|
111
|
+
* delta the indices are post-change coordinates (delete ops span no slot). Add marks with
|
|
112
|
+
* {@link import('./delta.js').DeltaBuilder#addMark}.
|
|
110
113
|
*
|
|
111
114
|
* Subtrees are pruned via each node's conservative `maybeHasMarks` flag (`false` ⇒ guaranteed empty,
|
|
112
115
|
* skipped). This is also the **sole resetter** of that flag: a descended subtree that turns out to hold
|
|
@@ -137,7 +140,8 @@ export const marksToPositions = d => {
|
|
|
137
140
|
: { id: m.id, path: [...path, m.key], assoc: m.assoc, attrs: m.attrs })
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
|
-
//
|
|
143
|
+
// descend the delta-valued insert embeds and the modify values (a change delta's mark rides on a
|
|
144
|
+
// modify's nested root marks - see markChange; `apply` may also leave a modify on a settled node)
|
|
141
145
|
let i = 0
|
|
142
146
|
for (const op of node.children) {
|
|
143
147
|
if (delta.$insertOp.check(op)) {
|
|
@@ -145,7 +149,12 @@ export const marksToPositions = d => {
|
|
|
145
149
|
if (delta.$deltaAny.check(el) && el.maybeHasMarks && walk(el, [...path, i])) found = true
|
|
146
150
|
i++
|
|
147
151
|
}
|
|
148
|
-
} else {
|
|
152
|
+
} else if (delta.$modifyOp.check(op)) {
|
|
153
|
+
if (op.value.maybeHasMarks && walk(op.value, [...path, i])) found = true
|
|
154
|
+
i++
|
|
155
|
+
} else if (!delta.$deleteOp.check(op)) {
|
|
156
|
+
// text / retain advance the content index; a delete occupies only the pre-change coordinate -
|
|
157
|
+
// mark paths (like mark keys, see delta.js's shiftMarkKey) are post-change content indices
|
|
149
158
|
i += op.length
|
|
150
159
|
}
|
|
151
160
|
}
|
|
@@ -163,3 +172,66 @@ export const marksToPositions = d => {
|
|
|
163
172
|
walk(d, [])
|
|
164
173
|
return out
|
|
165
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Shared body of {@link mapPositionsA}/{@link mapPositionsB}: encode `positions` as marks on one
|
|
178
|
+
* synthetic change (ids are the input indices), run it through `transform` once, and read the images
|
|
179
|
+
* back off the raw output. Nothing is ever applied to a document, so no mark persists anywhere.
|
|
180
|
+
*
|
|
181
|
+
* @param {Array<Pos>} positions
|
|
182
|
+
* @param {(change: delta.DeltaBuilderAny) => delta.DeltaAny?} transform
|
|
183
|
+
* @return {Array<Pos?>}
|
|
184
|
+
*/
|
|
185
|
+
const mapPositionsThrough = (positions, transform) => {
|
|
186
|
+
if (positions.length === 0) return []
|
|
187
|
+
const change = /** @type {delta.DeltaBuilderAny} */ (delta.create())
|
|
188
|
+
positions.forEach((pos, i) => change.addMark(pos, '' + i, true)) // transient probes - never to be stored
|
|
189
|
+
const mapped = transform(change)
|
|
190
|
+
/**
|
|
191
|
+
* @type {Map<string, MarkPos>}
|
|
192
|
+
*/
|
|
193
|
+
const byId = new Map()
|
|
194
|
+
if (mapped != null) {
|
|
195
|
+
// the raw output may alias transformer-internal state; marksToPositions' `maybeHasMarks`
|
|
196
|
+
// self-correction is safe there (`false` is only written where verifiably no marks exist)
|
|
197
|
+
for (const m of marksToPositions(mapped)) {
|
|
198
|
+
if (!byId.has(m.id)) byId.set(m.id, m) // fan-out (e.g. one datum in several project holes): first image in doc order wins
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return positions.map((_, i) => {
|
|
202
|
+
const m = byId.get('' + i)
|
|
203
|
+
return m === undefined ? null : create(m.path, m.assoc, m.attrs ?? null)
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* One-shot map of `positions` living on a transformer's **A side** to the B side (the mirror of
|
|
209
|
+
* {@link mapPositionsB}) — for when you want an answer *now* without maintaining a position-set as
|
|
210
|
+
* stored marks. One transform pass maps the whole batch; the result is in input order, each entry the
|
|
211
|
+
* B-side image or `null` when the position has none (a transformer maps marks best-effort — see the
|
|
212
|
+
* readme). `assoc` and `attrs` ride through on the probe mark.
|
|
213
|
+
*
|
|
214
|
+
* The probes are `transient` marks ({@link import('./delta.js').createMark Mark}`#transient`): a
|
|
215
|
+
* transformer that keeps marks in internal state must not store them (none does today). Beyond that
|
|
216
|
+
* the call is state-neutral by construction — a mark-only change shifts no content and is never
|
|
217
|
+
* applied to any document, so neither the documents nor the transformer are (semantically) modified.
|
|
218
|
+
*
|
|
219
|
+
* The transformer must already have been fed the document state (a live
|
|
220
|
+
* {@link import('./rdt.js').Binding} always has): with no document behind it there is nothing for a
|
|
221
|
+
* position to point into — and e.g. a fresh `project` transformer would consume its initial render.
|
|
222
|
+
*
|
|
223
|
+
* @param {import('./transformer/core.js').Transformer<any, any>} tr
|
|
224
|
+
* @param {Array<Pos>} positions positions on the A side
|
|
225
|
+
* @return {Array<Pos?>} their images on the B side (input order; `null` = no image)
|
|
226
|
+
*/
|
|
227
|
+
export const mapPositionsA = (tr, positions) => mapPositionsThrough(positions, c => tr.applyA(c).b)
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* One-shot map of `positions` living on a transformer's **B side** to the A side — see
|
|
231
|
+
* {@link mapPositionsA} for the contract (this is the same mapping, fed through `applyB`).
|
|
232
|
+
*
|
|
233
|
+
* @param {import('./transformer/core.js').Transformer<any, any>} tr
|
|
234
|
+
* @param {Array<Pos>} positions positions on the B side
|
|
235
|
+
* @return {Array<Pos?>} their images on the A side (input order; `null` = no image)
|
|
236
|
+
*/
|
|
237
|
+
export const mapPositionsB = (tr, positions) => mapPositionsThrough(positions, c => tr.applyB(c).a)
|
package/src/function.js
CHANGED
|
@@ -61,8 +61,6 @@ export const equalityStrict = (a, b) => a === b
|
|
|
61
61
|
*/
|
|
62
62
|
export const equalityFlat = (a, b) => a === b || (a != null && b != null && a.constructor === b.constructor && ((array.isArray(a) && array.equalFlat(a, /** @type {Array<T>} */ (b))) || (typeof a === 'object' && object.equalFlat(a, b))))
|
|
63
63
|
|
|
64
|
-
/* c8 ignore start */
|
|
65
|
-
|
|
66
64
|
/**
|
|
67
65
|
* @param {any} a
|
|
68
66
|
* @param {any} b
|
|
@@ -72,13 +70,20 @@ export const equalityDeep = (a, b) => {
|
|
|
72
70
|
if (a === b) {
|
|
73
71
|
return true
|
|
74
72
|
}
|
|
75
|
-
if (a == null || b == null
|
|
73
|
+
if (a == null || b == null) {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
// read the constructor off the prototype, not the object — a plain object may declare an own
|
|
77
|
+
// property named "constructor" (e.g. `{ constructor: 4 }`)
|
|
78
|
+
const aC = Object.getPrototypeOf(a)?.constructor
|
|
79
|
+
const bC = Object.getPrototypeOf(b)?.constructor
|
|
80
|
+
if (aC !== bC && (aC || Object) !== (bC || Object)) {
|
|
76
81
|
return false
|
|
77
82
|
}
|
|
78
83
|
if (a[equalityTrait.EqualityTraitSymbol] != null) {
|
|
79
84
|
return a[equalityTrait.EqualityTraitSymbol](b)
|
|
80
85
|
}
|
|
81
|
-
switch (
|
|
86
|
+
switch (aC) {
|
|
82
87
|
case ArrayBuffer:
|
|
83
88
|
a = new Uint8Array(a)
|
|
84
89
|
b = new Uint8Array(b)
|
|
@@ -152,7 +157,6 @@ export const equalityDeep = (a, b) => {
|
|
|
152
157
|
*/
|
|
153
158
|
// @ts-ignore
|
|
154
159
|
export const isOneOf = (value, options) => options.includes(value)
|
|
155
|
-
/* c8 ignore stop */
|
|
156
160
|
|
|
157
161
|
export const isArray = array.isArray
|
|
158
162
|
|
package/src/random.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Isomorphic module for true random numbers / buffers / uuids.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* uuidv4 uses the native `crypto.randomUUID` when available and falls back to a
|
|
5
|
+
* `getRandomValues`-based implementation otherwise (insecure browser contexts,
|
|
6
|
+
* react-native).
|
|
5
7
|
*
|
|
6
8
|
* @module random
|
|
7
9
|
*/
|
|
@@ -30,8 +32,20 @@ export const oneOf = arr => arr[math.floor(rand() * arr.length)]
|
|
|
30
32
|
const uuidv4Template = [1e7] + -1e3 + -4e3 + -8e3 + -1e11
|
|
31
33
|
|
|
32
34
|
/**
|
|
35
|
+
* Fallback for environments without `crypto.randomUUID` (insecure browser
|
|
36
|
+
* contexts, react-native).
|
|
37
|
+
*
|
|
33
38
|
* @return {string}
|
|
34
39
|
*/
|
|
35
|
-
export const
|
|
40
|
+
export const _uuidv4Polyfill = () => uuidv4Template.replace(/[018]/g, /** @param {number} c */ c =>
|
|
36
41
|
(c ^ uint32() & 15 >> c / 4).toString(16)
|
|
37
42
|
)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @type {() => string}
|
|
46
|
+
*/
|
|
47
|
+
export const uuidv4 = /* @__PURE__ */ (() =>
|
|
48
|
+
typeof crypto !== 'undefined' && crypto.randomUUID != null
|
|
49
|
+
? crypto.randomUUID.bind(crypto)
|
|
50
|
+
: _uuidv4Polyfill
|
|
51
|
+
)()
|