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