@peerbit/log 1.0.15 → 2.0.1

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/src/entry.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { HLC, LamportClock as Clock, Timestamp } from "./clock.js";
2
- import { isDefined } from "./is-defined.js";
3
2
  import {
4
3
  variant,
5
4
  field,
@@ -54,11 +53,10 @@ const isMaybeEryptionPublicKey = (o: any) => {
54
53
  export type EncryptionTemplateMaybeEncrypted = EntryEncryptionTemplate<
55
54
  MaybeEncryptionPublicKey,
56
55
  MaybeEncryptionPublicKey,
57
- MaybeEncryptionPublicKey | { [key: string]: MaybeEncryptionPublicKey }, // signature either all signature encrypted by same key, or each individually
58
- MaybeEncryptionPublicKey
56
+ MaybeEncryptionPublicKey | { [key: string]: MaybeEncryptionPublicKey } // signature either all signature encrypted by same key, or each individually
59
57
  >;
60
58
  export interface EntryEncryption {
61
- reciever: EncryptionTemplateMaybeEncrypted;
59
+ receiver: EncryptionTemplateMaybeEncrypted;
62
60
  keypair: X25519Keypair;
63
61
  }
64
62
 
@@ -110,6 +108,16 @@ export class Payload<T> {
110
108
  return equals(this.data, other.data);
111
109
  }
112
110
 
111
+ get isDecoded(): boolean {
112
+ return this._value != null;
113
+ }
114
+
115
+ get value(): T {
116
+ if (this._value == null) {
117
+ throw new Error("Value not decoded. Invoke: .getValue once");
118
+ }
119
+ return this._value;
120
+ }
113
121
  getValue(encoding: Encoding<T> = this.encoding || NO_ENCODING): T {
114
122
  if (this._value != undefined) {
115
123
  return this._value;
@@ -118,11 +126,10 @@ export class Payload<T> {
118
126
  }
119
127
  }
120
128
 
121
- export interface EntryEncryptionTemplate<A, B, C, D> {
122
- metadata: A;
129
+ export interface EntryEncryptionTemplate<A, B, C> {
130
+ meta: A;
123
131
  payload: B;
124
132
  signatures: C;
125
- next: D;
126
133
  }
127
134
 
128
135
  export enum EntryType {
@@ -131,30 +138,35 @@ export enum EntryType {
131
138
  }
132
139
 
133
140
  @variant(0)
134
- export class Metadata {
135
- @field({ type: "string" })
136
- gid: string; // graph id
137
-
141
+ export class Meta {
138
142
  @field({ type: Clock })
139
143
  clock: Clock;
140
144
 
141
- @field({ type: "u64" })
142
- maxChainLength: bigint; // longest chain/merkle tree path frmo this node. maxChainLength := max ( maxChainLength(this.next) , 1)
145
+ @field({ type: "string" })
146
+ gid: string; // graph id
147
+
148
+ @field({ type: vec("string") })
149
+ next: string[];
143
150
 
144
151
  @field({ type: "u8" })
145
152
  type: EntryType;
146
153
 
154
+ @field({ type: option(Uint8Array) })
155
+ data?: Uint8Array; // Optional metadata
156
+
147
157
  constructor(properties?: {
148
158
  gid: string;
149
159
  clock: Clock;
150
- maxChainLength: bigint;
151
160
  type: EntryType;
161
+ data?: Uint8Array;
162
+ next: string[];
152
163
  }) {
153
164
  if (properties) {
154
165
  this.gid = properties.gid;
155
166
  this.clock = properties.clock;
156
- this.maxChainLength = properties.maxChainLength;
157
167
  this.type = properties.type;
168
+ this.data = properties.data;
169
+ this.next = properties.next;
158
170
  }
159
171
  }
160
172
  }
@@ -186,21 +198,21 @@ export class Signatures {
186
198
  const maybeEncrypt = <Q>(
187
199
  thing: Q,
188
200
  keypair?: X25519Keypair,
189
- reciever?: MaybeEncryptionPublicKey
201
+ receiver?: MaybeEncryptionPublicKey
190
202
  ): Promise<MaybeEncrypted<Q>> | MaybeEncrypted<Q> => {
191
- const recievers = reciever
192
- ? Array.isArray(reciever)
193
- ? reciever
194
- : [reciever]
203
+ const receivers = receiver
204
+ ? Array.isArray(receiver)
205
+ ? receiver
206
+ : [receiver]
195
207
  : undefined;
196
- if (recievers?.length && recievers?.length > 0) {
208
+ if (receivers?.length && receivers?.length > 0) {
197
209
  if (!keypair) {
198
210
  throw new Error("Keypair not provided");
199
211
  }
200
212
  return new DecryptedThing<Q>({
201
213
  data: serialize(thing),
202
214
  value: thing,
203
- }).encrypt(keypair, ...recievers);
215
+ }).encrypt(keypair, ...receivers);
204
216
  }
205
217
  return new DecryptedThing<Q>({
206
218
  data: serialize(thing),
@@ -208,32 +220,35 @@ const maybeEncrypt = <Q>(
208
220
  });
209
221
  };
210
222
 
223
+ export interface ShallowEntry {
224
+ hash: string;
225
+ meta: {
226
+ clock: Clock;
227
+ data?: Uint8Array;
228
+ gid: string;
229
+ next: string[];
230
+ type: EntryType;
231
+ };
232
+ payloadByteLength: number;
233
+ }
234
+
211
235
  @variant(0)
212
236
  export class Entry<T>
213
- implements
214
- EntryEncryptionTemplate<
215
- Metadata,
216
- Payload<T>,
217
- SignatureWithKey[],
218
- Array<string>
219
- >
237
+ implements EntryEncryptionTemplate<Meta, Payload<T>, SignatureWithKey[]>
220
238
  {
221
239
  @field({ type: MaybeEncrypted })
222
- _metadata: MaybeEncrypted<Metadata>;
240
+ _meta: MaybeEncrypted<Meta>;
223
241
 
224
242
  @field({ type: MaybeEncrypted })
225
243
  _payload: MaybeEncrypted<Payload<T>>;
226
244
 
227
- @field({ type: MaybeEncrypted })
228
- _next: MaybeEncrypted<StringArray>; // Array of hashes (the tree)
229
-
230
245
  @field({ type: fixedArray("u8", 4) })
231
- _reserved: Uint8Array;
246
+ _reserved?: Uint8Array;
232
247
 
233
248
  @field({ type: option(Signatures) })
234
249
  _signatures?: Signatures;
235
250
 
236
- @field({ type: option("string") }) // we do option because we serialize and store this in a block without the hash, to recieve the hash, which we later set
251
+ @field({ type: option("string") }) // we do option because we serialize and store this in a block without the hash, to receive the hash, which we later set
237
252
  hash: string; // "zd...Foo", we'll set the hash after persisting the entry
238
253
 
239
254
  createdLocally?: boolean;
@@ -244,17 +259,15 @@ export class Entry<T>
244
259
  constructor(obj: {
245
260
  payload: MaybeEncrypted<Payload<T>>;
246
261
  signatures?: Signatures;
247
- metadata: MaybeEncrypted<Metadata>;
248
- next: MaybeEncrypted<StringArray>;
262
+ meta: MaybeEncrypted<Meta>;
249
263
  reserved?: Uint8Array; // intentational type 0 (not used)h
250
264
  hash?: string;
251
265
  createdLocally?: boolean;
252
266
  }) {
253
- this._metadata = obj.metadata;
267
+ this._meta = obj.meta;
254
268
  this._payload = obj.payload;
255
269
  this._signatures = obj.signatures;
256
- this._next = obj.next;
257
- this._reserved = obj.reserved || new Uint8Array([0, 0, 0, 0]);
270
+ this._reserved = new Uint8Array([0, 0, 0, 0]);
258
271
  this.createdLocally = obj.createdLocally;
259
272
  }
260
273
 
@@ -283,32 +296,25 @@ export class Entry<T>
283
296
  return this._encoding;
284
297
  }
285
298
 
286
- get metadata(): Metadata {
287
- return this._metadata.decrypted.getValue(Metadata);
299
+ get meta(): Meta {
300
+ return this._meta.decrypted.getValue(Meta);
288
301
  }
289
302
 
290
- async getMetadata(): Promise<Metadata> {
291
- await this._metadata.decrypt(this._keychain);
292
- return this.metadata;
293
- }
294
-
295
- get gid(): string {
296
- return this.metadata.gid;
297
- }
298
- async getGid(): Promise<string> {
299
- return (await this.getMetadata()).gid;
303
+ async getMeta(): Promise<Meta> {
304
+ await this._meta.decrypt(this._keychain);
305
+ return this.meta;
300
306
  }
301
307
 
302
308
  async getClock(): Promise<Clock> {
303
- return (await this.getMetadata()).clock;
309
+ return (await this.getMeta()).clock;
304
310
  }
305
311
 
306
- get maxChainLength(): bigint {
307
- return this._metadata.decrypted.getValue(Metadata).maxChainLength;
312
+ get gid(): string {
313
+ return this._meta.decrypted.getValue(Meta).gid;
308
314
  }
309
315
 
310
- async getMaxChainLength(): Promise<bigint> {
311
- return (await this.getMetadata()).maxChainLength;
316
+ async getGid(): Promise<string> {
317
+ return (await this.getMeta()).gid;
312
318
  }
313
319
 
314
320
  get payload(): Payload<T> {
@@ -328,7 +334,7 @@ export class Entry<T>
328
334
 
329
335
  async getPayloadValue(): Promise<T> {
330
336
  const payload = await this.getPayload();
331
- return payload.getValue(this.encoding);
337
+ return payload.isDecoded ? payload.value : payload.getValue(this.encoding);
332
338
  }
333
339
 
334
340
  get publicKeys(): PublicSignKey[] {
@@ -341,12 +347,11 @@ export class Entry<T>
341
347
  }
342
348
 
343
349
  get next(): string[] {
344
- return this._next.decrypted.getValue(StringArray).arr;
350
+ return this.meta.next;
345
351
  }
346
352
 
347
353
  async getNext(): Promise<string[]> {
348
- await this._next.decrypt(this._keychain);
349
- return this.next;
354
+ return (await this.getMeta()).next;
350
355
  }
351
356
 
352
357
  /**
@@ -409,8 +414,7 @@ export class Entry<T>
409
414
  static toSignable(entry: Entry<any>): Uint8Array {
410
415
  // TODO fix types
411
416
  const trimmed = new Entry({
412
- metadata: entry._metadata,
413
- next: entry._next,
417
+ meta: entry._meta,
414
418
  payload: entry._payload,
415
419
  reserved: entry._reserved,
416
420
  signatures: undefined,
@@ -433,9 +437,8 @@ export class Entry<T>
433
437
  equals(other: Entry<T>) {
434
438
  return (
435
439
  equals(this._reserved, other._reserved) &&
436
- this._metadata.equals(other._metadata) &&
440
+ this._meta.equals(other._meta) &&
437
441
  this._signatures!.equals(other._signatures!) &&
438
- this._next.equals(other._next) &&
439
442
  this._payload.equals(other._payload)
440
443
  ); // dont compare hashes because the hash is a function of the other properties
441
444
  }
@@ -453,24 +456,30 @@ export class Entry<T>
453
456
 
454
457
  static async create<T>(properties: {
455
458
  store: Blocks;
456
- gid?: string;
457
- type?: EntryType;
458
- gidSeed?: Uint8Array;
459
459
  data: T;
460
+ meta?: {
461
+ clock?: Clock;
462
+ gid?: string;
463
+ type?: EntryType;
464
+ gidSeed?: Uint8Array;
465
+ data?: Uint8Array;
466
+ next?: Entry<T>[];
467
+ };
460
468
  encoding?: Encoding<T>;
461
469
  canAppend?: CanAppend<T>;
462
- next?: Entry<T>[];
463
- clock?: Clock;
464
470
  encryption?: EntryEncryption;
465
471
  identity: Identity;
466
472
  signers?: ((
467
473
  data: Uint8Array
468
474
  ) => Promise<SignatureWithKey> | SignatureWithKey)[];
469
475
  }): Promise<Entry<T>> {
470
- if (!properties.encoding || !properties.next) {
476
+ if (!properties.encoding || !properties?.meta?.next) {
471
477
  properties = {
472
478
  ...properties,
473
- next: properties.next ? properties.next : [],
479
+ meta: {
480
+ ...properties?.meta,
481
+ next: properties.meta?.next ? properties.meta?.next : [],
482
+ },
474
483
  encoding: properties.encoding ? properties.encoding : NO_ENCODING,
475
484
  };
476
485
  }
@@ -479,12 +488,12 @@ export class Entry<T>
479
488
  throw new Error("Missing encoding options");
480
489
  }
481
490
 
482
- if (!isDefined(properties.data)) throw new Error("Entry requires data");
483
- if (!isDefined(properties.next) || !Array.isArray(properties.next))
491
+ if (properties.data == null) throw new Error("Entry requires data");
492
+ if (properties.meta?.next == null || !Array.isArray(properties.meta.next))
484
493
  throw new Error("'next' argument is not an array");
485
494
 
486
495
  // Clean the next objects and convert to hashes
487
- const nexts = properties.next;
496
+ const nexts = properties.meta?.next;
488
497
 
489
498
  const payloadToSave = new Payload<T>({
490
499
  data: properties.encoding.encoder(properties.data),
@@ -492,19 +501,19 @@ export class Entry<T>
492
501
  encoding: properties.encoding,
493
502
  });
494
503
 
495
- let clock: Clock | undefined = properties.clock;
504
+ let clock: Clock | undefined = properties.meta?.clock;
496
505
  if (!clock) {
497
506
  const hlc = new HLC();
498
507
  for (const next of nexts) {
499
- hlc.update(next.metadata.clock.timestamp);
508
+ hlc.update(next.meta.clock.timestamp);
500
509
  }
501
510
 
502
511
  if (
503
- properties.encryption?.reciever.signatures &&
504
- properties.encryption?.reciever.metadata
512
+ properties.encryption?.receiver.signatures &&
513
+ properties.encryption?.receiver.meta
505
514
  ) {
506
515
  throw new Error(
507
- "Signature is to be encrypted yet the clock is not, which contains the publicKey as id. Either provide a custom Clock value that is not sensitive or set the reciever (encryption target) for the clock"
516
+ "Signature is to be encrypted yet the clock is not, which contains the publicKey as id. Either provide a custom Clock value that is not sensitive or set the receiver (encryption target) for the clock"
508
517
  );
509
518
  }
510
519
  clock = new Clock({
@@ -515,10 +524,10 @@ export class Entry<T>
515
524
  const cv = clock;
516
525
  // check if nexts, that all nexts are happening BEFORE this clock value (else clock make no sense)
517
526
  for (const n of nexts) {
518
- if (Timestamp.compare(n.metadata.clock.timestamp, cv.timestamp) >= 0) {
527
+ if (Timestamp.compare(n.meta.clock.timestamp, cv.timestamp) >= 0) {
519
528
  throw new Error(
520
529
  "Expecting next(s) to happen before entry, got: " +
521
- n.metadata.clock.timestamp +
530
+ n.meta.clock.timestamp +
522
531
  " > " +
523
532
  cv.timestamp
524
533
  );
@@ -529,82 +538,57 @@ export class Entry<T>
529
538
  const payload = await maybeEncrypt(
530
539
  payloadToSave,
531
540
  properties.encryption?.keypair,
532
- properties.encryption?.reciever.payload
541
+ properties.encryption?.receiver.payload
533
542
  );
534
543
 
535
544
  const nextHashes: string[] = [];
536
- let gid!: string;
537
545
  let maxChainLength = 0n;
538
- const maxClock = new Timestamp({ wallTime: 0n, logical: 0 });
546
+ let gid: string | null = null;
539
547
  if (nexts?.length > 0) {
540
548
  // take min gid as our gid
549
+ if (properties.meta?.gid) {
550
+ throw new Error(
551
+ "Expecting '.meta.gid' property to be undefined if '.meta.next' is provided"
552
+ );
553
+ }
541
554
  for (const n of nexts) {
542
555
  if (!n.hash) {
543
556
  throw new Error("Expecting hash to be defined to next entries");
544
557
  }
545
558
  nextHashes.push(n.hash);
546
- if (
547
- maxChainLength < n.maxChainLength ||
548
- maxChainLength == n.maxChainLength
549
- ) {
550
- maxChainLength = n.maxChainLength;
551
- if (!gid) {
552
- gid = n.metadata.gid;
553
- continue;
554
- }
555
- // replace gid if next is from alonger chain, or from a later time, or same time but "smaller" gid
556
- else if (
557
- Timestamp.compare(n.metadata.clock.timestamp, maxClock) > 0 ||
558
- (Timestamp.compare(n.metadata.clock.timestamp, maxClock) == 0 &&
559
- n.metadata.gid < gid)
560
- ) {
561
- gid = n.metadata.gid;
562
- }
563
- }
564
- }
565
- if (!gid) {
566
- throw new Error("Unexpected behaviour, could not find gid");
559
+ gid =
560
+ gid == null
561
+ ? n.meta.gid
562
+ : n.meta.gid < (gid as string)
563
+ ? n.meta.gid
564
+ : gid;
567
565
  }
568
566
  } else {
569
- gid = properties.gid || (await Entry.createGid(properties.gidSeed));
567
+ gid =
568
+ properties.meta?.gid ||
569
+ (await Entry.createGid(properties.meta?.gidSeed));
570
570
  }
571
571
 
572
572
  maxChainLength += 1n; // include this
573
573
 
574
574
  const metadataEncrypted = await maybeEncrypt(
575
- new Metadata({
576
- maxChainLength,
575
+ new Meta({
577
576
  clock,
578
- gid,
579
- type: properties.type ?? EntryType.APPEND,
577
+ gid: gid!,
578
+ type: properties.meta?.type ?? EntryType.APPEND,
579
+ data: properties.meta?.data,
580
+ next: nextHashes,
580
581
  }),
581
582
  properties.encryption?.keypair,
582
- properties.encryption?.reciever.metadata
583
- );
584
-
585
- const next = nextHashes;
586
- next?.forEach((next) => {
587
- if (typeof next !== "string") {
588
- throw new Error("Unsupported next type");
589
- }
590
- });
591
-
592
- const nextEncrypted = await maybeEncrypt(
593
- new StringArray({
594
- arr: next,
595
- }),
596
- properties.encryption?.keypair,
597
- properties.encryption?.reciever.next
583
+ properties.encryption?.receiver.meta
598
584
  );
599
585
 
600
586
  // Sign id, encrypted payload, clock, nexts, refs
601
587
  const entry: Entry<T> = new Entry<T>({
602
588
  payload,
603
- metadata: metadataEncrypted,
589
+ meta: metadataEncrypted,
604
590
  signatures: undefined,
605
591
  createdLocally: true,
606
- next: nextEncrypted, // Array of hashes
607
- /* refs: properties.refs, */
608
592
  });
609
593
 
610
594
  const signers = properties.signers || [
@@ -618,12 +602,13 @@ export class Entry<T>
618
602
 
619
603
  const encryptedSignatures: MaybeEncrypted<SignatureWithKey>[] = [];
620
604
  const encryptAllSignaturesWithSameKey = isMaybeEryptionPublicKey(
621
- properties.encryption?.reciever?.signatures
605
+ properties.encryption?.receiver?.signatures
622
606
  );
607
+
623
608
  for (const signature of signatures) {
624
609
  const encryptionRecievers = encryptAllSignaturesWithSameKey
625
- ? properties.encryption?.reciever?.signatures
626
- : properties.encryption?.reciever?.signatures?.[
610
+ ? properties.encryption?.receiver?.signatures
611
+ : properties.encryption?.receiver?.signatures?.[
627
612
  signature.publicKey.hashcode()
628
613
  ];
629
614
  const signatureEncrypted = await maybeEncrypt(
@@ -650,6 +635,24 @@ export class Entry<T>
650
635
  return entry;
651
636
  }
652
637
 
638
+ get payloadByteLength() {
639
+ return this._payload.byteLength;
640
+ }
641
+
642
+ toShallow(): ShallowEntry {
643
+ return {
644
+ hash: this.hash,
645
+ payloadByteLength: this._payload.byteLength,
646
+ meta: {
647
+ gid: this.meta.gid,
648
+ data: this.meta.data,
649
+ clock: this.meta.clock,
650
+ next: this.meta.next,
651
+ type: this.meta.type,
652
+ },
653
+ };
654
+ }
655
+
653
656
  /**
654
657
  * Get the multihash of an Entry.
655
658
  * @example
@@ -695,8 +698,8 @@ export class Entry<T>
695
698
  * @returns {number} 1 if a is greater, -1 is b is greater
696
699
  */
697
700
  static compare<T>(a: Entry<T>, b: Entry<T>) {
698
- const aClock = a.metadata.clock;
699
- const bClock = b.metadata.clock;
701
+ const aClock = a.meta.clock;
702
+ const bClock = b.meta.clock;
700
703
  const distance = Clock.compare(aClock, bClock);
701
704
  if (distance === 0) return aClock.id < bClock.id ? -1 : 1;
702
705
  return distance;
@@ -741,9 +744,7 @@ export class Entry<T>
741
744
  prev = parent;
742
745
  parent = values.find((e) => Entry.isDirectParent(prev, e));
743
746
  }
744
- stack = stack.sort((a, b) =>
745
- Clock.compare(a.metadata.clock, b.metadata.clock)
746
- );
747
+ stack = stack.sort((a, b) => Clock.compare(a.meta.clock, b.meta.clock));
747
748
  return stack;
748
749
  }
749
750
  }
@@ -12,7 +12,7 @@ export class CachedValue {}
12
12
  /* export type AppendOptions<T> = {
13
13
  signers?: ((data: Uint8Array) => Promise<SignatureWithKey>)[];
14
14
  nexts?: Entry<T>[];
15
- reciever?: EncryptionTemplateMaybeEncrypted;
15
+ receiver?: EncryptionTemplateMaybeEncrypted;
16
16
  type?: EntryType;
17
17
  };
18
18
  */