@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.
@@ -34,6 +34,7 @@ import {
34
34
  MESSAGE_METADATA_KEY,
35
35
  MESSAGE_OBJECT_KEY,
36
36
  MESSAGE_TAGS_KEY,
37
+ type LabeledMessage,
37
38
  type MessageStream,
38
39
  } from "../1-services/4-inboxes";
39
40
 
@@ -131,6 +132,8 @@ export interface GraffitiDecentralizedOptions {
131
132
  defaultInboxEndpoints?: string[];
132
133
  }
133
134
 
135
+ const CONCURRENCY = 16;
136
+
134
137
  export class GraffitiDecentralized implements Graffiti {
135
138
  protected readonly dids = new DecentralizedIdentifiers();
136
139
  protected readonly authorization = new Authorization();
@@ -705,79 +708,87 @@ export class GraffitiDecentralized implements Graffiti {
705
708
  >(async (it, index) => indexedSingleEndpointQueryNext<Schema>(it, index));
706
709
  let active = indexedIteratorNexts.length;
707
710
 
708
- while (active > 0) {
709
- const next: IndexedSingleEndpointQueryResult<Schema> =
710
- await Promise.race<any>(indexedIteratorNexts);
711
- if (next.error !== undefined) {
712
- // Remove it from the race
713
- indexedIteratorNexts[next.index] = new Promise(() => {});
714
- active--;
715
- yield {
716
- error: next.error,
717
- origin: allInboxes[next.index].serviceEndpoint,
718
- };
719
- } else if (next.result.done) {
720
- // Store the cursor for future use
721
- const inbox = allInboxes[next.index];
722
- cursors[inbox.serviceEndpoint] = next.result.value;
723
- // Remove it from the race
724
- indexedIteratorNexts[next.index] = new Promise(() => {});
725
- active--;
726
- } else {
727
- // Re-arm the iterator
728
- indexedIteratorNexts[next.index] =
729
- indexedSingleEndpointQueryNext<Schema>(
730
- iterators[next.index],
731
- next.index,
732
- );
733
- const { object, tombstone, tags: receivedTags } = next.result.value;
734
- if (tombstone) {
735
- if (tombstones.get(object.url) === true) continue;
736
- tombstones.set(object.url, true);
711
+ try {
712
+ while (active > 0) {
713
+ const next: IndexedSingleEndpointQueryResult<Schema> =
714
+ await Promise.race<any>(indexedIteratorNexts);
715
+ if (next.error !== undefined) {
716
+ // Remove it from the race
717
+ indexedIteratorNexts[next.index] = new Promise(() => {});
718
+ active--;
737
719
  yield {
738
- tombstone,
739
- object: { url: object.url },
720
+ error: next.error,
721
+ origin: allInboxes[next.index].serviceEndpoint,
740
722
  };
723
+ } else if (next.result.done) {
724
+ // Store the cursor for future use
725
+ const inbox = allInboxes[next.index];
726
+ cursors[inbox.serviceEndpoint] = next.result.value;
727
+ // Remove it from the race
728
+ indexedIteratorNexts[next.index] = new Promise(() => {});
729
+ active--;
741
730
  } else {
742
- // Filter already seen
743
- if (tombstones.get(object.url) === false) continue;
744
-
745
- // Fill in the matched channels
746
- const matchedTagIndices = tags.reduce<number[]>(
747
- (acc, tag, tagIndex) => {
748
- for (const receivedTag of receivedTags) {
749
- if (
750
- tag.length === receivedTag.length &&
751
- tag.every((b, i) => receivedTag[i] === b)
752
- ) {
753
- acc.push(tagIndex);
754
- break;
731
+ // Re-arm the iterator
732
+ indexedIteratorNexts[next.index] =
733
+ indexedSingleEndpointQueryNext<Schema>(
734
+ iterators[next.index],
735
+ next.index,
736
+ );
737
+ const { object, tombstone, tags: receivedTags } = next.result.value;
738
+ if (tombstone) {
739
+ if (tombstones.get(object.url) === true) continue;
740
+ tombstones.set(object.url, true);
741
+ yield {
742
+ tombstone,
743
+ object: { url: object.url },
744
+ };
745
+ } else {
746
+ // Filter already seen
747
+ if (tombstones.get(object.url) === false) continue;
748
+
749
+ // Fill in the matched channels
750
+ const matchedTagIndices = tags.reduce<number[]>(
751
+ (acc, tag, tagIndex) => {
752
+ for (const receivedTag of receivedTags) {
753
+ if (
754
+ tag.length === receivedTag.length &&
755
+ tag.every((b, i) => receivedTag[i] === b)
756
+ ) {
757
+ acc.push(tagIndex);
758
+ break;
759
+ }
755
760
  }
756
- }
757
- return acc;
758
- },
759
- [],
760
- );
761
- const matchedChannels = matchedTagIndices.map(
762
- (index) => channels[index],
763
- );
764
- if (matchedChannels.length === 0) {
761
+ return acc;
762
+ },
763
+ [],
764
+ );
765
+ const matchedChannels = matchedTagIndices.map(
766
+ (index) => channels[index],
767
+ );
768
+ if (matchedChannels.length === 0) {
769
+ yield {
770
+ error: new Error(
771
+ "Inbox returned object without matching channels",
772
+ ),
773
+ origin: allInboxes[next.index].serviceEndpoint,
774
+ };
775
+ }
776
+ tombstones.set(object.url, false);
765
777
  yield {
766
- error: new Error(
767
- "Inbox returned object without matching channels",
768
- ),
769
- origin: allInboxes[next.index].serviceEndpoint,
778
+ object: {
779
+ ...object,
780
+ channels: matchedChannels,
781
+ },
770
782
  };
771
783
  }
772
- tombstones.set(object.url, false);
773
- yield {
774
- object: {
775
- ...object,
776
- channels: matchedChannels,
777
- },
778
- };
779
784
  }
780
785
  }
786
+ } finally {
787
+ await Promise.all(
788
+ iterators.map<Promise<void>>(async (it) => {
789
+ await it.return("");
790
+ }),
791
+ );
781
792
  }
782
793
 
783
794
  return {
@@ -785,8 +796,6 @@ export class GraffitiDecentralized implements Graffiti {
785
796
  channels,
786
797
  cursors,
787
798
  } satisfies infer_<typeof CursorSchema>),
788
- continue: (session) =>
789
- this.discoverMeta<Schema>(channels, schema, cursors, session),
790
799
  };
791
800
  }
792
801
 
@@ -795,6 +804,7 @@ export class GraffitiDecentralized implements Graffiti {
795
804
  return this.discoverMeta<(typeof args)[1]>(channels, schema, {}, session);
796
805
  };
797
806
 
807
+ // @ts-ignore
798
808
  continueDiscover: Graffiti["continueDiscover"] = (...args) => {
799
809
  const [cursor, session] = args;
800
810
  // Extract the channels from the cursor
@@ -1009,237 +1019,281 @@ export class GraffitiDecentralized implements Graffiti {
1009
1019
  inboxToken,
1010
1020
  ) as unknown as MessageStream<Schema>);
1011
1021
 
1012
- while (true) {
1013
- const itResult = await iterator.next();
1014
- // Return the cursor if done
1015
- if (itResult.done) return itResult.value;
1022
+ const inFlight: Promise<SingleEndpointQueryResult<Schema> | void>[] = [];
1023
+ let doneValue: string | null = null;
1024
+
1025
+ try {
1026
+ while (true) {
1027
+ while (doneValue === null && inFlight.length < CONCURRENCY) {
1028
+ const itResult = await iterator.next();
1029
+ if (itResult.done) {
1030
+ doneValue = itResult.value;
1031
+ break;
1032
+ }
1033
+
1034
+ const processPromise = this.processOneLabeledMessage<Schema>(
1035
+ inboxEndpoint,
1036
+ itResult.value,
1037
+ inboxToken,
1038
+ recipient,
1039
+ ).catch((e) => {
1040
+ throw e;
1041
+ });
1042
+
1043
+ inFlight.push(processPromise);
1044
+ }
1016
1045
 
1017
- const result = itResult.value;
1046
+ const nextProcessedPromise = inFlight.shift();
1018
1047
 
1019
- const label = result.l;
1020
- // Anything invalid or unexpected, we can skip
1048
+ if (!nextProcessedPromise) {
1049
+ if (doneValue !== null) return doneValue;
1050
+
1051
+ throw new Error("Process queue empty but no return value");
1052
+ }
1053
+
1054
+ const processed = await nextProcessedPromise;
1055
+ if (processed) yield processed;
1056
+ }
1057
+ } finally {
1058
+ await iterator.return("");
1059
+ }
1060
+ }
1061
+
1062
+ protected async processOneLabeledMessage<Schema extends JSONSchema>(
1063
+ inboxEndpoint: string,
1064
+ result: LabeledMessage<Schema>,
1065
+ inboxToken?: string | null,
1066
+ recipient?: string | null,
1067
+ ): Promise<SingleEndpointQueryResult<Schema> | void> {
1068
+ const label = result.l;
1069
+ // Anything invalid or unexpected, we can skip
1070
+ if (
1071
+ label !== MESSAGE_LABEL_VALID &&
1072
+ label !== MESSAGE_LABEL_UNLABELED &&
1073
+ label !== MESSAGE_LABEL_TRASH
1074
+ )
1075
+ return;
1076
+
1077
+ const messageId = result.id;
1078
+ const { o: object, m: metadataBytes, t: receivedTags } = result.m;
1079
+
1080
+ let metadata: MessageMetadata;
1081
+ try {
1082
+ const metadataRaw = dagCborDecode(metadataBytes);
1083
+ metadata = MessageMetadataSchema.parse(metadataRaw);
1084
+ } catch (e) {
1085
+ this.inboxes.label(
1086
+ inboxEndpoint,
1087
+ messageId,
1088
+ MESSAGE_LABEL_INVALID,
1089
+ inboxToken,
1090
+ );
1091
+ return;
1092
+ }
1093
+
1094
+ const {
1095
+ [MESSAGE_DATA_STORAGE_BUCKET_KEY]: storageBucketKey,
1096
+ [MESSAGE_DATA_TOMBSTONED_MESSAGE_ID_KEY]: tombstonedMessageId,
1097
+ } = metadata;
1098
+
1099
+ const allowedTickets =
1100
+ MESSAGE_DATA_ALLOWED_TICKETS_KEY in metadata
1101
+ ? metadata[MESSAGE_DATA_ALLOWED_TICKETS_KEY]
1102
+ : undefined;
1103
+ const announcements =
1104
+ MESSAGE_DATA_ANNOUNCEMENTS_KEY in metadata
1105
+ ? metadata[MESSAGE_DATA_ANNOUNCEMENTS_KEY]
1106
+ : undefined;
1107
+
1108
+ if (label === MESSAGE_LABEL_VALID) {
1109
+ return {
1110
+ messageId,
1111
+ object,
1112
+ storageBucketKey,
1113
+ allowedTickets,
1114
+ tags: receivedTags,
1115
+ announcements,
1116
+ };
1117
+ } else if (label === MESSAGE_LABEL_TRASH) {
1118
+ // If it is simply trash, just continue.
1119
+ if (!tombstonedMessageId) return;
1120
+
1121
+ // Make sure the tombstone points to a real message
1122
+ const past = await this.inboxes.get(
1123
+ inboxEndpoint,
1124
+ tombstonedMessageId,
1125
+ inboxToken,
1126
+ );
1021
1127
  if (
1022
- label !== MESSAGE_LABEL_VALID &&
1023
- label !== MESSAGE_LABEL_UNLABELED &&
1024
- label !== MESSAGE_LABEL_TRASH
1128
+ !past ||
1129
+ past[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url !== object.url
1025
1130
  )
1026
- continue;
1131
+ return;
1027
1132
 
1028
- const messageId = result.id;
1029
- const { o: object, m: metadataBytes, t: receivedTags } = result.m;
1030
-
1031
- let metadata: MessageMetadata;
1032
- try {
1033
- const metadataRaw = dagCborDecode(metadataBytes);
1034
- metadata = MessageMetadataSchema.parse(metadataRaw);
1035
- } catch (e) {
1133
+ // If the referred to message isn't labeled as trash, trash it
1134
+ // This may happen if a trash message is processed on another
1135
+ // device and the device cache is out of date.
1136
+ if (past[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH) {
1137
+ // Label the message as trash
1036
1138
  this.inboxes.label(
1037
1139
  inboxEndpoint,
1038
- messageId,
1039
- MESSAGE_LABEL_INVALID,
1140
+ tombstonedMessageId,
1141
+ MESSAGE_LABEL_TRASH,
1040
1142
  inboxToken,
1041
1143
  );
1042
- continue;
1043
1144
  }
1044
1145
 
1045
- const {
1046
- [MESSAGE_DATA_STORAGE_BUCKET_KEY]: storageBucketKey,
1047
- [MESSAGE_DATA_TOMBSTONED_MESSAGE_ID_KEY]: tombstonedMessageId,
1048
- } = metadata;
1146
+ // Return the tombstone
1147
+ return {
1148
+ messageId,
1149
+ tombstone: true,
1150
+ object,
1151
+ storageBucketKey,
1152
+ allowedTickets,
1153
+ tags: receivedTags,
1154
+ announcements,
1155
+ };
1156
+ }
1049
1157
 
1050
- const allowedTickets =
1051
- MESSAGE_DATA_ALLOWED_TICKETS_KEY in metadata
1052
- ? metadata[MESSAGE_DATA_ALLOWED_TICKETS_KEY]
1053
- : undefined;
1054
- const announcements =
1055
- MESSAGE_DATA_ANNOUNCEMENTS_KEY in metadata
1056
- ? metadata[MESSAGE_DATA_ANNOUNCEMENTS_KEY]
1158
+ // Otherwise, unlabeled: try to validate the object
1159
+ let validationError: unknown | undefined = undefined;
1160
+ try {
1161
+ const actor = object.actor;
1162
+ const actorDocument = await this.dids.resolve(actor);
1163
+ const storageBucketService = actorDocument?.service?.find(
1164
+ (service) =>
1165
+ service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&
1166
+ service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,
1167
+ );
1168
+ if (!storageBucketService) {
1169
+ throw new GraffitiErrorNotFound(
1170
+ `Actor ${actor} has no storage bucket service`,
1171
+ );
1172
+ }
1173
+ if (typeof storageBucketService.serviceEndpoint !== "string") {
1174
+ throw new GraffitiErrorNotFound(
1175
+ `Actor ${actor} does not have a valid storage bucket endpoint`,
1176
+ );
1177
+ }
1178
+ const storageBucketEndpoint = storageBucketService.serviceEndpoint;
1179
+
1180
+ const objectBytes = await this.storageBuckets.get(
1181
+ storageBucketEndpoint,
1182
+ storageBucketKey,
1183
+ MAX_OBJECT_SIZE_BYTES,
1184
+ );
1185
+
1186
+ if (MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata && !recipient) {
1187
+ throw new GraffitiErrorForbidden(
1188
+ `Recipient is required when allowed ticket is present`,
1189
+ );
1190
+ }
1191
+ const privateObjectInfo = allowedTickets
1192
+ ? { allowedTickets }
1193
+ : MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata
1194
+ ? {
1195
+ recipient: recipient ?? "null",
1196
+ allowedTicket: metadata[MESSAGE_DATA_ALLOWED_TICKET_KEY],
1197
+ allowedIndex: metadata[MESSAGE_DATA_ALLOWED_TICKET_INDEX_KEY],
1198
+ }
1057
1199
  : undefined;
1058
1200
 
1059
- if (label === MESSAGE_LABEL_VALID) {
1060
- yield {
1201
+ await this.objectEncoding.validate(
1202
+ object,
1203
+ receivedTags,
1204
+ objectBytes,
1205
+ privateObjectInfo,
1206
+ );
1207
+ } catch (e) {
1208
+ validationError = e;
1209
+ }
1210
+
1211
+ if (tombstonedMessageId) {
1212
+ if (validationError instanceof GraffitiErrorNotFound) {
1213
+ // Not found == The tombstone is correct
1214
+ this.inboxes
1215
+ // Get the referenced message
1216
+ .get(inboxEndpoint, tombstonedMessageId, inboxToken)
1217
+ .then((result) => {
1218
+ if (
1219
+ // Make sure that it actually references the object being deleted
1220
+ result &&
1221
+ result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url ===
1222
+ object.url &&
1223
+ // And that the object is not already marked as trash
1224
+ result[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH
1225
+ ) {
1226
+ // If valid but not yet trash, label the message as trash
1227
+ this.inboxes.label(
1228
+ inboxEndpoint,
1229
+ tombstonedMessageId,
1230
+ MESSAGE_LABEL_TRASH,
1231
+ inboxToken,
1232
+ );
1233
+ }
1234
+
1235
+ // Then, label the tombstone message as trash
1236
+ this.inboxes.label(
1237
+ inboxEndpoint,
1238
+ messageId,
1239
+ MESSAGE_LABEL_TRASH,
1240
+ inboxToken,
1241
+ );
1242
+ });
1243
+
1244
+ return {
1061
1245
  messageId,
1246
+ tombstone: true,
1062
1247
  object,
1063
1248
  storageBucketKey,
1064
1249
  allowedTickets,
1065
1250
  tags: receivedTags,
1066
1251
  announcements,
1067
1252
  };
1068
- continue;
1069
- } else if (label === MESSAGE_LABEL_TRASH) {
1070
- // If it is simply trash, just continue.
1071
- if (!tombstonedMessageId) continue;
1072
-
1073
- // Make sure the tombstone points to a real message
1074
- const past = await this.inboxes.get(
1253
+ } else {
1254
+ console.error("Recieved an incorrect tombstone object");
1255
+ console.error(validationError);
1256
+ this.inboxes.label(
1075
1257
  inboxEndpoint,
1076
- tombstonedMessageId,
1258
+ messageId,
1259
+ MESSAGE_LABEL_INVALID,
1077
1260
  inboxToken,
1078
1261
  );
1079
- if (
1080
- !past ||
1081
- past[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url !==
1082
- object.url
1083
- )
1084
- continue;
1085
-
1086
- // If the referred to message isn't labeled as trash, trash it
1087
- // This may happen if a trash message is processed on another
1088
- // device and the device cache is out of date.
1089
- if (past[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH) {
1090
- // Label the message as trash
1091
- this.inboxes.label(
1092
- inboxEndpoint,
1093
- tombstonedMessageId,
1094
- MESSAGE_LABEL_TRASH,
1095
- inboxToken,
1096
- );
1097
- }
1098
-
1099
- // Return the tombstone
1100
- yield {
1262
+ }
1263
+ } else {
1264
+ if (validationError === undefined) {
1265
+ this.inboxes.label(
1266
+ inboxEndpoint,
1267
+ messageId,
1268
+ MESSAGE_LABEL_VALID,
1269
+ inboxToken,
1270
+ );
1271
+ return {
1101
1272
  messageId,
1102
- tombstone: true,
1103
1273
  object,
1104
1274
  storageBucketKey,
1105
- allowedTickets,
1106
1275
  tags: receivedTags,
1276
+ allowedTickets,
1107
1277
  announcements,
1108
1278
  };
1109
- continue;
1110
- }
1111
-
1112
- // Otherwise, unlabeled: try to validate the object
1113
- let validationError: unknown | undefined = undefined;
1114
- try {
1115
- const actor = object.actor;
1116
- const actorDocument = await this.dids.resolve(actor);
1117
- const storageBucketService = actorDocument?.service?.find(
1118
- (service) =>
1119
- service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&
1120
- service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,
1121
- );
1122
- if (!storageBucketService) {
1123
- throw new GraffitiErrorNotFound(
1124
- `Actor ${actor} has no storage bucket service`,
1125
- );
1126
- }
1127
- if (typeof storageBucketService.serviceEndpoint !== "string") {
1128
- throw new GraffitiErrorNotFound(
1129
- `Actor ${actor} does not have a valid storage bucket endpoint`,
1130
- );
1131
- }
1132
- const storageBucketEndpoint = storageBucketService.serviceEndpoint;
1133
-
1134
- const objectBytes = await this.storageBuckets.get(
1135
- storageBucketEndpoint,
1136
- storageBucketKey,
1137
- MAX_OBJECT_SIZE_BYTES,
1138
- );
1139
-
1140
- if (MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata && !recipient) {
1141
- throw new GraffitiErrorForbidden(
1142
- `Recipient is required when allowed ticket is present`,
1143
- );
1144
- }
1145
- const privateObjectInfo = allowedTickets
1146
- ? { allowedTickets }
1147
- : MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata
1148
- ? {
1149
- recipient: recipient ?? "null",
1150
- allowedTicket: metadata[MESSAGE_DATA_ALLOWED_TICKET_KEY],
1151
- allowedIndex: metadata[MESSAGE_DATA_ALLOWED_TICKET_INDEX_KEY],
1152
- }
1153
- : undefined;
1154
-
1155
- await this.objectEncoding.validate(
1156
- object,
1157
- receivedTags,
1158
- objectBytes,
1159
- privateObjectInfo,
1279
+ } else if (validationError instanceof GraffitiErrorNotFound) {
1280
+ // Item was deleted before we got a chance to
1281
+ // validate it. Just label the message as trash.
1282
+ this.inboxes.label(
1283
+ inboxEndpoint,
1284
+ messageId,
1285
+ MESSAGE_LABEL_TRASH,
1286
+ inboxToken,
1160
1287
  );
1161
- } catch (e) {
1162
- validationError = e;
1163
- }
1164
-
1165
- if (tombstonedMessageId) {
1166
- if (validationError instanceof GraffitiErrorNotFound) {
1167
- // Not found == The tombstone is correct
1168
- this.inboxes
1169
- // Get the referenced message
1170
- .get(inboxEndpoint, tombstonedMessageId, inboxToken)
1171
- .then((result) => {
1172
- if (
1173
- // Make sure that it actually references the object being deleted
1174
- result &&
1175
- result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url ===
1176
- object.url &&
1177
- // And that the object is not already marked as trash
1178
- result[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH
1179
- ) {
1180
- // If valid but not yet trash, label the message as trash
1181
- this.inboxes.label(
1182
- inboxEndpoint,
1183
- tombstonedMessageId,
1184
- MESSAGE_LABEL_TRASH,
1185
- inboxToken,
1186
- );
1187
- }
1188
-
1189
- // Then, label the tombstone message as trash
1190
- this.inboxes.label(
1191
- inboxEndpoint,
1192
- messageId,
1193
- MESSAGE_LABEL_TRASH,
1194
- inboxToken,
1195
- );
1196
- });
1197
-
1198
- yield {
1199
- messageId,
1200
- tombstone: true,
1201
- object,
1202
- storageBucketKey,
1203
- allowedTickets,
1204
- tags: receivedTags,
1205
- announcements,
1206
- };
1207
- } else {
1208
- console.error("Recieved an incorrect object");
1209
- console.error(validationError);
1210
- this.inboxes.label(
1211
- inboxEndpoint,
1212
- messageId,
1213
- MESSAGE_LABEL_INVALID,
1214
- inboxToken,
1215
- );
1216
- }
1217
1288
  } else {
1218
- if (validationError === undefined) {
1219
- this.inboxes.label(
1220
- inboxEndpoint,
1221
- messageId,
1222
- MESSAGE_LABEL_VALID,
1223
- inboxToken,
1224
- );
1225
- yield {
1226
- messageId,
1227
- object,
1228
- storageBucketKey,
1229
- tags: receivedTags,
1230
- allowedTickets,
1231
- announcements,
1232
- };
1233
- } else {
1234
- console.error("Recieved an incorrect object");
1235
- console.error(validationError);
1236
- this.inboxes.label(
1237
- inboxEndpoint,
1238
- messageId,
1239
- MESSAGE_LABEL_INVALID,
1240
- inboxToken,
1241
- );
1242
- }
1289
+ console.error("Recieved an incorrect object");
1290
+ console.error(validationError);
1291
+ this.inboxes.label(
1292
+ inboxEndpoint,
1293
+ messageId,
1294
+ MESSAGE_LABEL_INVALID,
1295
+ inboxToken,
1296
+ );
1243
1297
  }
1244
1298
  }
1245
1299
  }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../node_modules/@graffiti-garden/modal/src/style.css"],
4
- "sourcesContent": [".graffiti-modal {\n --back: rgba(26, 26, 26, 0.85);\n --halfback: rgba(80, 80, 80, 0.85);\n --halfback2: rgba(26, 26, 26, 0.85);\n --hover: rgba(202, 122, 204, 0.3);\n --frontfaded: rgba(190, 190, 190);\n --front: rgba(240, 240, 240);\n --emph: rgb(202, 122, 204);\n --blurpix: 3px;\n border-color: var(--emph);\n box-sizing: border-box;\n border-width: 2px;\n padding: 0;\n margin: 0;\n border-radius: 1rem;\n box-shadow: 0 0 2rem black;\n overflow: hidden;\n opacity: 0;\n transition: opacity 0.3s;\n pointer-events: none;\n min-width: 95dvw;\n min-height: 95dvh;\n height: 95dvh;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: center;\n font-family:\n Inter,\n -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n \"Fira Sans\",\n \"Droid Sans\",\n \"Helvetica Neue\",\n sans-serif;\n color: var(--front);\n font-size: 150%;\n\n * {\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n }\n\n ::selection {\n background: rgba(202, 122, 204, 0.3);\n }\n\n :focus {\n outline: 2px solid var(--front);\n }\n\n header {\n width: 100%;\n display: flex;\n justify-content: flex-end;\n }\n\n main {\n flex: 1;\n max-width: 600px;\n width: 100%;\n gap: 2em;\n padding-inline: clamp(1rem, 4dvw, 3rem);\n padding-block: clamp(1rem, 4dvh, 3rem);\n margin-inline: clamp(1rem, 4dvw, 3rem);\n margin-block: clamp(1rem, 4dvh, 3rem);\n background: var(--back);\n border-radius: 1rem;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n scrollbar-color: var(--emph) rgba(0, 0, 0, 0);\n }\n\n ul {\n list-style-type: none;\n display: flex;\n flex-direction: column;\n gap: 0.5em;\n align-items: stretch;\n justify-content: stretch;\n }\n\n aside {\n color: var(--frontfaded);\n }\n\n .secondary,\n a:not([type=\"button\"]) {\n color: var(--emph);\n }\n\n h1 {\n font-size: 120%;\n font-family:\n Rock Salt,\n cursive,\n sans-serif;\n letter-spacing: 0.1em;\n text-align: center;\n color: var(--front);\n }\n\n h1 a:not([type=\"button\"]) {\n color: inherit;\n }\n\n h1 a:hover {\n background: none;\n color: inherit;\n }\n\n button,\n input[type=\"submit\"],\n input[type=\"text\"],\n a[type=\"button\"] {\n font-size: inherit;\n width: 100%;\n text-align: center;\n display: block;\n border-radius: 1rem;\n border: 2px solid var(--emph);\n padding: 1em;\n padding-top: 0.5em;\n padding-bottom: 0.5em;\n transition: 0.1s;\n text-overflow: ellipsis;\n background: none;\n line-height: 1.2em;\n }\n\n input[type=\"text\"] {\n font-weight: 500;\n background: var(--front);\n text-align: left;\n color: black;\n }\n\n header button {\n border-radius: 0 0 0 1rem;\n border-right: none;\n border-top: none;\n background-color: var(--halfback2);\n width: fit-content;\n position: relative;\n overflow: hidden;\n }\n\n header button::before {\n z-index: -1;\n top: 0;\n left: 0;\n height: 100%;\n position: absolute;\n width: 100%;\n content: \"\";\n background-color: var(--back);\n }\n\n @media (max-height: 600px) {\n main {\n gap: 1em;\n }\n\n h1 {\n font-size: 100%;\n }\n }\n\n @media (max-width: 600px) {\n main {\n border-radius: 0;\n margin: auto;\n overflow: auto;\n }\n\n header button {\n border-radius: 0;\n border-left: none;\n width: 100%;\n }\n\n html {\n justify-content: safe center;\n }\n }\n\n a {\n text-decoration: none;\n }\n\n :is(button, ul a, input[type=\"submit\"]):hover {\n cursor: pointer;\n background: var(--hover);\n color: var(--front);\n }\n\n a:hover {\n text-decoration: underline;\n cursor: pointer;\n }\n\n form {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: stretch;\n gap: 0.5em;\n }\n\n iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n :is(button, a[type=\"button\"], input[type=\"submit\"]).secondary {\n color: rgb(244, 213, 244);\n background: rgba(26, 26, 26, 0.6);\n }\n\n :is(button, a[type=\"button\"], input[type=\"submit\"]):not(.secondary) {\n background: var(--hover);\n color: white;\n }\n\n :is(button, a[type=\"button\"], input[type=\"submit\"]):hover {\n background: rgba(202, 122, 204, 0.6);\n color: white;\n text-decoration: none;\n }\n}\n\n.graffiti-modal[open] {\n pointer-events: inherit;\n opacity: 1;\n}\n\n.graffiti-modal::backdrop {\n background-color: black;\n opacity: 0.5;\n}\n\n.graffiti-modal::before {\n content: \"\";\n position: fixed;\n left: 0;\n right: 0;\n z-index: -1;\n background-image: url(graffiti.jpg);\n background-size: cover;\n background-repeat: no-repeat;\n background-position: 50% 50%;\n height: calc(100% + 2 * var(--blurpix));\n width: calc(100% + 2 * var(--blurpix));\n filter: blur(var(--blurpix));\n margin: calc(-1 * var(--blurpix));\n}\n"],
5
- "mappings": "4BAAA,IAAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
6
- "names": ["style_default"]
7
- }