rapier-html 1.2.40 → 1.2.43
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/BUILD.json +20 -20
- package/README.md +10 -10
- package/dist/ledger/data.mjs +59 -2
- package/dist/ledger/format.mjs +65 -22
- package/dist/ledger/history.mjs +1 -1
- package/dist/ledger/inverse-source.mjs +9 -0
- package/dist/ledger/journal-records.mjs +197 -106
- package/dist/ledger/md-assets.mjs +7 -10
- package/dist/ledger/will.mjs +2 -2
- package/dist/rapier.html +74 -73
- package/dist/wrap.mjs +1 -0
- package/package.json +1 -1
|
@@ -76,7 +76,7 @@ function _rapierValidAuthored(origin) {
|
|
|
76
76
|
!/[\uD800-\uDFFF]/u.test(row.removed) && !/[\uD800-\uDFFF]/u.test(row.inserted));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function _rapierRecordIndex(records) {
|
|
79
|
+
function _rapierRecordIndex(records, shared = false) {
|
|
80
80
|
const byId = new Map(), splices = new Map(), metadata = new Map();
|
|
81
81
|
for (const record of records) {
|
|
82
82
|
const id = record?.transaction?.id;
|
|
@@ -100,20 +100,41 @@ function _rapierRecordIndex(records) {
|
|
|
100
100
|
}
|
|
101
101
|
cache.set(current, value);
|
|
102
102
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
103
|
-
if (path[i].transaction.reverts && value != null)
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
if (path[i].transaction.reverts && value != null) {
|
|
104
|
+
value = effects
|
|
105
|
+
? Object.fromEntries(Object.entries(value).map(([key, pair]) => [key, {before: pair.after, after: pair.before}]))
|
|
106
|
+
: value.slice().reverse().map(row => ({pos: row.pos, removed: row.inserted, inserted: row.removed}));
|
|
107
|
+
if (shared) { for (const row of Object.values(value)) Object.freeze(row); Object.freeze(value); }
|
|
108
|
+
}
|
|
106
109
|
cache.set(path[i], value);
|
|
107
110
|
}
|
|
108
111
|
return value;
|
|
109
112
|
};
|
|
110
|
-
|
|
113
|
+
const extend = more => { for (const record of more) { const id = record?.transaction?.id; if (!byId.has(id)) byId.set(id, []); byId.get(id).push(record); } };
|
|
114
|
+
return {byId, extend, splices: record => resolve(record, false), metadata: record => resolve(record, true)};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const _rapierLedgerIndexes = new WeakMap();
|
|
118
|
+
function _rapierLedgerIndex(ledger) {
|
|
119
|
+
const records = ledger || [];
|
|
120
|
+
if (!Array.isArray(records)) return _rapierRecordIndex(records);
|
|
121
|
+
const held = _rapierLedgerIndexes.get(records);
|
|
122
|
+
if (held && held.records.length === records.length && held.records.at(-1) === records.at(-1) &&
|
|
123
|
+
(held.checked || held.records.every((record, at) => record === records[at]))) {
|
|
124
|
+
if (!held.checked) { held.checked = true; queueMicrotask(() => { held.checked = false; }); }
|
|
125
|
+
return held.index;
|
|
126
|
+
}
|
|
127
|
+
if (!records.every(record => record && typeof record === 'object' && Object.isFrozen(record) && Object.isFrozen(record.transaction ?? {})))
|
|
128
|
+
return _rapierRecordIndex(records);
|
|
129
|
+
const index = _rapierRecordIndex(records, true);
|
|
130
|
+
_rapierLedgerIndexes.set(records, {records: records.slice(), index});
|
|
131
|
+
return index;
|
|
111
132
|
}
|
|
112
133
|
|
|
113
134
|
function _rapierRecordSplices(entry, ledger) {
|
|
114
135
|
try {
|
|
115
136
|
if (entry && Array.isArray(entry.splices)) return entry.splices;
|
|
116
|
-
return
|
|
137
|
+
return _rapierLedgerIndex(ledger).splices(entry);
|
|
117
138
|
} catch (_) { return null; }
|
|
118
139
|
}
|
|
119
140
|
|
|
@@ -163,7 +184,7 @@ function _rapierRecordMetadata(entry, ledger) {
|
|
|
163
184
|
try {
|
|
164
185
|
if (Object.hasOwn(entry, 'metadata')) return _rapierValidMetadataEffect(entry.metadata) ? entry.metadata : undefined;
|
|
165
186
|
if (Array.isArray(entry.splices)) return null;
|
|
166
|
-
return
|
|
187
|
+
return _rapierLedgerIndex(ledger).metadata(entry);
|
|
167
188
|
} catch (_) { return undefined; }
|
|
168
189
|
}
|
|
169
190
|
|
|
@@ -213,87 +234,89 @@ function _rapierPictureFinish(record, original, earlier, lookup) {
|
|
|
213
234
|
});
|
|
214
235
|
}
|
|
215
236
|
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
inactive.add(ref);
|
|
266
|
-
}
|
|
237
|
+
function _rapierHistoryWalker(records, lookup, visit = null, observe = null) {
|
|
238
|
+
let nodes = new Map(), references = new Map(), pictures = new Map();
|
|
239
|
+
const byId = new Map(), finished = new Set(), earlier = [];
|
|
240
|
+
const update = (target, delta) => {
|
|
241
|
+
const pending = [[target, delta]];
|
|
242
|
+
while (pending.length) {
|
|
243
|
+
const [id, change] = pending.pop(), node = nodes.get(id), was = node.active;
|
|
244
|
+
node.blockers += change;
|
|
245
|
+
if (node.blockers < 0) throw Error('effect');
|
|
246
|
+
node.active = node.blockers === 0;
|
|
247
|
+
if (was !== node.active) for (const ref of node.references) pending.push([ref, node.active ? 1 : -1]);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const activity = new Map();
|
|
251
|
+
const active = id => {
|
|
252
|
+
if (visit) return nodes.get(id).active;
|
|
253
|
+
const pending = [[id, 0]];
|
|
254
|
+
while (pending.length) {
|
|
255
|
+
const frame = pending.at(-1), target = frame[0];
|
|
256
|
+
if (activity.has(target)) { pending.pop(); continue; }
|
|
257
|
+
const dependents = nodes.get(target).dependents;
|
|
258
|
+
if (frame[1] === dependents.length) { activity.set(target, true); pending.pop(); continue; }
|
|
259
|
+
const next = dependents[frame[1]];
|
|
260
|
+
if (!activity.has(next)) pending.push([next, 0]);
|
|
261
|
+
else if (activity.get(next)) { activity.set(target, false); pending.pop(); }
|
|
262
|
+
else frame[1]++;
|
|
263
|
+
}
|
|
264
|
+
return activity.get(id);
|
|
265
|
+
};
|
|
266
|
+
const lastUndo = new Map(), contexts = new Map(), order = [];
|
|
267
|
+
const contextOf = origin => {
|
|
268
|
+
if (!origin) return null;
|
|
269
|
+
if (!_rapierValidAuthored(origin)) throw Error('origin');
|
|
270
|
+
const known = new Set(), pending = origin.basis.slice();
|
|
271
|
+
while (pending.length) {
|
|
272
|
+
const id = pending.pop(), context = contexts.get(id);
|
|
273
|
+
if (!context) throw Error('origin');
|
|
274
|
+
if (known.has(id)) continue;
|
|
275
|
+
known.add(id);
|
|
276
|
+
if (context.basis) pending.push(...context.basis);
|
|
277
|
+
else for (let index = 0; index < context.index; index++) known.add(order[index]);
|
|
278
|
+
}
|
|
279
|
+
const inactive = new Set();
|
|
280
|
+
for (let index = order.length - 1; index >= 0; index--) {
|
|
281
|
+
const id = order[index];
|
|
282
|
+
if (!known.has(id) || inactive.has(id)) continue;
|
|
283
|
+
for (const ref of references.get(id)) {
|
|
284
|
+
if (!known.has(ref)) throw Error('origin');
|
|
285
|
+
inactive.add(ref);
|
|
267
286
|
}
|
|
268
|
-
|
|
269
|
-
};
|
|
270
|
-
|
|
287
|
+
}
|
|
288
|
+
return {known, inactive};
|
|
289
|
+
};
|
|
290
|
+
const walker = {observe, failed: false};
|
|
291
|
+
walker.add = record => {
|
|
292
|
+
if (walker.failed) return false;
|
|
293
|
+
try {
|
|
271
294
|
const tx = record?.transaction;
|
|
272
|
-
if (!tx || typeof tx.id !== 'string' || !tx.id || nodes.has(tx.id) || (tx.reverts && tx.reapplies)) return
|
|
295
|
+
if (!tx || typeof tx.id !== 'string' || !tx.id || nodes.has(tx.id) || (tx.reverts && tx.reapplies)) { walker.failed = true; return false; }
|
|
273
296
|
const supplied = tx.sourceTransactionIds || (tx.sourceTransactionId ? [tx.sourceTransactionId] : []);
|
|
274
297
|
if (!Array.isArray(supplied) || new Set(supplied).size !== supplied.length ||
|
|
275
|
-
(tx.sourceTransactionIds && tx.sourceTransactionId && (supplied.length !== 1 || supplied[0] !== tx.sourceTransactionId))) return
|
|
298
|
+
(tx.sourceTransactionIds && tx.sourceTransactionId && (supplied.length !== 1 || supplied[0] !== tx.sourceTransactionId))) { walker.failed = true; return false; }
|
|
276
299
|
let refs = supplied;
|
|
277
300
|
if (tx.operation === 'document.finish-picture' && tx.sourceTransactionId) {
|
|
278
301
|
const owner = tx.sourceTransactionId;
|
|
279
302
|
if (!nodes.has(owner) || !active(owner) || finished.has(owner) ||
|
|
280
|
-
!_rapierPictureFinish(record, byId.get(owner), earlier, lookup ||= _rapierRecordIndex(records))) return
|
|
303
|
+
!_rapierPictureFinish(record, byId.get(owner), earlier, lookup ||= _rapierRecordIndex(records))) { walker.failed = true; return false; }
|
|
281
304
|
pictures.set(tx.id, owner); finished.add(owner); refs = [];
|
|
282
305
|
}
|
|
283
306
|
if (tx.reverts || tx.reapplies) {
|
|
284
307
|
const target = tx.reverts || tx.reapplies;
|
|
285
|
-
if (!nodes.has(target) || (supplied.length && (supplied.length !== 1 || supplied[0] !== target))) return
|
|
308
|
+
if (!nodes.has(target) || (supplied.length && (supplied.length !== 1 || supplied[0] !== target))) { walker.failed = true; return false; }
|
|
286
309
|
if (tx.reverts) refs = [target];
|
|
287
310
|
else {
|
|
288
311
|
const context = contextOf(record.authored || tx.authored);
|
|
289
312
|
const undo = (lastUndo.get(target) || []).findLast(id => context
|
|
290
313
|
? context.known.has(id) && !context.inactive.has(id) : active(id));
|
|
291
|
-
if (!undo) return
|
|
314
|
+
if (!undo) { walker.failed = true; return false; }
|
|
292
315
|
refs = [undo];
|
|
293
316
|
}
|
|
294
317
|
}
|
|
295
|
-
if (refs.some(id => typeof id !== 'string' || !nodes.has(id) || pictures.has(id))) return
|
|
296
|
-
if (tx.actor?.kind === 'agent' && refs.some(id => byId.get(id).transaction.actor?.kind !== 'agent')) return
|
|
318
|
+
if (refs.some(id => typeof id !== 'string' || !nodes.has(id) || pictures.has(id))) { walker.failed = true; return false; }
|
|
319
|
+
if (tx.actor?.kind === 'agent' && refs.some(id => byId.get(id).transaction.actor?.kind !== 'agent')) { walker.failed = true; return false; }
|
|
297
320
|
nodes.set(tx.id, {active: true, blockers: 0, references: refs, dependents: []}); references.set(tx.id, refs.slice());
|
|
298
321
|
byId.set(tx.id, record); earlier.push(record);
|
|
299
322
|
for (const ref of refs) {
|
|
@@ -308,23 +331,45 @@ function _rapierWalkHistoryEffects(records, lookup, visit = null, observe = null
|
|
|
308
331
|
lastUndo.get(tx.reverts).push(tx.id);
|
|
309
332
|
}
|
|
310
333
|
if (visit) visit(record, nodes, refs);
|
|
311
|
-
if (observe) observe(record, refs, active);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
if (walker.observe) walker.observe(record, refs, active);
|
|
335
|
+
return true;
|
|
336
|
+
} catch (_) { walker.failed = true; return false; }
|
|
337
|
+
};
|
|
338
|
+
walker.finish = () => {
|
|
339
|
+
if (walker.failed) return {ok: false};
|
|
340
|
+
const blockers = new Map(), effectiveActIds = [];
|
|
341
|
+
for (let i = order.length - 1; i >= 0; i--) {
|
|
342
|
+
const id = order[i], node = nodes.get(id), on = visit ? node.active : !(blockers.get(id) > 0);
|
|
343
|
+
if (!visit && on) for (const ref of node.references) blockers.set(ref, (blockers.get(ref) || 0) + 1);
|
|
344
|
+
if (on && !pictures.has(id)) effectiveActIds.push(id);
|
|
316
345
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
346
|
+
effectiveActIds.reverse();
|
|
347
|
+
const result = {ok: true, effectiveActIds, active: new Set(effectiveActIds), references, pictures};
|
|
348
|
+
walker.shared = true;
|
|
349
|
+
return result;
|
|
350
|
+
};
|
|
351
|
+
const add = walker.add;
|
|
352
|
+
walker.add = record => {
|
|
353
|
+
if (walker.shared) { references = new Map(references); pictures = new Map(pictures); walker.shared = false; }
|
|
354
|
+
return add(record);
|
|
355
|
+
};
|
|
356
|
+
return walker;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function _rapierWalkHistoryEffects(records, lookup, visit = null, observe = null) {
|
|
360
|
+
if (!Array.isArray(records)) return {ok: false};
|
|
361
|
+
const walker = _rapierHistoryWalker(records, lookup, visit, observe);
|
|
362
|
+
for (const record of records) if (!walker.add(record)) return {ok: false};
|
|
363
|
+
return walker.finish();
|
|
320
364
|
}
|
|
321
365
|
|
|
322
366
|
function _rapierHistoryEffects(records, visit = null) {
|
|
323
367
|
return _rapierWalkHistoryEffects(records, null, visit);
|
|
324
368
|
}
|
|
325
369
|
|
|
326
|
-
function _rapierMetadataProof(lookup) {
|
|
327
|
-
const values = {}, required = {},
|
|
370
|
+
function _rapierMetadataProof(lookup, from = null) {
|
|
371
|
+
const values = {...from?.values}, required = {...from?.required},
|
|
372
|
+
assignments = {filename: [...(from?.assignments.filename || [])], docKind: [...(from?.assignments.docKind || [])]};
|
|
328
373
|
const requireInitial = (key, value) => {
|
|
329
374
|
if (Object.hasOwn(required, key) && required[key] !== value) throw Error('metadata_effect');
|
|
330
375
|
required[key] = value;
|
|
@@ -351,7 +396,7 @@ function _rapierMetadataProof(lookup) {
|
|
|
351
396
|
if (!_rapierValidMetadata(initial) || Object.keys(required).some(key => initial[key] !== required[key])) return null;
|
|
352
397
|
return {...initial, ...values};
|
|
353
398
|
};
|
|
354
|
-
return {observe, replay};
|
|
399
|
+
return {observe, replay, fork: other => _rapierMetadataProof(other || lookup, {values, required, assignments})};
|
|
355
400
|
}
|
|
356
401
|
|
|
357
402
|
function _rapierMetadataState(initial, records, effects = _rapierHistoryEffects(records)) {
|
|
@@ -458,16 +503,79 @@ const _rapierPrepareHistoryRecords = (() => {
|
|
|
458
503
|
frame.descriptors = descriptors; frame.fields = fields;
|
|
459
504
|
frame.leaving = true; active.add(value); pending.push(frame);
|
|
460
505
|
for (let at = fields.length - 1; at >= 0; at--) {
|
|
461
|
-
const key = fields[at]
|
|
506
|
+
const key = fields[at], child = descriptors[key].value;
|
|
507
|
+
const prior = array && child && typeof child === 'object' ? copies.get(child) : undefined;
|
|
508
|
+
if (prior?.immutable && !active.has(child) && frame.depth + 1 + prior.height <= 64) {
|
|
509
|
+
finish({parent: frame, key}, prior.copy, true, prior.height); continue;
|
|
510
|
+
}
|
|
511
|
+
pending.push({value: child, parent: frame, key, depth: frame.depth + 1});
|
|
462
512
|
}
|
|
463
513
|
}
|
|
464
514
|
return result.value;
|
|
465
515
|
};
|
|
516
|
+
let resumable = [];
|
|
517
|
+
const rowOf = (lookup, record) => {
|
|
518
|
+
const splices = lookup.splices(record), metadata = lookup.metadata(record);
|
|
519
|
+
if (splices && !Object.isFrozen(splices)) {
|
|
520
|
+
for (const row of splices) Object.freeze(row);
|
|
521
|
+
Object.freeze(splices);
|
|
522
|
+
}
|
|
523
|
+
if (metadata && !Object.isFrozen(metadata)) {
|
|
524
|
+
for (const pair of Object.values(metadata)) Object.freeze(pair);
|
|
525
|
+
Object.freeze(metadata);
|
|
526
|
+
}
|
|
527
|
+
return Object.freeze({record, splices, metadata});
|
|
528
|
+
};
|
|
529
|
+
const observe = (records, basis, byId, rows, effects, proof) => {
|
|
530
|
+
for (const refs of effects.references.values()) Object.freeze(refs);
|
|
531
|
+
const effectView = Object.freeze({ok: true, effectiveActIds: Object.freeze(effects.effectiveActIds),
|
|
532
|
+
active: view(effects.active), references: view(effects.references), pictures: view(effects.pictures)});
|
|
533
|
+
const observation = Object.freeze({ok: effects.ok, records, rows: Object.freeze(rows), effects: effectView, byId: view(byId),
|
|
534
|
+
replayMetadata: initial => effects.ok ? proof.replay(initial) : null,
|
|
535
|
+
validRecord: (index, documentAuthority, maxDocumentBytes, utf8Length, allowGap = false) => {
|
|
536
|
+
if (!effects.ok || !Number.isSafeInteger(index) || index < 0 || index >= records.length) return false;
|
|
537
|
+
const row = rows[index], tx = row.record.transaction;
|
|
538
|
+
return _rapierCheckLedgerRecord(row.record, records[index - 1] || null, documentAuthority, maxDocumentBytes, utf8Length, allowGap,
|
|
539
|
+
{...row, duplicate: false, semantic: !!(tx.reverts || tx.reapplies || tx.sourceTransactionId || tx.sourceTransactionIds?.length)});
|
|
540
|
+
}});
|
|
541
|
+
prepared = new WeakMap([[records, {basis, observation}]]);
|
|
542
|
+
return observation;
|
|
543
|
+
};
|
|
544
|
+
const authorityOf = state => state.records[0]?.transaction?.documentAuthority;
|
|
545
|
+
const keep = state => { resumable = [state, ...resumable.filter(row => row !== state && row.records[0] !== state.records[0] &&
|
|
546
|
+
authorityOf(row) === authorityOf(state))].slice(0, 4); };
|
|
547
|
+
const extend = records => {
|
|
548
|
+
const base = resumable.find(row => row.records.length < records.length && row.records[0] === records[0] &&
|
|
549
|
+
row.records.every((record, at) => record === records[at]));
|
|
550
|
+
if (!base) return null;
|
|
551
|
+
resumable = resumable.filter(row => row !== base);
|
|
552
|
+
const more = records.slice(base.records.length), byId = new Map(base.byId);
|
|
553
|
+
for (const record of more) {
|
|
554
|
+
const id = record?.transaction?.id;
|
|
555
|
+
if (typeof id !== 'string' || !id || byId.has(id)) return null;
|
|
556
|
+
byId.set(id, record);
|
|
557
|
+
}
|
|
558
|
+
const {lookup, walker} = base;
|
|
559
|
+
lookup.extend(more);
|
|
560
|
+
const rows = base.rows.concat(more.map(record => rowOf(lookup, record)));
|
|
561
|
+
const proof = base.proof.fork(lookup);
|
|
562
|
+
walker.observe = proof.observe;
|
|
563
|
+
for (const record of more) if (!walker.add(record)) return null;
|
|
564
|
+
const effects = walker.finish();
|
|
565
|
+
if (!effects.ok) return null;
|
|
566
|
+
if (rows.every(row => row.splices && row.metadata !== undefined)) keep({records, lookup, walker, proof, byId, rows});
|
|
567
|
+
return observe(records, records, byId, rows, effects, proof);
|
|
568
|
+
};
|
|
466
569
|
return (input, earlier = input) => {
|
|
467
570
|
try {
|
|
468
571
|
if (!Array.isArray(input) || !Array.isArray(earlier)) return {ok: false};
|
|
469
572
|
const records = capture(input), basis = input === earlier ? records : capture(earlier), previous = prepared.get(records);
|
|
470
573
|
if (previous?.basis === basis) return previous.observation;
|
|
574
|
+
if (basis === records) {
|
|
575
|
+
let grown = null;
|
|
576
|
+
try { grown = extend(records); } catch (_) { grown = null; }
|
|
577
|
+
if (grown) return grown;
|
|
578
|
+
}
|
|
471
579
|
const byId = new Map();
|
|
472
580
|
for (const record of records) {
|
|
473
581
|
const id = record?.transaction?.id;
|
|
@@ -475,41 +583,24 @@ const _rapierPrepareHistoryRecords = (() => {
|
|
|
475
583
|
byId.set(id, record);
|
|
476
584
|
}
|
|
477
585
|
const lookup = _rapierRecordIndex(basis), proof = _rapierMetadataProof(lookup);
|
|
478
|
-
const rows = records.map(record =>
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
Object.freeze(splices);
|
|
483
|
-
}
|
|
484
|
-
if (metadata && !Object.isFrozen(metadata)) {
|
|
485
|
-
for (const pair of Object.values(metadata)) Object.freeze(pair);
|
|
486
|
-
Object.freeze(metadata);
|
|
487
|
-
}
|
|
488
|
-
return Object.freeze({record, splices, metadata});
|
|
489
|
-
});
|
|
490
|
-
const effects = _rapierWalkHistoryEffects(records, lookup, null, proof.observe);
|
|
586
|
+
const rows = records.map(record => rowOf(lookup, record));
|
|
587
|
+
const walker = _rapierHistoryWalker(records, lookup, null, proof.observe);
|
|
588
|
+
for (const record of records) if (!walker.add(record)) return {ok: false};
|
|
589
|
+
const effects = walker.finish();
|
|
491
590
|
if (!effects.ok) return {ok: false};
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const observation = Object.freeze({ok: effects.ok, records, rows: Object.freeze(rows), effects: effectView, byId: view(byId),
|
|
496
|
-
replayMetadata: initial => effects.ok ? proof.replay(initial) : null,
|
|
497
|
-
validRecord: (index, documentAuthority, maxDocumentBytes, utf8Length, allowGap = false) => {
|
|
498
|
-
if (!effects.ok || !Number.isSafeInteger(index) || index < 0 || index >= records.length) return false;
|
|
499
|
-
const row = rows[index], tx = row.record.transaction;
|
|
500
|
-
return _rapierCheckLedgerRecord(row.record, records[index - 1] || null, documentAuthority, maxDocumentBytes, utf8Length, allowGap,
|
|
501
|
-
{...row, duplicate: false, semantic: !!(tx.reverts || tx.reapplies || tx.sourceTransactionId || tx.sourceTransactionIds?.length)});
|
|
502
|
-
}});
|
|
503
|
-
prepared = new WeakMap([[records, {basis, observation}]]); return observation;
|
|
591
|
+
if (basis === records && rows.every(row => row.splices && row.metadata !== undefined))
|
|
592
|
+
keep({records, lookup, walker, proof, byId, rows});
|
|
593
|
+
return observe(records, basis, byId, rows, effects, proof);
|
|
504
594
|
} catch (_) { return {ok: false}; }
|
|
505
595
|
};
|
|
506
596
|
})();
|
|
507
597
|
|
|
508
598
|
const _rapierJournalRecord = (() => {
|
|
509
599
|
const copies = new WeakMap();
|
|
600
|
+
const fixed = value => { if (value && typeof value === 'object') { for (const key of Object.keys(value)) fixed(value[key]); Object.freeze(value); } return value; };
|
|
510
601
|
return record => {
|
|
511
602
|
let copy = copies.get(record);
|
|
512
|
-
if (!copy) { copy = JSON.parse(JSON.stringify(record)); copies.set(record, copy); }
|
|
603
|
+
if (!copy) { copy = fixed(JSON.parse(JSON.stringify(record))); copies.set(record, copy); }
|
|
513
604
|
return copy;
|
|
514
605
|
};
|
|
515
606
|
})();
|
|
@@ -100,7 +100,7 @@ export function* blockWindows(source, bodyOffset, parser, env, keepNul = false)
|
|
|
100
100
|
return end < body.length ? sourceOffset(end + 1) : undefined;
|
|
101
101
|
}});
|
|
102
102
|
let lines = 0;
|
|
103
|
-
for (let at =
|
|
103
|
+
for (let at = body.indexOf('\n'); at !== -1; at = body.indexOf('\n', at + 1)) lines++;
|
|
104
104
|
if (lines <= BLOCK_WINDOW) {
|
|
105
105
|
state = new parser.block.State(body, parser, env, tokens);
|
|
106
106
|
state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
|
|
@@ -179,8 +179,7 @@ export function* blockWindows(source, bodyOffset, parser, env, keepNul = false)
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
const
|
|
183
|
-
code >= 97 && code <= 122 || '-._~:/?@+$,;='.includes(String.fromCharCode(code)));
|
|
182
|
+
const INVALID_PERCENT_PAYLOAD = /[^A-Za-z0-9._~:\/?@+$,;=%-]/;
|
|
184
183
|
const isHexUnit = code => code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
|
|
185
184
|
let lastScan = null;
|
|
186
185
|
export function dataImage(url) {
|
|
@@ -197,13 +196,11 @@ function scanDataImage(url) {
|
|
|
197
196
|
...(prepared.encoding ? {encoding: prepared.encoding} : {})};
|
|
198
197
|
if (/^data:image\/svg\+xml,/i.test(url)) {
|
|
199
198
|
const payloadStart = 19;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
at += 2;
|
|
206
|
-
} else if (!(code < 128 && PERCENT_PAYLOAD[code])) return null;
|
|
199
|
+
if (INVALID_PERCENT_PAYLOAD.test(url)) return null;
|
|
200
|
+
let byteLength = url.length - payloadStart;
|
|
201
|
+
for (let at = url.indexOf('%', payloadStart); at !== -1; at = url.indexOf('%', at + 3)) {
|
|
202
|
+
if (!isHexUnit(url.charCodeAt(at + 1)) || !isHexUnit(url.charCodeAt(at + 2))) return null;
|
|
203
|
+
byteLength -= 2;
|
|
207
204
|
}
|
|
208
205
|
return byteLength && byteLength <= IMAGE_LIMITS.bytes
|
|
209
206
|
? {codec: 'image/svg+xml', byteLength, payloadStart, encoding: 'percent'} : null;
|
package/dist/ledger/will.mjs
CHANGED
|
@@ -215,7 +215,7 @@ export function willTransition(beforeText, afterText, splices, markerSplices = s
|
|
|
215
215
|
return null;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
export function evaluateWill(beforeText, splices) {
|
|
218
|
+
export function evaluateWill(beforeText, splices, {restores = null} = {}) {
|
|
219
219
|
const invalid = rule => ({outcome: 'invalid', reason: 'precondition', rule});
|
|
220
220
|
if (typeof beforeText !== 'string' || !beforeText.isWellFormed()) return invalid('invalid_unicode');
|
|
221
221
|
if (!Array.isArray(splices)) return invalid('malformed_splice');
|
|
@@ -231,7 +231,7 @@ export function evaluateWill(beforeText, splices) {
|
|
|
231
231
|
if (afterText.slice(row.pos, end) !== row.removed) return invalid('source_mismatch');
|
|
232
232
|
afterText = afterText.slice(0, row.pos) + row.inserted + afterText.slice(end);
|
|
233
233
|
}
|
|
234
|
-
const refusal = splices.length && willTransition(beforeText, afterText, splices);
|
|
234
|
+
const refusal = splices.length && willTransition(beforeText, afterText, splices, splices, {restores});
|
|
235
235
|
return refusal ? {outcome: 'refused', reason: 'document_law', ...refusal} : {outcome: 'applied'};
|
|
236
236
|
}
|
|
237
237
|
|