@graffiti-garden/implementation-decentralized 0.0.3 → 0.0.5

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.
@@ -105,6 +105,7 @@ const MESSAGE_LABEL_UNLABELED = 0;
105
105
  const MESSAGE_LABEL_VALID = 1;
106
106
  const MESSAGE_LABEL_TRASH = 2;
107
107
  const MESSAGE_LABEL_INVALID = 3;
108
+ const CONCURRENCY = 16;
108
109
  class GraffitiDecentralized {
109
110
  dids = new DecentralizedIdentifiers();
110
111
  authorization = new Authorization();
@@ -547,82 +548,90 @@ class GraffitiDecentralized {
547
548
  );
548
549
  let indexedIteratorNexts = iterators.map(async (it, index) => indexedSingleEndpointQueryNext(it, index));
549
550
  let active = indexedIteratorNexts.length;
550
- while (active > 0) {
551
- const next = await Promise.race(indexedIteratorNexts);
552
- if (next.error !== void 0) {
553
- indexedIteratorNexts[next.index] = new Promise(() => {
554
- });
555
- active--;
556
- yield {
557
- error: next.error,
558
- origin: allInboxes[next.index].serviceEndpoint
559
- };
560
- } else if (next.result.done) {
561
- const inbox = allInboxes[next.index];
562
- cursors[inbox.serviceEndpoint] = next.result.value;
563
- indexedIteratorNexts[next.index] = new Promise(() => {
564
- });
565
- active--;
566
- } else {
567
- indexedIteratorNexts[next.index] = indexedSingleEndpointQueryNext(
568
- iterators[next.index],
569
- next.index
570
- );
571
- const { object, tombstone, tags: receivedTags } = next.result.value;
572
- if (tombstone) {
573
- if (tombstones.get(object.url) === true) continue;
574
- tombstones.set(object.url, true);
551
+ try {
552
+ while (active > 0) {
553
+ const next = await Promise.race(indexedIteratorNexts);
554
+ if (next.error !== void 0) {
555
+ indexedIteratorNexts[next.index] = new Promise(() => {
556
+ });
557
+ active--;
575
558
  yield {
576
- tombstone,
577
- object: { url: object.url }
559
+ error: next.error,
560
+ origin: allInboxes[next.index].serviceEndpoint
578
561
  };
562
+ } else if (next.result.done) {
563
+ const inbox = allInboxes[next.index];
564
+ cursors[inbox.serviceEndpoint] = next.result.value;
565
+ indexedIteratorNexts[next.index] = new Promise(() => {
566
+ });
567
+ active--;
579
568
  } else {
580
- if (tombstones.get(object.url) === false) continue;
581
- const matchedTagIndices = tags.reduce(
582
- (acc, tag, tagIndex) => {
583
- for (const receivedTag of receivedTags) {
584
- if (tag.length === receivedTag.length && tag.every((b, i) => receivedTag[i] === b)) {
585
- acc.push(tagIndex);
586
- break;
587
- }
588
- }
589
- return acc;
590
- },
591
- []
569
+ indexedIteratorNexts[next.index] = indexedSingleEndpointQueryNext(
570
+ iterators[next.index],
571
+ next.index
592
572
  );
593
- const matchedChannels = matchedTagIndices.map(
594
- (index) => channels[index]
595
- );
596
- if (matchedChannels.length === 0) {
573
+ const { object, tombstone, tags: receivedTags } = next.result.value;
574
+ if (tombstone) {
575
+ if (tombstones.get(object.url) === true) continue;
576
+ tombstones.set(object.url, true);
597
577
  yield {
598
- error: new Error(
599
- "Inbox returned object without matching channels"
600
- ),
601
- origin: allInboxes[next.index].serviceEndpoint
578
+ tombstone,
579
+ object: { url: object.url }
602
580
  };
603
- }
604
- tombstones.set(object.url, false);
605
- yield {
606
- object: {
607
- ...object,
608
- channels: matchedChannels
581
+ } else {
582
+ if (tombstones.get(object.url) === false) continue;
583
+ const matchedTagIndices = tags.reduce(
584
+ (acc, tag, tagIndex) => {
585
+ for (const receivedTag of receivedTags) {
586
+ if (tag.length === receivedTag.length && tag.every((b, i) => receivedTag[i] === b)) {
587
+ acc.push(tagIndex);
588
+ break;
589
+ }
590
+ }
591
+ return acc;
592
+ },
593
+ []
594
+ );
595
+ const matchedChannels = matchedTagIndices.map(
596
+ (index) => channels[index]
597
+ );
598
+ if (matchedChannels.length === 0) {
599
+ yield {
600
+ error: new Error(
601
+ "Inbox returned object without matching channels"
602
+ ),
603
+ origin: allInboxes[next.index].serviceEndpoint
604
+ };
609
605
  }
610
- };
606
+ tombstones.set(object.url, false);
607
+ yield {
608
+ object: {
609
+ ...object,
610
+ channels: matchedChannels
611
+ }
612
+ };
613
+ }
611
614
  }
612
615
  }
616
+ } finally {
617
+ await Promise.all(
618
+ iterators.map(async (it) => {
619
+ await it.return("");
620
+ })
621
+ );
613
622
  }
614
623
  return {
615
624
  cursor: JSON.stringify({
616
625
  channels,
617
626
  cursors
618
- }),
619
- continue: (session2) => this.discoverMeta(channels, schema, cursors, session2)
627
+ })
620
628
  };
621
629
  }
622
630
  discover = (...args) => {
623
631
  const [channels, schema, session] = args;
624
632
  return this.discoverMeta(channels, schema, {}, session);
625
633
  };
634
+ // @ts-ignore
626
635
  continueDiscover = (...args) => {
627
636
  const [cursor, session] = args;
628
637
  let channels;
@@ -769,62 +778,164 @@ class GraffitiDecentralized {
769
778
  queryArguments.cursor,
770
779
  inboxToken
771
780
  );
772
- while (true) {
773
- const itResult = await iterator.next();
774
- if (itResult.done) return itResult.value;
775
- const result = itResult.value;
776
- const label = result.l;
777
- if (label !== MESSAGE_LABEL_VALID && label !== MESSAGE_LABEL_UNLABELED && label !== MESSAGE_LABEL_TRASH)
778
- continue;
779
- const messageId = result.id;
780
- const { o: object, m: metadataBytes, t: receivedTags } = result.m;
781
- let metadata;
782
- try {
783
- const metadataRaw = dagCborDecode(metadataBytes);
784
- metadata = MessageMetadataSchema.parse(metadataRaw);
785
- } catch (e) {
781
+ const inFlight = [];
782
+ let doneValue = null;
783
+ try {
784
+ while (true) {
785
+ while (doneValue === null && inFlight.length < CONCURRENCY) {
786
+ const itResult = await iterator.next();
787
+ if (itResult.done) {
788
+ doneValue = itResult.value;
789
+ break;
790
+ }
791
+ const processPromise = this.processOneLabeledMessage(
792
+ inboxEndpoint,
793
+ itResult.value,
794
+ inboxToken,
795
+ recipient
796
+ ).catch((e) => {
797
+ throw e;
798
+ });
799
+ inFlight.push(processPromise);
800
+ }
801
+ const nextProcessedPromise = inFlight.shift();
802
+ if (!nextProcessedPromise) {
803
+ if (doneValue !== null) return doneValue;
804
+ throw new Error("Process queue empty but no return value");
805
+ }
806
+ const processed = await nextProcessedPromise;
807
+ if (processed) yield processed;
808
+ }
809
+ } finally {
810
+ await iterator.return("");
811
+ }
812
+ }
813
+ async processOneLabeledMessage(inboxEndpoint, result, inboxToken, recipient) {
814
+ const label = result.l;
815
+ if (label !== MESSAGE_LABEL_VALID && label !== MESSAGE_LABEL_UNLABELED && label !== MESSAGE_LABEL_TRASH)
816
+ return;
817
+ const messageId = result.id;
818
+ const { o: object, m: metadataBytes, t: receivedTags } = result.m;
819
+ let metadata;
820
+ try {
821
+ const metadataRaw = dagCborDecode(metadataBytes);
822
+ metadata = MessageMetadataSchema.parse(metadataRaw);
823
+ } catch (e) {
824
+ this.inboxes.label(
825
+ inboxEndpoint,
826
+ messageId,
827
+ MESSAGE_LABEL_INVALID,
828
+ inboxToken
829
+ );
830
+ return;
831
+ }
832
+ const {
833
+ [MESSAGE_DATA_STORAGE_BUCKET_KEY]: storageBucketKey,
834
+ [MESSAGE_DATA_TOMBSTONED_MESSAGE_ID_KEY]: tombstonedMessageId
835
+ } = metadata;
836
+ const allowedTickets = MESSAGE_DATA_ALLOWED_TICKETS_KEY in metadata ? metadata[MESSAGE_DATA_ALLOWED_TICKETS_KEY] : void 0;
837
+ const announcements = MESSAGE_DATA_ANNOUNCEMENTS_KEY in metadata ? metadata[MESSAGE_DATA_ANNOUNCEMENTS_KEY] : void 0;
838
+ if (label === MESSAGE_LABEL_VALID) {
839
+ return {
840
+ messageId,
841
+ object,
842
+ storageBucketKey,
843
+ allowedTickets,
844
+ tags: receivedTags,
845
+ announcements
846
+ };
847
+ } else if (label === MESSAGE_LABEL_TRASH) {
848
+ if (!tombstonedMessageId) return;
849
+ const past = await this.inboxes.get(
850
+ inboxEndpoint,
851
+ tombstonedMessageId,
852
+ inboxToken
853
+ );
854
+ if (!past || past[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url !== object.url)
855
+ return;
856
+ if (past[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH) {
786
857
  this.inboxes.label(
787
858
  inboxEndpoint,
788
- messageId,
789
- MESSAGE_LABEL_INVALID,
859
+ tombstonedMessageId,
860
+ MESSAGE_LABEL_TRASH,
790
861
  inboxToken
791
862
  );
792
- continue;
793
863
  }
794
- const {
795
- [MESSAGE_DATA_STORAGE_BUCKET_KEY]: storageBucketKey,
796
- [MESSAGE_DATA_TOMBSTONED_MESSAGE_ID_KEY]: tombstonedMessageId
797
- } = metadata;
798
- const allowedTickets = MESSAGE_DATA_ALLOWED_TICKETS_KEY in metadata ? metadata[MESSAGE_DATA_ALLOWED_TICKETS_KEY] : void 0;
799
- const announcements = MESSAGE_DATA_ANNOUNCEMENTS_KEY in metadata ? metadata[MESSAGE_DATA_ANNOUNCEMENTS_KEY] : void 0;
800
- if (label === MESSAGE_LABEL_VALID) {
801
- yield {
802
- messageId,
803
- object,
804
- storageBucketKey,
805
- allowedTickets,
806
- tags: receivedTags,
807
- announcements
808
- };
809
- continue;
810
- } else if (label === MESSAGE_LABEL_TRASH) {
811
- if (!tombstonedMessageId) continue;
812
- const past = await this.inboxes.get(
813
- inboxEndpoint,
814
- tombstonedMessageId,
815
- inboxToken
864
+ return {
865
+ messageId,
866
+ tombstone: true,
867
+ object,
868
+ storageBucketKey,
869
+ allowedTickets,
870
+ tags: receivedTags,
871
+ announcements
872
+ };
873
+ }
874
+ let validationError = void 0;
875
+ try {
876
+ const actor = object.actor;
877
+ const actorDocument = await this.dids.resolve(actor);
878
+ const storageBucketService = actorDocument?.service?.find(
879
+ (service) => service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET && service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET
880
+ );
881
+ if (!storageBucketService) {
882
+ throw new GraffitiErrorNotFound(
883
+ `Actor ${actor} has no storage bucket service`
816
884
  );
817
- if (!past || past[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url !== object.url)
818
- continue;
819
- if (past[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH) {
885
+ }
886
+ if (typeof storageBucketService.serviceEndpoint !== "string") {
887
+ throw new GraffitiErrorNotFound(
888
+ `Actor ${actor} does not have a valid storage bucket endpoint`
889
+ );
890
+ }
891
+ const storageBucketEndpoint = storageBucketService.serviceEndpoint;
892
+ const objectBytes = await this.storageBuckets.get(
893
+ storageBucketEndpoint,
894
+ storageBucketKey,
895
+ MAX_OBJECT_SIZE_BYTES
896
+ );
897
+ if (MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata && !recipient) {
898
+ throw new GraffitiErrorForbidden(
899
+ `Recipient is required when allowed ticket is present`
900
+ );
901
+ }
902
+ const privateObjectInfo = allowedTickets ? { allowedTickets } : MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata ? {
903
+ recipient: recipient ?? "null",
904
+ allowedTicket: metadata[MESSAGE_DATA_ALLOWED_TICKET_KEY],
905
+ allowedIndex: metadata[MESSAGE_DATA_ALLOWED_TICKET_INDEX_KEY]
906
+ } : void 0;
907
+ await this.objectEncoding.validate(
908
+ object,
909
+ receivedTags,
910
+ objectBytes,
911
+ privateObjectInfo
912
+ );
913
+ } catch (e) {
914
+ validationError = e;
915
+ }
916
+ if (tombstonedMessageId) {
917
+ if (validationError instanceof GraffitiErrorNotFound) {
918
+ this.inboxes.get(inboxEndpoint, tombstonedMessageId, inboxToken).then((result2) => {
919
+ if (
920
+ // Make sure that it actually references the object being deleted
921
+ result2 && result2[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url === object.url && // And that the object is not already marked as trash
922
+ result2[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH
923
+ ) {
924
+ this.inboxes.label(
925
+ inboxEndpoint,
926
+ tombstonedMessageId,
927
+ MESSAGE_LABEL_TRASH,
928
+ inboxToken
929
+ );
930
+ }
820
931
  this.inboxes.label(
821
932
  inboxEndpoint,
822
- tombstonedMessageId,
933
+ messageId,
823
934
  MESSAGE_LABEL_TRASH,
824
935
  inboxToken
825
936
  );
826
- }
827
- yield {
937
+ });
938
+ return {
828
939
  messageId,
829
940
  tombstone: true,
830
941
  object,
@@ -833,117 +944,48 @@ class GraffitiDecentralized {
833
944
  tags: receivedTags,
834
945
  announcements
835
946
  };
836
- continue;
837
- }
838
- let validationError = void 0;
839
- try {
840
- const actor = object.actor;
841
- const actorDocument = await this.dids.resolve(actor);
842
- const storageBucketService = actorDocument?.service?.find(
843
- (service) => service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET && service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET
947
+ } else {
948
+ console.error("Recieved an incorrect tombstone object");
949
+ console.error(validationError);
950
+ this.inboxes.label(
951
+ inboxEndpoint,
952
+ messageId,
953
+ MESSAGE_LABEL_INVALID,
954
+ inboxToken
844
955
  );
845
- if (!storageBucketService) {
846
- throw new GraffitiErrorNotFound(
847
- `Actor ${actor} has no storage bucket service`
848
- );
849
- }
850
- if (typeof storageBucketService.serviceEndpoint !== "string") {
851
- throw new GraffitiErrorNotFound(
852
- `Actor ${actor} does not have a valid storage bucket endpoint`
853
- );
854
- }
855
- const storageBucketEndpoint = storageBucketService.serviceEndpoint;
856
- const objectBytes = await this.storageBuckets.get(
857
- storageBucketEndpoint,
858
- storageBucketKey,
859
- MAX_OBJECT_SIZE_BYTES
956
+ }
957
+ } else {
958
+ if (validationError === void 0) {
959
+ this.inboxes.label(
960
+ inboxEndpoint,
961
+ messageId,
962
+ MESSAGE_LABEL_VALID,
963
+ inboxToken
860
964
  );
861
- if (MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata && !recipient) {
862
- throw new GraffitiErrorForbidden(
863
- `Recipient is required when allowed ticket is present`
864
- );
865
- }
866
- const privateObjectInfo = allowedTickets ? { allowedTickets } : MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata ? {
867
- recipient: recipient ?? "null",
868
- allowedTicket: metadata[MESSAGE_DATA_ALLOWED_TICKET_KEY],
869
- allowedIndex: metadata[MESSAGE_DATA_ALLOWED_TICKET_INDEX_KEY]
870
- } : void 0;
871
- await this.objectEncoding.validate(
965
+ return {
966
+ messageId,
872
967
  object,
873
- receivedTags,
874
- objectBytes,
875
- privateObjectInfo
968
+ storageBucketKey,
969
+ tags: receivedTags,
970
+ allowedTickets,
971
+ announcements
972
+ };
973
+ } else if (validationError instanceof GraffitiErrorNotFound) {
974
+ this.inboxes.label(
975
+ inboxEndpoint,
976
+ messageId,
977
+ MESSAGE_LABEL_TRASH,
978
+ inboxToken
876
979
  );
877
- } catch (e) {
878
- validationError = e;
879
- }
880
- if (tombstonedMessageId) {
881
- if (validationError instanceof GraffitiErrorNotFound) {
882
- this.inboxes.get(inboxEndpoint, tombstonedMessageId, inboxToken).then((result2) => {
883
- if (
884
- // Make sure that it actually references the object being deleted
885
- result2 && result2[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url === object.url && // And that the object is not already marked as trash
886
- result2[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH
887
- ) {
888
- this.inboxes.label(
889
- inboxEndpoint,
890
- tombstonedMessageId,
891
- MESSAGE_LABEL_TRASH,
892
- inboxToken
893
- );
894
- }
895
- this.inboxes.label(
896
- inboxEndpoint,
897
- messageId,
898
- MESSAGE_LABEL_TRASH,
899
- inboxToken
900
- );
901
- });
902
- yield {
903
- messageId,
904
- tombstone: true,
905
- object,
906
- storageBucketKey,
907
- allowedTickets,
908
- tags: receivedTags,
909
- announcements
910
- };
911
- } else {
912
- console.error("Recieved an incorrect object");
913
- console.error(validationError);
914
- this.inboxes.label(
915
- inboxEndpoint,
916
- messageId,
917
- MESSAGE_LABEL_INVALID,
918
- inboxToken
919
- );
920
- }
921
980
  } else {
922
- if (validationError === void 0) {
923
- this.inboxes.label(
924
- inboxEndpoint,
925
- messageId,
926
- MESSAGE_LABEL_VALID,
927
- inboxToken
928
- );
929
- yield {
930
- messageId,
931
- object,
932
- storageBucketKey,
933
- tags: receivedTags,
934
- allowedTickets,
935
- announcements
936
- };
937
- } else {
938
- console.error("Recieved an incorrect object");
939
- console.error(validationError);
940
- this.inboxes.label(
941
- inboxEndpoint,
942
- messageId,
943
- MESSAGE_LABEL_INVALID,
944
- inboxToken
945
- );
946
- }
981
+ console.error("Recieved an incorrect object");
982
+ console.error(validationError);
983
+ this.inboxes.label(
984
+ inboxEndpoint,
985
+ messageId,
986
+ MESSAGE_LABEL_INVALID,
987
+ inboxToken
988
+ );
947
989
  }
948
990
  }
949
991
  }