@peerbit/document 13.0.8 → 13.0.10
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/benchmark/file-ingest.d.ts +2 -0
- package/dist/benchmark/file-ingest.d.ts.map +1 -0
- package/dist/benchmark/file-ingest.js +606 -0
- package/dist/benchmark/file-ingest.js.map +1 -0
- package/dist/src/program.d.ts +2 -0
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +45 -28
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +15 -2
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +55 -7
- package/dist/src/search.js.map +1 -1
- package/package.json +14 -13
- package/src/program.ts +73 -35
- package/src/search.ts +80 -15
package/src/program.ts
CHANGED
|
@@ -43,9 +43,12 @@ import {
|
|
|
43
43
|
type CanSearch,
|
|
44
44
|
DocumentIndex,
|
|
45
45
|
type GetOptions,
|
|
46
|
+
type IndexedContextOnly,
|
|
47
|
+
INDEX_CONTEXT_SHAPE,
|
|
46
48
|
type PrefetchOptions,
|
|
47
49
|
type ReachScope,
|
|
48
50
|
type TransformOptions,
|
|
51
|
+
type WithContext,
|
|
49
52
|
type WithIndexedContext,
|
|
50
53
|
coerceWithContext,
|
|
51
54
|
coerceWithIndexed,
|
|
@@ -176,6 +179,33 @@ export class Documents<
|
|
|
176
179
|
return this._index;
|
|
177
180
|
}
|
|
178
181
|
|
|
182
|
+
private getLocalIndexedContext(
|
|
183
|
+
key: indexerTypes.IdKey,
|
|
184
|
+
): Promise<indexerTypes.IndexedResult<IndexedContextOnly<I>> | undefined> {
|
|
185
|
+
return this._index.index.get(key, {
|
|
186
|
+
shape: INDEX_CONTEXT_SHAPE,
|
|
187
|
+
}) as Promise<
|
|
188
|
+
indexerTypes.IndexedResult<IndexedContextOnly<I>> | undefined
|
|
189
|
+
>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private getExistingContext(
|
|
193
|
+
existing:
|
|
194
|
+
| ResultIndexedValue<WithContext<I>>
|
|
195
|
+
| indexerTypes.IndexedResult<WithContext<I>>
|
|
196
|
+
| indexerTypes.IndexedResult<IndexedContextOnly<I>>
|
|
197
|
+
| null
|
|
198
|
+
| undefined,
|
|
199
|
+
): indexerTypes.ReturnTypeFromShape<
|
|
200
|
+
WithContext<I>,
|
|
201
|
+
typeof INDEX_CONTEXT_SHAPE
|
|
202
|
+
>["__context"]
|
|
203
|
+
| undefined {
|
|
204
|
+
return existing instanceof ResultIndexedValue
|
|
205
|
+
? existing.context
|
|
206
|
+
: existing?.value.__context;
|
|
207
|
+
}
|
|
208
|
+
|
|
179
209
|
get changes() {
|
|
180
210
|
return this.events;
|
|
181
211
|
}
|
|
@@ -453,20 +483,23 @@ export class Documents<
|
|
|
453
483
|
|
|
454
484
|
const key = indexerTypes.toId(keyValue);
|
|
455
485
|
|
|
456
|
-
const existingDocument =
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
486
|
+
const existingDocument = this.immutable
|
|
487
|
+
? (
|
|
488
|
+
await this.index.getDetailed(key, {
|
|
489
|
+
resolve: false,
|
|
490
|
+
local: true,
|
|
491
|
+
remote: { strategy: "fallback" },
|
|
492
|
+
})
|
|
493
|
+
)?.[0]?.results[0]
|
|
494
|
+
: await this.getLocalIndexedContext(key);
|
|
495
|
+
const existingContext = this.getExistingContext(existingDocument);
|
|
496
|
+
if (existingContext && existingContext.head !== entry.hash) {
|
|
464
497
|
// econd condition can false if we reset the operation log, while not resetting the index. For example when doing .recover
|
|
465
498
|
if (this.immutable) {
|
|
466
499
|
// key already exist but pick the oldest entry
|
|
467
500
|
// this is because we can not overwrite same id if immutable
|
|
468
501
|
if (
|
|
469
|
-
|
|
502
|
+
existingContext.created <
|
|
470
503
|
entry.meta.clock.timestamp.wallTime
|
|
471
504
|
) {
|
|
472
505
|
return false;
|
|
@@ -485,7 +518,7 @@ export class Documents<
|
|
|
485
518
|
}
|
|
486
519
|
|
|
487
520
|
const prevEntry = await this.log.log.entryIndex.get(
|
|
488
|
-
|
|
521
|
+
existingContext.head,
|
|
489
522
|
);
|
|
490
523
|
if (!prevEntry) {
|
|
491
524
|
logger.error(
|
|
@@ -510,19 +543,26 @@ export class Documents<
|
|
|
510
543
|
if (entry.meta.next.length !== 1) {
|
|
511
544
|
return false;
|
|
512
545
|
}
|
|
513
|
-
const existingDocument =
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
546
|
+
const existingDocument = this.immutable
|
|
547
|
+
? (
|
|
548
|
+
await this.index.getDetailed(operation.key, {
|
|
549
|
+
resolve: false,
|
|
550
|
+
local: true,
|
|
551
|
+
remote: true,
|
|
552
|
+
})
|
|
553
|
+
)?.[0]?.results[0]
|
|
554
|
+
: await this.getLocalIndexedContext(
|
|
555
|
+
operation.key instanceof indexerTypes.IdKey
|
|
556
|
+
? operation.key
|
|
557
|
+
: indexerTypes.toId(operation.key),
|
|
558
|
+
);
|
|
559
|
+
const existingHead = this.getExistingContext(existingDocument)?.head;
|
|
560
|
+
|
|
561
|
+
if (!existingHead) {
|
|
522
562
|
// already deleted
|
|
523
563
|
return coerceDeleteOperation(operation); // assume ok
|
|
524
564
|
}
|
|
525
|
-
let doc = await this.log.log.get(
|
|
565
|
+
let doc = await this.log.log.get(existingHead);
|
|
526
566
|
if (!doc) {
|
|
527
567
|
logger.error("Failed to find Document from head");
|
|
528
568
|
return false;
|
|
@@ -560,7 +600,6 @@ export class Documents<
|
|
|
560
600
|
|
|
561
601
|
// type check the key
|
|
562
602
|
indexerTypes.checkId(keyValue);
|
|
563
|
-
|
|
564
603
|
const ser = serialize(doc);
|
|
565
604
|
if (ser.length > MAX_BATCH_SIZE) {
|
|
566
605
|
throw new Error(
|
|
@@ -570,17 +609,18 @@ export class Documents<
|
|
|
570
609
|
);
|
|
571
610
|
}
|
|
572
611
|
|
|
573
|
-
const
|
|
612
|
+
const existingHead = options?.unique
|
|
574
613
|
? undefined
|
|
575
|
-
:
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
614
|
+
: options?.checkRemote
|
|
615
|
+
? (
|
|
616
|
+
await this._index.getDetailed(keyValue, {
|
|
617
|
+
resolve: false,
|
|
618
|
+
local: true,
|
|
619
|
+
remote: { replicate: options?.replicate },
|
|
620
|
+
})
|
|
621
|
+
)?.[0]?.results[0]?.context.head
|
|
622
|
+
: (await this.getLocalIndexedContext(indexerTypes.toId(keyValue)))
|
|
623
|
+
?.value.__context.head;
|
|
584
624
|
|
|
585
625
|
let operation: PutOperation | PutWithKeyOperation;
|
|
586
626
|
if (this.compatibility === 6) {
|
|
@@ -601,9 +641,7 @@ export class Documents<
|
|
|
601
641
|
const appended = await this.log.append(operation, {
|
|
602
642
|
...options,
|
|
603
643
|
meta: {
|
|
604
|
-
next:
|
|
605
|
-
? [await this._resolveEntry(existingDocument.context.head)]
|
|
606
|
-
: [],
|
|
644
|
+
next: existingHead ? [await this._resolveEntry(existingHead)] : [],
|
|
607
645
|
...options?.meta,
|
|
608
646
|
},
|
|
609
647
|
canAppend: (entry) => {
|
|
@@ -732,7 +770,7 @@ export class Documents<
|
|
|
732
770
|
// if no casual ordering is used, use timestamps to order docs
|
|
733
771
|
let existing = reference?.unique
|
|
734
772
|
? null
|
|
735
|
-
: (await this.
|
|
773
|
+
: (await this.getLocalIndexedContext(key)) || null;
|
|
736
774
|
if (!this.strictHistory && existing) {
|
|
737
775
|
// if immutable use oldest, else use newest
|
|
738
776
|
let shouldIgnoreChange = this.immutable
|
package/src/search.ts
CHANGED
|
@@ -197,6 +197,7 @@ export type RemoteQueryOptions<Q, R, D> = RPCRequestAllOptions<Q, R> & {
|
|
|
197
197
|
replicate?: boolean;
|
|
198
198
|
minAge?: number;
|
|
199
199
|
throwOnMissing?: boolean;
|
|
200
|
+
retryMissingResponses?: boolean;
|
|
200
201
|
strategy?: "fallback";
|
|
201
202
|
domain?:
|
|
202
203
|
| {
|
|
@@ -404,6 +405,7 @@ const introduceEntries = async <
|
|
|
404
405
|
RPCResponse<types.Results<types.ResultTypeFromRequest<R, T, I>>>[]
|
|
405
406
|
> => {
|
|
406
407
|
const results: RPCResponse<types.Results<any>>[] = [];
|
|
408
|
+
const replicatedHeads = new Set<string>();
|
|
407
409
|
for (const response of responses) {
|
|
408
410
|
if (!response.from) {
|
|
409
411
|
logger.error("Missing from for response");
|
|
@@ -416,7 +418,27 @@ const introduceEntries = async <
|
|
|
416
418
|
: r.init(indexedType),
|
|
417
419
|
);
|
|
418
420
|
if (typeof options?.remote !== "boolean" && options?.remote?.replicate) {
|
|
419
|
-
|
|
421
|
+
const uniqueResults = response.response.results.filter((result) => {
|
|
422
|
+
const head =
|
|
423
|
+
result instanceof types.ResultIndexedValue &&
|
|
424
|
+
result.entries.length > 0
|
|
425
|
+
? result.entries[0]!.hash
|
|
426
|
+
: result.context.head;
|
|
427
|
+
if (replicatedHeads.has(head)) {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
replicatedHeads.add(head);
|
|
431
|
+
return true;
|
|
432
|
+
});
|
|
433
|
+
if (uniqueResults.length > 0) {
|
|
434
|
+
await sync(
|
|
435
|
+
queryRequest,
|
|
436
|
+
new types.Results({
|
|
437
|
+
results: uniqueResults,
|
|
438
|
+
kept: response.response.kept,
|
|
439
|
+
}),
|
|
440
|
+
);
|
|
441
|
+
}
|
|
420
442
|
}
|
|
421
443
|
options?.onResponse &&
|
|
422
444
|
(await options.onResponse(response.response, response.from!)); // TODO fix types
|
|
@@ -524,6 +546,19 @@ export type WithContext<I> = {
|
|
|
524
546
|
__context: types.Context;
|
|
525
547
|
} & I;
|
|
526
548
|
|
|
549
|
+
export const INDEX_CONTEXT_SHAPE = {
|
|
550
|
+
__context: {
|
|
551
|
+
created: true,
|
|
552
|
+
modified: true,
|
|
553
|
+
head: true,
|
|
554
|
+
gid: true,
|
|
555
|
+
size: true,
|
|
556
|
+
},
|
|
557
|
+
} as const;
|
|
558
|
+
|
|
559
|
+
export type IndexedContextOnly<I extends Record<string, any>> =
|
|
560
|
+
indexerTypes.ReturnTypeFromShape<WithContext<I>, typeof INDEX_CONTEXT_SHAPE>;
|
|
561
|
+
|
|
527
562
|
export type WithIndexed<T, I> = {
|
|
528
563
|
// experimental, used to quickly get the indexed representation
|
|
529
564
|
__indexed: I;
|
|
@@ -1534,10 +1569,18 @@ export class DocumentIndex<
|
|
|
1534
1569
|
value: T,
|
|
1535
1570
|
id: indexerTypes.IdKey,
|
|
1536
1571
|
entry: Entry<Operation>,
|
|
1537
|
-
existing:
|
|
1572
|
+
existing:
|
|
1573
|
+
| indexerTypes.IndexedResult<WithContext<I>>
|
|
1574
|
+
| indexerTypes.IndexedResult<IndexedContextOnly<I>>
|
|
1575
|
+
| null
|
|
1576
|
+
| undefined,
|
|
1538
1577
|
): Promise<{ context: types.Context; indexable: I }> {
|
|
1539
1578
|
const existingDefined =
|
|
1540
|
-
existing === undefined
|
|
1579
|
+
existing === undefined
|
|
1580
|
+
? await this.index.get(id, {
|
|
1581
|
+
shape: INDEX_CONTEXT_SHAPE,
|
|
1582
|
+
})
|
|
1583
|
+
: existing;
|
|
1541
1584
|
const context = new types.Context({
|
|
1542
1585
|
created:
|
|
1543
1586
|
existingDefined?.value.__context.created ||
|
|
@@ -1547,13 +1590,16 @@ export class DocumentIndex<
|
|
|
1547
1590
|
gid: entry.meta.gid,
|
|
1548
1591
|
size: entry.payload.byteLength,
|
|
1549
1592
|
});
|
|
1550
|
-
return this.putWithContext(value, id, context
|
|
1593
|
+
return this.putWithContext(value, id, context, {
|
|
1594
|
+
replace: existingDefined != null,
|
|
1595
|
+
});
|
|
1551
1596
|
}
|
|
1552
1597
|
|
|
1553
1598
|
public async putWithContext(
|
|
1554
1599
|
value: T,
|
|
1555
1600
|
id: indexerTypes.IdKey,
|
|
1556
1601
|
context: types.Context,
|
|
1602
|
+
options?: { replace?: boolean },
|
|
1557
1603
|
): Promise<{ context: types.Context; indexable: I }> {
|
|
1558
1604
|
const idString = id.primitive;
|
|
1559
1605
|
if (
|
|
@@ -1582,7 +1628,7 @@ export class DocumentIndex<
|
|
|
1582
1628
|
|
|
1583
1629
|
coerceWithContext(value, context);
|
|
1584
1630
|
|
|
1585
|
-
await this.index.put(wrappedValueToIndex);
|
|
1631
|
+
await this.index.put(wrappedValueToIndex, undefined, options);
|
|
1586
1632
|
return { context, indexable: valueToIndex };
|
|
1587
1633
|
}
|
|
1588
1634
|
|
|
@@ -2580,17 +2626,29 @@ export class DocumentIndex<
|
|
|
2580
2626
|
options?: O,
|
|
2581
2627
|
): Promise<ValueTypeFromRequest<Resolve, T, I>[]> {
|
|
2582
2628
|
// Set fetch to search size, or max value (default to max u32 (4294967295))
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2629
|
+
const coercedRequest = coerceQuery(
|
|
2630
|
+
queryRequest,
|
|
2631
|
+
options,
|
|
2632
|
+
this.compatibility,
|
|
2633
|
+
);
|
|
2634
|
+
coercedRequest.fetch = coercedRequest.fetch ?? 0xffffffff;
|
|
2635
|
+
|
|
2636
|
+
const searchOptions =
|
|
2637
|
+
typeof options?.remote === "object" &&
|
|
2638
|
+
options.remote.retryMissingResponses === undefined
|
|
2639
|
+
? ({
|
|
2640
|
+
...options,
|
|
2641
|
+
remote: {
|
|
2642
|
+
...options.remote,
|
|
2643
|
+
retryMissingResponses: false,
|
|
2644
|
+
},
|
|
2645
|
+
} as O)
|
|
2646
|
+
: options;
|
|
2589
2647
|
|
|
2590
|
-
|
|
2591
|
-
|
|
2648
|
+
// Use an iterator so large results respect message size limits.
|
|
2649
|
+
const iterator = this.iterate<Resolve>(coercedRequest, searchOptions);
|
|
2592
2650
|
|
|
2593
|
-
|
|
2651
|
+
const allResults: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
2594
2652
|
|
|
2595
2653
|
while (
|
|
2596
2654
|
iterator.done() !== true &&
|
|
@@ -2870,6 +2928,10 @@ export class DocumentIndex<
|
|
|
2870
2928
|
) {
|
|
2871
2929
|
queryRequestCoerced.replicate = true;
|
|
2872
2930
|
}
|
|
2931
|
+
const retryMissingResponseGroups =
|
|
2932
|
+
typeof options?.remote === "object"
|
|
2933
|
+
? options.remote.retryMissingResponses ?? true
|
|
2934
|
+
: true;
|
|
2873
2935
|
|
|
2874
2936
|
indexIteratorLogger.trace("Iterate with options", {
|
|
2875
2937
|
query: queryRequestCoerced,
|
|
@@ -3216,6 +3278,9 @@ export class DocumentIndex<
|
|
|
3216
3278
|
},
|
|
3217
3279
|
onMissingResponses: (error) => {
|
|
3218
3280
|
missingResponses = true;
|
|
3281
|
+
if (!retryMissingResponseGroups) {
|
|
3282
|
+
return;
|
|
3283
|
+
}
|
|
3219
3284
|
const missingGroups = (error as MissingResponsesError & {
|
|
3220
3285
|
missingGroups?: string[][];
|
|
3221
3286
|
}).missingGroups;
|
|
@@ -3242,7 +3307,7 @@ export class DocumentIndex<
|
|
|
3242
3307
|
fetchOptions?.fetchedFirstForRemote,
|
|
3243
3308
|
);
|
|
3244
3309
|
|
|
3245
|
-
if (missingResponses) {
|
|
3310
|
+
if (missingResponses && retryMissingResponseGroups) {
|
|
3246
3311
|
hasMore = true;
|
|
3247
3312
|
unsetDone();
|
|
3248
3313
|
}
|