@graffiti-garden/implementation-decentralized 0.0.2 → 0.0.3
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/1-services/4-inboxes-tests.d.ts.map +1 -1
- package/dist/1-services/4-inboxes.d.ts +3 -3
- package/dist/1-services/4-inboxes.d.ts.map +1 -1
- package/dist/3-protocol/4-graffiti.d.ts.map +1 -1
- package/dist/3-protocol/login-dialog.html.d.ts +1 -1
- package/dist/3-protocol/login-dialog.html.d.ts.map +1 -1
- package/dist/browser/index.js +7 -7
- package/dist/browser/index.js.map +3 -3
- package/dist/browser/login-dialog.html-VTDKJZBG.js +44 -0
- package/dist/browser/login-dialog.html-VTDKJZBG.js.map +7 -0
- package/dist/browser/{style-YUTCEBZV-RWYJV575.js → style-3ALLGCD7-QNFKN6AK.js} +18 -36
- package/dist/browser/style-3ALLGCD7-QNFKN6AK.js.map +7 -0
- package/dist/cjs/1-services/4-inboxes-tests.js +2 -0
- package/dist/cjs/1-services/4-inboxes-tests.js.map +2 -2
- package/dist/cjs/1-services/4-inboxes.js +17 -8
- package/dist/cjs/1-services/4-inboxes.js.map +2 -2
- package/dist/cjs/3-protocol/4-graffiti.js +40 -12
- package/dist/cjs/3-protocol/4-graffiti.js.map +2 -2
- package/dist/cjs/3-protocol/login-dialog.html.js +9 -9
- package/dist/cjs/3-protocol/login-dialog.html.js.map +1 -1
- package/dist/esm/1-services/4-inboxes-tests.js +2 -0
- package/dist/esm/1-services/4-inboxes-tests.js.map +2 -2
- package/dist/esm/1-services/4-inboxes.js +19 -9
- package/dist/esm/1-services/4-inboxes.js.map +2 -2
- package/dist/esm/3-protocol/4-graffiti.js +41 -12
- package/dist/esm/3-protocol/4-graffiti.js.map +2 -2
- package/dist/esm/3-protocol/login-dialog.html.js +9 -9
- package/dist/esm/3-protocol/login-dialog.html.js.map +1 -1
- package/package.json +3 -3
- package/src/1-services/4-inboxes-tests.ts +2 -0
- package/src/1-services/4-inboxes.ts +25 -15
- package/src/3-protocol/4-graffiti.ts +65 -17
- package/src/3-protocol/login-dialog.html.ts +9 -9
- package/dist/browser/login-dialog.html-XUWYDNNI.js +0 -44
- package/dist/browser/login-dialog.html-XUWYDNNI.js.map +0 -7
- package/dist/browser/style-YUTCEBZV-RWYJV575.js.map +0 -7
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import"./chunk-RFBBAUMM.js";var i=`<template id="graffiti-login-welcome">
|
|
2
|
+
<h1>
|
|
3
|
+
<a target="_blank" href="https://graffiti.garden">Graffiti Log In</a>
|
|
4
|
+
</h1>
|
|
5
|
+
|
|
6
|
+
<ul>
|
|
7
|
+
<li><a type="button" id="graffiti-login-new">Create new Graffiti identity</a></li>
|
|
8
|
+
<li><button class="secondary" id="graffiti-login-existing">Use existing Graffiti identity</button></li>
|
|
9
|
+
</ul>
|
|
10
|
+
|
|
11
|
+
<aside>
|
|
12
|
+
This application is built with
|
|
13
|
+
<a target="_blank" href="https://graffiti.garden">Graffiti</a>.
|
|
14
|
+
</aside>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<template id="graffiti-login-handle">
|
|
18
|
+
<h1>
|
|
19
|
+
<a target="_blank" href="https://graffiti.garden">Graffiti Log In</a>
|
|
20
|
+
</h1>
|
|
21
|
+
|
|
22
|
+
<form id="graffiti-login-handle-form">
|
|
23
|
+
<label for="username">Graffiti handle:</label>
|
|
24
|
+
<input
|
|
25
|
+
type="text"
|
|
26
|
+
name="username"
|
|
27
|
+
id="username"
|
|
28
|
+
autocomplete="username"
|
|
29
|
+
autocapitalize="none"
|
|
30
|
+
spellcheck="false"
|
|
31
|
+
inputmode="url"
|
|
32
|
+
placeholder="you.graffiti.actor"
|
|
33
|
+
required
|
|
34
|
+
>
|
|
35
|
+
<button id="graffiti-login-handle-submit" type="submit">
|
|
36
|
+
Log In
|
|
37
|
+
</button>
|
|
38
|
+
</form>
|
|
39
|
+
|
|
40
|
+
<p>
|
|
41
|
+
Don't have a Graffiti handle? <a id="graffiti-login-new">Create one</a>.
|
|
42
|
+
</p>
|
|
43
|
+
</template>`;export{i as template};
|
|
44
|
+
//# sourceMappingURL=login-dialog.html-VTDKJZBG.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/3-protocol/login-dialog.html.ts"],
|
|
4
|
+
"sourcesContent": ["export const template = `<template id=\"graffiti-login-welcome\">\n <h1>\n <a target=\"_blank\" href=\"https://graffiti.garden\">Graffiti Log In</a>\n </h1>\n\n <ul>\n <li><a type=\"button\" id=\"graffiti-login-new\">Create new Graffiti identity</a></li>\n <li><button class=\"secondary\" id=\"graffiti-login-existing\">Use existing Graffiti identity</button></li>\n </ul>\n\n <aside>\n This application is built with\n <a target=\"_blank\" href=\"https://graffiti.garden\">Graffiti</a>.\n </aside>\n</template>\n\n<template id=\"graffiti-login-handle\">\n<h1>\n <a target=\"_blank\" href=\"https://graffiti.garden\">Graffiti Log In</a>\n</h1>\n\n <form id=\"graffiti-login-handle-form\">\n <label for=\"username\">Graffiti handle:</label>\n <input\n type=\"text\"\n name=\"username\"\n id=\"username\"\n autocomplete=\"username\"\n autocapitalize=\"none\"\n spellcheck=\"false\"\n inputmode=\"url\"\n placeholder=\"you.graffiti.actor\"\n required\n >\n <button id=\"graffiti-login-handle-submit\" type=\"submit\">\n Log In\n </button>\n </form>\n\n <p>\n Don't have a Graffiti handle? <a id=\"graffiti-login-new\">Create one</a>.\n </p>\n</template>`;\n"],
|
|
5
|
+
"mappings": "4BAAO,IAAMA,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
|
+
"names": ["template"]
|
|
7
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import"./chunk-RFBBAUMM.js";var
|
|
2
|
-
--back:
|
|
1
|
+
import"./chunk-RFBBAUMM.js";var e=`.graffiti-modal {
|
|
2
|
+
--back: rgba(26, 26, 26, 0.85);
|
|
3
3
|
--halfback: rgba(80, 80, 80, 0.85);
|
|
4
4
|
--halfback2: rgba(26, 26, 26, 0.85);
|
|
5
5
|
--hover: rgba(202, 122, 204, 0.3);
|
|
@@ -18,7 +18,6 @@ import"./chunk-RFBBAUMM.js";var t=`.graffiti-modal {
|
|
|
18
18
|
opacity: 0;
|
|
19
19
|
transition: opacity 0.3s;
|
|
20
20
|
pointer-events: none;
|
|
21
|
-
display: block;
|
|
22
21
|
min-width: 95dvw;
|
|
23
22
|
min-height: 95dvh;
|
|
24
23
|
height: 95dvh;
|
|
@@ -70,14 +69,10 @@ import"./chunk-RFBBAUMM.js";var t=`.graffiti-modal {
|
|
|
70
69
|
max-width: 600px;
|
|
71
70
|
width: 100%;
|
|
72
71
|
gap: 2em;
|
|
73
|
-
padding-
|
|
74
|
-
padding-
|
|
75
|
-
margin-
|
|
76
|
-
margin-
|
|
77
|
-
margin-left: 4dvw;
|
|
78
|
-
margin-right: 4dvw;
|
|
79
|
-
padding-left: 4dvw;
|
|
80
|
-
padding-right: 4dvw;
|
|
72
|
+
padding-inline: clamp(1rem, 4dvw, 3rem);
|
|
73
|
+
padding-block: clamp(1rem, 4dvh, 3rem);
|
|
74
|
+
margin-inline: clamp(1rem, 4dvw, 3rem);
|
|
75
|
+
margin-block: clamp(1rem, 4dvh, 3rem);
|
|
81
76
|
background: var(--back);
|
|
82
77
|
border-radius: 1rem;
|
|
83
78
|
display: flex;
|
|
@@ -171,6 +166,16 @@ import"./chunk-RFBBAUMM.js";var t=`.graffiti-modal {
|
|
|
171
166
|
background-color: var(--back);
|
|
172
167
|
}
|
|
173
168
|
|
|
169
|
+
@media (max-height: 600px) {
|
|
170
|
+
main {
|
|
171
|
+
gap: 1em;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
h1 {
|
|
175
|
+
font-size: 100%;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
174
179
|
@media (max-width: 600px) {
|
|
175
180
|
main {
|
|
176
181
|
border-radius: 0;
|
|
@@ -233,29 +238,6 @@ import"./chunk-RFBBAUMM.js";var t=`.graffiti-modal {
|
|
|
233
238
|
color: white;
|
|
234
239
|
text-decoration: none;
|
|
235
240
|
}
|
|
236
|
-
|
|
237
|
-
h3 {
|
|
238
|
-
color: var(--frontfaded);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
h2 {
|
|
242
|
-
text-align: center;
|
|
243
|
-
font-weight: 500;
|
|
244
|
-
font-size: 150%;
|
|
245
|
-
background: var(--halfback2);
|
|
246
|
-
padding: 1rem;
|
|
247
|
-
border-radius: 1rem;
|
|
248
|
-
border: 1px solid var(--frontfaded);
|
|
249
|
-
color: var(--front);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
main > div {
|
|
253
|
-
display: flex;
|
|
254
|
-
flex-direction: column;
|
|
255
|
-
justify-content: center;
|
|
256
|
-
align-items: stretch;
|
|
257
|
-
gap: 0.5rem;
|
|
258
|
-
}
|
|
259
241
|
}
|
|
260
242
|
|
|
261
243
|
.graffiti-modal[open] {
|
|
@@ -283,5 +265,5 @@ import"./chunk-RFBBAUMM.js";var t=`.graffiti-modal {
|
|
|
283
265
|
filter: blur(var(--blurpix));
|
|
284
266
|
margin: calc(-1 * var(--blurpix));
|
|
285
267
|
}
|
|
286
|
-
`;export{
|
|
287
|
-
//# sourceMappingURL=style-
|
|
268
|
+
`;export{e as default};
|
|
269
|
+
//# sourceMappingURL=style-3ALLGCD7-QNFKN6AK.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
}
|
|
@@ -49,6 +49,7 @@ function inboxTests(inboxEndpoint, inboxToken) {
|
|
|
49
49
|
};
|
|
50
50
|
const messageId = await inboxes.send(inboxEndpoint, sending);
|
|
51
51
|
const message = await inboxes.get(inboxEndpoint, messageId, inboxToken);
|
|
52
|
+
(0, import_vitest.assert)(message !== null);
|
|
52
53
|
(0, import_vitest.expect)(message.m).toEqual(sending);
|
|
53
54
|
(0, import_vitest.expect)(message.l).toEqual(0);
|
|
54
55
|
const iterator = inboxes.query(inboxEndpoint, tags, {}, inboxToken);
|
|
@@ -68,6 +69,7 @@ function inboxTests(inboxEndpoint, inboxToken) {
|
|
|
68
69
|
const endResult2 = await iterator2.next();
|
|
69
70
|
(0, import_vitest.expect)(endResult2.done).toBe(true);
|
|
70
71
|
const message2 = await inboxes.get(inboxEndpoint, messageId, inboxToken);
|
|
72
|
+
(0, import_vitest.assert)(message2 !== null);
|
|
71
73
|
(0, import_vitest.expect)(message2.m).toEqual(sending);
|
|
72
74
|
(0, import_vitest.expect)(message2.l).toEqual(42);
|
|
73
75
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/1-services/4-inboxes-tests.ts"],
|
|
4
|
-
"sourcesContent": ["import { assert, describe, expect, test } from \"vitest\";\nimport { Inboxes, LABELED_MESSAGE_LABEL_KEY } from \"./4-inboxes\";\nimport { GraffitiErrorUnauthorized } from \"./utilities\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport type { GraffitiObjectBase } from \"@graffiti-garden/api\";\n\nexport function inboxTests(inboxEndpoint: string, inboxToken: string) {\n describe(\"Inboxes\", async () => {\n const inboxes = new Inboxes();\n\n test(\"send, get\", async () => {\n const tags = [randomBytes(), randomBytes()];\n const metadata = randomBytes();\n const object: GraffitiObjectBase = {\n url: \"url:example\",\n actor: \"did:example\",\n channels: [\"example\", \"something\"],\n value: {\n nested: {\n property: [1, \"askdfj\", null],\n },\n },\n allowed: [\"did:example2\"],\n };\n\n const sending = {\n m: metadata,\n o: object,\n t: tags,\n };\n const messageId = await inboxes.send(inboxEndpoint, sending);\n\n // Get the message back\n const message = await inboxes.get(inboxEndpoint, messageId, inboxToken);\n expect(message.m).toEqual(sending);\n expect(message.l).toEqual(0);\n\n const iterator = inboxes.query<{}>(inboxEndpoint, tags, {}, inboxToken);\n\n const result = await iterator.next();\n assert(!result.done);\n\n // No label yet so it must be zero\n expect(result.value.l).toEqual(0);\n\n expect(result.value.m.t).toEqual(tags);\n expect(result.value.m.o).toEqual(object);\n expect(result.value.id).toEqual(messageId);\n\n const endResult = await iterator.next();\n expect(endResult.done).toBe(true);\n\n // Label the message\n await inboxes.label(inboxEndpoint, messageId, 42, inboxToken);\n\n const iterator2 = inboxes.query<{}>(inboxEndpoint, tags, {}, inboxToken);\n\n const result2 = await iterator2.next();\n assert(!result2.done);\n expect(result2.value.l).toEqual(42);\n const endResult2 = await iterator2.next();\n expect(endResult2.done).toBe(true);\n\n const message2 = await inboxes.get(inboxEndpoint, messageId, inboxToken);\n expect(message2.m).toEqual(sending);\n expect(message2.l).toEqual(42);\n });\n\n test(\"query with continue\", async () => {\n const tags = [randomBytes(), randomBytes()];\n\n const nullResult = await inboxes\n .query<{}>(inboxEndpoint, tags, {}, inboxToken)\n .next();\n assert(nullResult.done);\n const cursor = nullResult.value;\n\n const metadata = randomBytes();\n\n const messageId = await inboxes.send(inboxEndpoint, {\n o: {\n url: \"url:example\",\n actor: \"did:example\",\n channels: [\"example\", \"something\"],\n value: {\n nested: {\n property: [1, \"askdfj\", null],\n },\n },\n allowed: [\"did:example2\"],\n },\n t: [randomBytes(), tags[0]],\n m: metadata,\n });\n\n const result = await inboxes\n .continueQuery(inboxEndpoint, cursor, inboxToken)\n .next();\n assert(!result.done);\n expect(result.value.id).toEqual(messageId);\n });\n\n test(\"unauthorized access\", async () => {\n const tags = [randomBytes()];\n\n await expect(\n inboxes.query(inboxEndpoint, tags, {}, \"invalid-token\").next(),\n ).rejects.toThrowError(GraffitiErrorUnauthorized);\n await expect(\n inboxes.label(inboxEndpoint, \"1\", 1, \"invalid-token\"),\n ).rejects.toThrowError(GraffitiErrorUnauthorized);\n await expect(\n inboxes.export(inboxEndpoint, \"invalid-token\").next(),\n ).rejects.toThrowError(GraffitiErrorUnauthorized);\n }, 30000);\n\n test(\"query paged\", async () => {\n const tags = [randomBytes(), randomBytes()];\n\n const numSends = 211;\n for (let i = 0; i < numSends; i++) {\n await inboxes.send(inboxEndpoint, {\n t: tags,\n m: randomBytes(),\n o: {\n url: \"url:example\",\n actor: \"did:example\",\n channels: [\"example\", \"something\"],\n value: {\n nested: {\n property: [1, \"askdfj\", null],\n },\n },\n allowed: [\"did:example2\"],\n },\n });\n }\n\n const iterator = inboxes.query(\n inboxEndpoint,\n [randomBytes(), tags[1], randomBytes()],\n {},\n inboxToken,\n );\n\n let count = 0;\n for await (const _ of iterator) {\n count++;\n }\n\n expect(count).toBe(numSends);\n }, 100000);\n });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { assert, describe, expect, test } from \"vitest\";\nimport { Inboxes, LABELED_MESSAGE_LABEL_KEY } from \"./4-inboxes\";\nimport { GraffitiErrorUnauthorized } from \"./utilities\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport type { GraffitiObjectBase } from \"@graffiti-garden/api\";\n\nexport function inboxTests(inboxEndpoint: string, inboxToken: string) {\n describe(\"Inboxes\", async () => {\n const inboxes = new Inboxes();\n\n test(\"send, get\", async () => {\n const tags = [randomBytes(), randomBytes()];\n const metadata = randomBytes();\n const object: GraffitiObjectBase = {\n url: \"url:example\",\n actor: \"did:example\",\n channels: [\"example\", \"something\"],\n value: {\n nested: {\n property: [1, \"askdfj\", null],\n },\n },\n allowed: [\"did:example2\"],\n };\n\n const sending = {\n m: metadata,\n o: object,\n t: tags,\n };\n const messageId = await inboxes.send(inboxEndpoint, sending);\n\n // Get the message back\n const message = await inboxes.get(inboxEndpoint, messageId, inboxToken);\n assert(message !== null);\n expect(message.m).toEqual(sending);\n expect(message.l).toEqual(0);\n\n const iterator = inboxes.query<{}>(inboxEndpoint, tags, {}, inboxToken);\n\n const result = await iterator.next();\n assert(!result.done);\n\n // No label yet so it must be zero\n expect(result.value.l).toEqual(0);\n\n expect(result.value.m.t).toEqual(tags);\n expect(result.value.m.o).toEqual(object);\n expect(result.value.id).toEqual(messageId);\n\n const endResult = await iterator.next();\n expect(endResult.done).toBe(true);\n\n // Label the message\n await inboxes.label(inboxEndpoint, messageId, 42, inboxToken);\n\n const iterator2 = inboxes.query<{}>(inboxEndpoint, tags, {}, inboxToken);\n\n const result2 = await iterator2.next();\n assert(!result2.done);\n expect(result2.value.l).toEqual(42);\n const endResult2 = await iterator2.next();\n expect(endResult2.done).toBe(true);\n\n const message2 = await inboxes.get(inboxEndpoint, messageId, inboxToken);\n assert(message2 !== null);\n expect(message2.m).toEqual(sending);\n expect(message2.l).toEqual(42);\n });\n\n test(\"query with continue\", async () => {\n const tags = [randomBytes(), randomBytes()];\n\n const nullResult = await inboxes\n .query<{}>(inboxEndpoint, tags, {}, inboxToken)\n .next();\n assert(nullResult.done);\n const cursor = nullResult.value;\n\n const metadata = randomBytes();\n\n const messageId = await inboxes.send(inboxEndpoint, {\n o: {\n url: \"url:example\",\n actor: \"did:example\",\n channels: [\"example\", \"something\"],\n value: {\n nested: {\n property: [1, \"askdfj\", null],\n },\n },\n allowed: [\"did:example2\"],\n },\n t: [randomBytes(), tags[0]],\n m: metadata,\n });\n\n const result = await inboxes\n .continueQuery(inboxEndpoint, cursor, inboxToken)\n .next();\n assert(!result.done);\n expect(result.value.id).toEqual(messageId);\n });\n\n test(\"unauthorized access\", async () => {\n const tags = [randomBytes()];\n\n await expect(\n inboxes.query(inboxEndpoint, tags, {}, \"invalid-token\").next(),\n ).rejects.toThrowError(GraffitiErrorUnauthorized);\n await expect(\n inboxes.label(inboxEndpoint, \"1\", 1, \"invalid-token\"),\n ).rejects.toThrowError(GraffitiErrorUnauthorized);\n await expect(\n inboxes.export(inboxEndpoint, \"invalid-token\").next(),\n ).rejects.toThrowError(GraffitiErrorUnauthorized);\n }, 30000);\n\n test(\"query paged\", async () => {\n const tags = [randomBytes(), randomBytes()];\n\n const numSends = 211;\n for (let i = 0; i < numSends; i++) {\n await inboxes.send(inboxEndpoint, {\n t: tags,\n m: randomBytes(),\n o: {\n url: \"url:example\",\n actor: \"did:example\",\n channels: [\"example\", \"something\"],\n value: {\n nested: {\n property: [1, \"askdfj\", null],\n },\n },\n allowed: [\"did:example2\"],\n },\n });\n }\n\n const iterator = inboxes.query(\n inboxEndpoint,\n [randomBytes(), tags[1], randomBytes()],\n {},\n inboxToken,\n );\n\n let count = 0;\n for await (const _ of iterator) {\n count++;\n }\n\n expect(count).toBe(numSends);\n }, 100000);\n });\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+C;AAC/C,qBAAmD;AACnD,uBAA0C;AAC1C,mBAA4B;AAGrB,SAAS,WAAW,eAAuB,YAAoB;AACpE,8BAAS,WAAW,YAAY;AAC9B,UAAM,UAAU,IAAI,uBAAQ;AAE5B,4BAAK,aAAa,YAAY;AAC5B,YAAM,OAAO,KAAC,0BAAY,OAAG,0BAAY,CAAC;AAC1C,YAAM,eAAW,0BAAY;AAC7B,YAAM,SAA6B;AAAA,QACjC,KAAK;AAAA,QACL,OAAO;AAAA,QACP,UAAU,CAAC,WAAW,WAAW;AAAA,QACjC,OAAO;AAAA,UACL,QAAQ;AAAA,YACN,UAAU,CAAC,GAAG,UAAU,IAAI;AAAA,UAC9B;AAAA,QACF;AAAA,QACA,SAAS,CAAC,cAAc;AAAA,MAC1B;AAEA,YAAM,UAAU;AAAA,QACd,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,MACL;AACA,YAAM,YAAY,MAAM,QAAQ,KAAK,eAAe,OAAO;AAG3D,YAAM,UAAU,MAAM,QAAQ,IAAI,eAAe,WAAW,UAAU;AACtE,gCAAO,YAAY,IAAI;AACvB,gCAAO,QAAQ,CAAC,EAAE,QAAQ,OAAO;AACjC,gCAAO,QAAQ,CAAC,EAAE,QAAQ,CAAC;AAE3B,YAAM,WAAW,QAAQ,MAAU,eAAe,MAAM,CAAC,GAAG,UAAU;AAEtE,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,gCAAO,CAAC,OAAO,IAAI;AAGnB,gCAAO,OAAO,MAAM,CAAC,EAAE,QAAQ,CAAC;AAEhC,gCAAO,OAAO,MAAM,EAAE,CAAC,EAAE,QAAQ,IAAI;AACrC,gCAAO,OAAO,MAAM,EAAE,CAAC,EAAE,QAAQ,MAAM;AACvC,gCAAO,OAAO,MAAM,EAAE,EAAE,QAAQ,SAAS;AAEzC,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,gCAAO,UAAU,IAAI,EAAE,KAAK,IAAI;AAGhC,YAAM,QAAQ,MAAM,eAAe,WAAW,IAAI,UAAU;AAE5D,YAAM,YAAY,QAAQ,MAAU,eAAe,MAAM,CAAC,GAAG,UAAU;AAEvE,YAAM,UAAU,MAAM,UAAU,KAAK;AACrC,gCAAO,CAAC,QAAQ,IAAI;AACpB,gCAAO,QAAQ,MAAM,CAAC,EAAE,QAAQ,EAAE;AAClC,YAAM,aAAa,MAAM,UAAU,KAAK;AACxC,gCAAO,WAAW,IAAI,EAAE,KAAK,IAAI;AAEjC,YAAM,WAAW,MAAM,QAAQ,IAAI,eAAe,WAAW,UAAU;AACvE,gCAAO,aAAa,IAAI;AACxB,gCAAO,SAAS,CAAC,EAAE,QAAQ,OAAO;AAClC,gCAAO,SAAS,CAAC,EAAE,QAAQ,EAAE;AAAA,IAC/B,CAAC;AAED,4BAAK,uBAAuB,YAAY;AACtC,YAAM,OAAO,KAAC,0BAAY,OAAG,0BAAY,CAAC;AAE1C,YAAM,aAAa,MAAM,QACtB,MAAU,eAAe,MAAM,CAAC,GAAG,UAAU,EAC7C,KAAK;AACR,gCAAO,WAAW,IAAI;AACtB,YAAM,SAAS,WAAW;AAE1B,YAAM,eAAW,0BAAY;AAE7B,YAAM,YAAY,MAAM,QAAQ,KAAK,eAAe;AAAA,QAClD,GAAG;AAAA,UACD,KAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,WAAW,WAAW;AAAA,UACjC,OAAO;AAAA,YACL,QAAQ;AAAA,cACN,UAAU,CAAC,GAAG,UAAU,IAAI;AAAA,YAC9B;AAAA,UACF;AAAA,UACA,SAAS,CAAC,cAAc;AAAA,QAC1B;AAAA,QACA,GAAG,KAAC,0BAAY,GAAG,KAAK,CAAC,CAAC;AAAA,QAC1B,GAAG;AAAA,MACL,CAAC;AAED,YAAM,SAAS,MAAM,QAClB,cAAc,eAAe,QAAQ,UAAU,EAC/C,KAAK;AACR,gCAAO,CAAC,OAAO,IAAI;AACnB,gCAAO,OAAO,MAAM,EAAE,EAAE,QAAQ,SAAS;AAAA,IAC3C,CAAC;AAED,4BAAK,uBAAuB,YAAY;AACtC,YAAM,OAAO,KAAC,0BAAY,CAAC;AAE3B,gBAAM;AAAA,QACJ,QAAQ,MAAM,eAAe,MAAM,CAAC,GAAG,eAAe,EAAE,KAAK;AAAA,MAC/D,EAAE,QAAQ,aAAa,0CAAyB;AAChD,gBAAM;AAAA,QACJ,QAAQ,MAAM,eAAe,KAAK,GAAG,eAAe;AAAA,MACtD,EAAE,QAAQ,aAAa,0CAAyB;AAChD,gBAAM;AAAA,QACJ,QAAQ,OAAO,eAAe,eAAe,EAAE,KAAK;AAAA,MACtD,EAAE,QAAQ,aAAa,0CAAyB;AAAA,IAClD,GAAG,GAAK;AAER,4BAAK,eAAe,YAAY;AAC9B,YAAM,OAAO,KAAC,0BAAY,OAAG,0BAAY,CAAC;AAE1C,YAAM,WAAW;AACjB,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,cAAM,QAAQ,KAAK,eAAe;AAAA,UAChC,GAAG;AAAA,UACH,OAAG,0BAAY;AAAA,UACf,GAAG;AAAA,YACD,KAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU,CAAC,WAAW,WAAW;AAAA,YACjC,OAAO;AAAA,cACL,QAAQ;AAAA,gBACN,UAAU,CAAC,GAAG,UAAU,IAAI;AAAA,cAC9B;AAAA,YACF;AAAA,YACA,SAAS,CAAC,cAAc;AAAA,UAC1B;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,WAAW,QAAQ;AAAA,QACvB;AAAA,QACA,KAAC,0BAAY,GAAG,KAAK,CAAC,OAAG,0BAAY,CAAC;AAAA,QACtC,CAAC;AAAA,QACD;AAAA,MACF;AAEA,UAAI,QAAQ;AACZ,uBAAiB,KAAK,UAAU;AAC9B;AAAA,MACF;AAEA,gCAAO,KAAK,EAAE,KAAK,QAAQ;AAAA,IAC7B,GAAG,GAAM;AAAA,EACX,CAAC;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -73,16 +73,25 @@ class Inboxes {
|
|
|
73
73
|
const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);
|
|
74
74
|
const cache = await this.cache;
|
|
75
75
|
const cached = await cache.messages.get(messageCacheKey);
|
|
76
|
-
if (cached) return cached;
|
|
76
|
+
if (cached !== void 0) return cached;
|
|
77
77
|
const url2 = `${inboxUrl}/message/${messageId}`;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
let response = null;
|
|
79
|
+
try {
|
|
80
|
+
response = await (0, import_utilities.fetchWithErrorHandling)(url2, {
|
|
81
|
+
method: "GET",
|
|
82
|
+
headers: {
|
|
83
|
+
...inboxToken ? {
|
|
84
|
+
Authorization: `Bearer ${inboxToken}`
|
|
85
|
+
} : {}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
} catch (e) {
|
|
89
|
+
if (e instanceof import_api.GraffitiErrorNotFound) {
|
|
90
|
+
await cache.messages.set(messageCacheKey, null);
|
|
91
|
+
return null;
|
|
84
92
|
}
|
|
85
|
-
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
86
95
|
const blob = await response.blob();
|
|
87
96
|
const cbor = (0, import_dag_cbor.decode)(await blob.arrayBuffer());
|
|
88
97
|
const parsed = LabeledMessageBaseSchema.parse(cbor);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/1-services/4-inboxes.ts"],
|
|
4
|
-
"sourcesContent": ["import type { JSONSchema, GraffitiObject } from \"@graffiti-garden/api\";\nimport {\n getAuthorizationEndpoint,\n fetchWithErrorHandling,\n verifyHTTPSEndpoint,\n} from \"./utilities\";\nimport {\n compileGraffitiObjectSchema,\n GraffitiErrorCursorExpired,\n} from \"@graffiti-garden/api\";\nimport {\n encode as dagCborEncode,\n decode as dagCborDecode,\n} from \"@ipld/dag-cbor\";\nimport {\n type infer as infer_,\n string,\n url,\n array,\n optional,\n nullable,\n strictObject,\n looseObject,\n nonnegative,\n int,\n boolean,\n custom,\n number,\n union,\n} from \"zod/mini\";\n\nexport class Inboxes {\n getAuthorizationEndpoint = getAuthorizationEndpoint;\n protected cache_: Promise<Cache> | null = null;\n protected get cache() {\n if (!this.cache_) {\n this.cache_ = createCache();\n }\n return this.cache_;\n }\n\n async send(inboxUrl: string, message: Message<{}>): Promise<string> {\n verifyHTTPSEndpoint(inboxUrl);\n const url = `${inboxUrl}/send`;\n\n const response = await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n },\n body: new Uint8Array(dagCborEncode({ m: message })),\n });\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = SendResponseSchema.parse(cbor);\n return parsed.id;\n }\n\n async get(\n inboxUrl: string,\n messageId: string,\n inboxToken?: string | null,\n ): Promise<LabeledMessageBase> {\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const cache = await this.cache;\n const cached = await cache.messages.get(messageCacheKey);\n if (cached) return cached;\n\n const url = `${inboxUrl}/message/${messageId}`;\n const response = await fetchWithErrorHandling(url, {\n method: \"GET\",\n headers: {\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n });\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = LabeledMessageBaseSchema.parse(cbor);\n\n await cache.messages.set(messageCacheKey, parsed);\n return parsed;\n }\n\n async label(\n inboxUrl: string,\n messageId: string,\n label: number,\n inboxToken?: string | null,\n ): Promise<void> {\n verifyHTTPSEndpoint(inboxUrl);\n\n if (inboxToken) {\n const url = `${inboxUrl}/label/${messageId}`;\n\n await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n Authorization: `Bearer ${inboxToken}`,\n },\n body: new Uint8Array(dagCborEncode({ l: label })),\n });\n }\n\n // Update the cache, even if no token.\n // Therefore people not logged in do not need to\n // repeatedly re-validate objects.\n const cache = await this.cache;\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const result = await cache.messages.get(messageCacheKey);\n if (result) {\n await cache.messages.set(messageCacheKey, {\n ...result,\n l: label,\n });\n }\n }\n\n protected async fetchMessageBatch(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n cursor?: string,\n ) {\n const response = await fetchWithErrorHandling(\n `${inboxUrl}/${type}${cursor ? `?cursor=${encodeURIComponent(cursor)}` : \"\"}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n body,\n },\n );\n const retryAfterHeader = response.headers.get(\"Retry-After\");\n const retryAfter = retryAfterHeader\n ? parseInt(retryAfterHeader)\n : undefined;\n\n const waitTil =\n retryAfter && Number.isFinite(retryAfter)\n ? Date.now() + retryAfter * 1000\n : undefined;\n\n return { response, waitTil };\n }\n\n protected async *yieldFromCache(\n cache: Cache,\n inboxUrl: string,\n messageIdsCacheKey: string,\n cachedMessageIds: CacheQueryValue,\n cacheNumSeen: number = 0,\n ): AsyncGenerator<LabeledMessageBase> {\n // Filter out all messageIds before\n // the number already seen\n const messageIds = cachedMessageIds.messageIds.slice(cacheNumSeen);\n\n // Get all the messages pointed to in the cache\n const messages = await Promise.all(\n messageIds.map(async (id) => {\n const message = await cache.messages.get(\n getMessageCacheKey(inboxUrl, id),\n );\n if (!message) {\n // Something is very wrong with the cache,\n // it refers to message IDs that are not cached\n try {\n await cache.messageIds.del(messageIdsCacheKey);\n } catch {}\n throw new Error(\"Cache out of sync - perhaps clear browser storage\");\n }\n return message;\n }),\n );\n\n yield* messages;\n }\n\n protected async *lockedMessageStreamer<Schema extends JSONSchema>(\n ...args: Parameters<typeof this.messageStreamer<Schema>>\n ): MessageStream<Schema> {\n if (typeof window === \"undefined\" || !(await canUseIDB())) {\n // TODO: implement locking in node as well, but not\n // high priority since most use will be in browser\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n }\n\n // Request the lock\n const messageIdsCacheKey = await args[0];\n const lockKey = `graffiti:inbox:${messageIdsCacheKey}`;\n let releaseLock = () => {};\n let hasLock: boolean = false;\n await new Promise<void>((resolvehasLock) => {\n window.navigator.locks.request(\n lockKey,\n {\n mode: \"exclusive\",\n ifAvailable: true,\n },\n async (lock) => {\n // Immediately return whether we\n // acquired the lock or not\n hasLock = !!lock;\n resolvehasLock();\n\n // Then wait for the release to be called\n await new Promise<void>((r) => (releaseLock = r));\n },\n );\n });\n if (hasLock) {\n // If we have the lock, simply proceed with the regular streamer\n try {\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n } finally {\n // Release the lock when all done\n releaseLock();\n }\n }\n\n // Someone else has the lock,\n // so wait until the lock is released,\n // then just return from the cache\n releaseLock();\n await window.navigator.locks.request(lockKey, () => {});\n\n // TODO: the arguments here are brittle\n // at some point, refactor things\n const inboxUrl = args[1];\n const objectSchema = args[5] ?? {};\n const cacheVersion = args[6];\n const cacheNumSeen = args[7];\n\n const cache = await this.cache;\n const cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (!cachedMessageIds) {\n throw new Error(\"Cache not found\");\n }\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cachedMessageIds.messageIds.length,\n version: cachedMessageIds.version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n protected async *messageStreamer<Schema extends JSONSchema>(\n messageIdsCacheKey_: Promise<string>,\n inboxUrl: string,\n type: \"export\" | \"query\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n objectSchema: Schema = {} as Schema,\n cacheVersion?: string,\n cacheNumSeen: number = 0,\n ): MessageStream<Schema> {\n const validator = await compileGraffitiObjectSchema(objectSchema);\n const messageIdsCacheKey = await messageIdsCacheKey_;\n const cache = await this.cache;\n\n let cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds?.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n // If we are rate-limited, wait\n let waitTil = cachedMessageIds?.waitTil;\n await waitFor(waitTil);\n\n // See if the cursor is still active by\n // requesting an initial batch of messages\n const cachedCursor = cachedMessageIds?.cursor;\n let firstResponse: Response | undefined = undefined;\n try {\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n cachedCursor,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n } catch (e) {\n if (!(e instanceof GraffitiErrorCursorExpired && cachedCursor)) {\n console.error(\n \"Unexpected error in stream, waiting 5 seconds before continuing...\",\n );\n await new Promise((resolve) => setTimeout(resolve, 5000));\n throw e;\n }\n\n // The cursor is stale\n await cache.messageIds.del(messageIdsCacheKey);\n if (cacheVersion === undefined) {\n // The query is not a continuation\n // so we can effectively ignore the error\n cachedMessageIds = undefined;\n } else {\n // Otherwise propogate it up so the\n // consumer can clear their message history\n throw e;\n }\n }\n\n if (firstResponse !== undefined && cachedMessageIds) {\n // Cursor is valid! Yield from the cache\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n }\n\n if (firstResponse === undefined) {\n // The cursor was stale: try again\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n }\n\n // Continue streaming results\n let response = firstResponse;\n let cursor: string;\n const version = cachedMessageIds?.version ?? crypto.randomUUID();\n let messageIds = cachedMessageIds?.messageIds ?? [];\n while (true) {\n const blob = await response.blob();\n const decoded = dagCborDecode(await blob.arrayBuffer());\n const {\n results,\n hasMore,\n cursor: nextCursor,\n } = MessageResultSchema.parse(decoded);\n cursor = nextCursor;\n\n const labeledMessages: LabeledMessage<Schema>[] = results.map(\n (result) => {\n const object =\n result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY];\n if (!validator(object)) {\n throw new Error(\"Server returned data that does not match schema\");\n }\n return {\n ...result,\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n ...result[LABELED_MESSAGE_MESSAGE_KEY],\n [MESSAGE_OBJECT_KEY]: object,\n },\n };\n },\n );\n\n // First cache the messages with their labels\n await Promise.all(\n labeledMessages.map((m: LabeledMessageBase) =>\n cache.messages.set(\n getMessageCacheKey(inboxUrl, m[LABELED_MESSAGE_ID_KEY]),\n m,\n ),\n ),\n );\n // Then store all the messageids\n messageIds = [\n ...messageIds,\n ...labeledMessages.map(\n (m: LabeledMessageBase) => m[LABELED_MESSAGE_ID_KEY],\n ),\n ];\n await cache.messageIds.set(messageIdsCacheKey, {\n cursor,\n version,\n messageIds,\n waitTil,\n });\n\n // Update how many we've seen\n cacheNumSeen += labeledMessages.length;\n\n // Return the values\n for (const m of labeledMessages) yield m;\n\n if (!hasMore) break;\n\n // Otherwise get another response (after waiting for rate-limit)\n await waitFor(waitTil);\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n undefined, // Body is never past the first time\n inboxToken,\n cursor,\n );\n response = out.response;\n waitTil = out.waitTil;\n }\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cacheNumSeen,\n version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n query<Schema extends JSONSchema>(\n inboxUrl: string,\n tags: Uint8Array[],\n objectSchema: Schema,\n inboxToken?: string | null,\n ): MessageStream<Schema> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const body = dagCborEncode({\n tags,\n schema: objectSchema,\n });\n\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"query\", body);\n return this.lockedMessageStreamer<Schema>(\n messageIdsCacheKey,\n inboxUrl,\n \"query\",\n new Uint8Array(body),\n inboxToken,\n objectSchema,\n );\n }\n\n continueQuery(\n inboxUrl: string,\n cursor: string,\n inboxToken?: string | null,\n ): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const decodedCursor = JSON.parse(cursor);\n const { messageIdsCacheKey, numSeen, objectSchema, version } =\n CursorSchema.parse(decodedCursor);\n\n return this.lockedMessageStreamer<{}>(\n Promise.resolve(messageIdsCacheKey),\n inboxUrl,\n \"query\",\n undefined,\n inboxToken,\n objectSchema,\n version,\n numSeen,\n );\n }\n\n export(inboxUrl: string, inboxToken: string): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"export\");\n return this.lockedMessageStreamer<{}>(\n messageIdsCacheKey,\n inboxUrl,\n \"export\",\n undefined,\n inboxToken,\n );\n }\n}\n\nconst GraffitiObjectSchema = strictObject({\n value: looseObject({}),\n channels: array(string()),\n allowed: optional(nullable(array(url()))),\n url: url(),\n actor: url(),\n});\nexport const Uint8ArraySchema = custom<Uint8Array>(\n (v): v is Uint8Array => v instanceof Uint8Array,\n);\nexport const TagsSchema = array(Uint8ArraySchema);\n\nexport const MESSAGE_TAGS_KEY = \"t\";\nexport const MESSAGE_OBJECT_KEY = \"o\";\nexport const MESSAGE_METADATA_KEY = \"m\";\nexport const MessageBaseSchema = strictObject({\n [MESSAGE_TAGS_KEY]: TagsSchema,\n [MESSAGE_OBJECT_KEY]: GraffitiObjectSchema,\n [MESSAGE_METADATA_KEY]: Uint8ArraySchema,\n});\ntype MessageBase = infer_<typeof MessageBaseSchema>;\n\nexport const LABELED_MESSAGE_ID_KEY = \"id\";\nexport const LABELED_MESSAGE_MESSAGE_KEY = \"m\";\nexport const LABELED_MESSAGE_LABEL_KEY = \"l\";\nexport const LabeledMessageBaseSchema = strictObject({\n [LABELED_MESSAGE_ID_KEY]: string(),\n [LABELED_MESSAGE_MESSAGE_KEY]: MessageBaseSchema,\n [LABELED_MESSAGE_LABEL_KEY]: number(),\n});\ntype LabeledMessageBase = infer_<typeof LabeledMessageBaseSchema>;\n\nexport type Message<Schema extends JSONSchema> = MessageBase & {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n};\nexport type LabeledMessage<Schema extends JSONSchema> = LabeledMessageBase & {\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n };\n};\n\nconst SendResponseSchema = strictObject({ id: string() });\n\nconst MessageResultSchema = strictObject({\n results: array(LabeledMessageBaseSchema),\n hasMore: boolean(),\n cursor: string(),\n});\n\nconst CursorSchema = strictObject({\n messageIdsCacheKey: string(),\n version: string(),\n numSeen: int().check(nonnegative()),\n objectSchema: union([looseObject({}), boolean()]),\n});\n\nexport interface MessageStream<\n Schema extends JSONSchema,\n> extends AsyncGenerator<LabeledMessage<Schema>, string> {}\n\ntype CacheQueryValue = {\n cursor: string;\n version: string;\n messageIds: string[];\n waitTil?: number;\n};\n\nasync function canUseIDB(): Promise<boolean> {\n try {\n if (!globalThis.indexedDB) return false;\n\n // Small probe database\n await new Promise<void>((resolve, reject) => {\n const req = indexedDB.open(\"__idb_probe__\", 1);\n req.onupgradeneeded = () => req.result.createObjectStore(\"k\");\n req.onsuccess = () => {\n req.result.close();\n resolve();\n };\n req.onerror = () => reject(req.error);\n });\n\n return true;\n } catch {\n return false;\n }\n}\n\ntype Cache = {\n messages: {\n get(k: string): Promise<LabeledMessageBase | undefined>;\n set(k: string, value: LabeledMessageBase): Promise<void>;\n del(k: string): Promise<void>;\n };\n messageIds: {\n get(k: string): Promise<CacheQueryValue | undefined>;\n set(k: string, value: CacheQueryValue): Promise<void>;\n del(k: string): Promise<void>;\n };\n};\n\nfunction getMessageCacheKey(inboxUrl: string, messageId: string) {\n return `${encodeURIComponent(inboxUrl)}:${encodeURIComponent(messageId)}`;\n}\nasync function getMessageIdsCacheKey(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body?: Uint8Array,\n): Promise<string> {\n const cacheIdData = dagCborEncode({\n inboxUrl,\n type,\n body: body ?? null,\n });\n return crypto.subtle\n .digest(\"SHA-256\", new Uint8Array(cacheIdData))\n .then((bytes) =>\n Array.from(new Uint8Array(bytes))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\"),\n );\n}\n\nasync function resetCacheDB() {\n await new Promise<void>((resolve) => {\n const req = indexedDB.deleteDatabase(\"graffiti-inbox-cache\");\n req.onsuccess = () => resolve();\n req.onerror = () => resolve(); // best effort\n req.onblocked = () => resolve(); // best effort\n });\n}\n\nasync function createCache(): Promise<Cache> {\n if (await canUseIDB()) {\n const { openDB } = await import(\"idb\");\n const db = await openDB(\"graffiti-inbox-cache\", 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(\"m\")) db.createObjectStore(\"m\");\n if (!db.objectStoreNames.contains(\"q\")) db.createObjectStore(\"q\");\n },\n });\n\n return {\n messages: {\n get: async (k) => {\n try {\n return db.get(\"m\", k);\n } catch (error) {\n console.error(\"Error getting message from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"m\", v, k);\n },\n del: (k) => db.delete(\"m\", k),\n },\n messageIds: {\n get: async (k) => {\n try {\n return await db.get(\"q\", k);\n } catch (error) {\n console.error(\"Error getting message IDs from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"q\", v, k);\n },\n del: (k) => db.delete(\"q\", k),\n },\n };\n }\n\n const m = new Map<string, LabeledMessageBase>();\n const q = new Map<string, CacheQueryValue>();\n\n return {\n messages: {\n get: async (k) => m.get(k),\n set: async (k, v) => void m.set(k, v),\n del: async (k) => void m.delete(k),\n },\n messageIds: {\n get: async (k) => q.get(k),\n set: async (k, v) => void q.set(k, v),\n del: async (k) => void q.delete(k),\n },\n };\n}\n\nasync function waitFor(waitTil?: number) {\n if (waitTil !== undefined) {\n const waitFor = waitTil - Date.now();\n if (waitFor > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitFor));\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { JSONSchema, GraffitiObject } from \"@graffiti-garden/api\";\nimport {\n getAuthorizationEndpoint,\n fetchWithErrorHandling,\n verifyHTTPSEndpoint,\n} from \"./utilities\";\nimport {\n compileGraffitiObjectSchema,\n GraffitiErrorCursorExpired,\n GraffitiErrorNotFound,\n} from \"@graffiti-garden/api\";\nimport {\n encode as dagCborEncode,\n decode as dagCborDecode,\n} from \"@ipld/dag-cbor\";\nimport {\n type infer as infer_,\n string,\n url,\n array,\n optional,\n nullable,\n strictObject,\n looseObject,\n nonnegative,\n int,\n boolean,\n custom,\n number,\n union,\n} from \"zod/mini\";\n\nexport class Inboxes {\n getAuthorizationEndpoint = getAuthorizationEndpoint;\n protected cache_: Promise<Cache> | null = null;\n protected get cache() {\n if (!this.cache_) {\n this.cache_ = createCache();\n }\n return this.cache_;\n }\n\n async send(inboxUrl: string, message: Message<{}>): Promise<string> {\n verifyHTTPSEndpoint(inboxUrl);\n const url = `${inboxUrl}/send`;\n\n const response = await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n },\n body: new Uint8Array(dagCborEncode({ m: message })),\n });\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = SendResponseSchema.parse(cbor);\n return parsed.id;\n }\n\n async get(\n inboxUrl: string,\n messageId: string,\n inboxToken?: string | null,\n ): Promise<LabeledMessageBase | null> {\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const cache = await this.cache;\n const cached = await cache.messages.get(messageCacheKey);\n if (cached !== undefined) return cached;\n\n const url = `${inboxUrl}/message/${messageId}`;\n let response: Response | null = null;\n try {\n response = await fetchWithErrorHandling(url, {\n method: \"GET\",\n headers: {\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n });\n } catch (e) {\n if (e instanceof GraffitiErrorNotFound) {\n await cache.messages.set(messageCacheKey, null);\n return null;\n }\n throw e;\n }\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = LabeledMessageBaseSchema.parse(cbor);\n\n await cache.messages.set(messageCacheKey, parsed);\n return parsed;\n }\n\n async label(\n inboxUrl: string,\n messageId: string,\n label: number,\n inboxToken?: string | null,\n ): Promise<void> {\n verifyHTTPSEndpoint(inboxUrl);\n\n if (inboxToken) {\n const url = `${inboxUrl}/label/${messageId}`;\n\n await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n Authorization: `Bearer ${inboxToken}`,\n },\n body: new Uint8Array(dagCborEncode({ l: label })),\n });\n }\n\n // Update the cache, even if no token.\n // Therefore people not logged in do not need to\n // repeatedly re-validate objects.\n const cache = await this.cache;\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const result = await cache.messages.get(messageCacheKey);\n if (result) {\n await cache.messages.set(messageCacheKey, {\n ...result,\n l: label,\n });\n }\n }\n\n protected async fetchMessageBatch(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n cursor?: string,\n ) {\n const response = await fetchWithErrorHandling(\n `${inboxUrl}/${type}${cursor ? `?cursor=${encodeURIComponent(cursor)}` : \"\"}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n body,\n },\n );\n const retryAfterHeader = response.headers.get(\"Retry-After\");\n const retryAfter = retryAfterHeader\n ? parseInt(retryAfterHeader)\n : undefined;\n\n const waitTil =\n retryAfter && Number.isFinite(retryAfter)\n ? Date.now() + retryAfter * 1000\n : undefined;\n\n return { response, waitTil };\n }\n\n protected async *yieldFromCache(\n cache: Cache,\n inboxUrl: string,\n messageIdsCacheKey: string,\n cachedMessageIds: CacheQueryValue,\n cacheNumSeen: number = 0,\n ): AsyncGenerator<LabeledMessageBase> {\n // Filter out all messageIds before\n // the number already seen\n const messageIds = cachedMessageIds.messageIds.slice(cacheNumSeen);\n\n // Get all the messages pointed to in the cache\n const messages = await Promise.all(\n messageIds.map(async (id) => {\n const message = await cache.messages.get(\n getMessageCacheKey(inboxUrl, id),\n );\n if (!message) {\n // Something is very wrong with the cache,\n // it refers to message IDs that are not cached\n try {\n await cache.messageIds.del(messageIdsCacheKey);\n } catch {}\n throw new Error(\"Cache out of sync - perhaps clear browser storage\");\n }\n return message;\n }),\n );\n\n yield* messages;\n }\n\n protected async *lockedMessageStreamer<Schema extends JSONSchema>(\n ...args: Parameters<typeof this.messageStreamer<Schema>>\n ): MessageStream<Schema> {\n if (typeof window === \"undefined\" || !(await canUseIDB())) {\n // TODO: implement locking in node as well, but not\n // high priority since most use will be in browser\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n }\n\n // Request the lock\n const messageIdsCacheKey = await args[0];\n const lockKey = `graffiti:inbox:${messageIdsCacheKey}`;\n let releaseLock = () => {};\n let hasLock: boolean = false;\n await new Promise<void>((resolvehasLock) => {\n window.navigator.locks.request(\n lockKey,\n {\n mode: \"exclusive\",\n ifAvailable: true,\n },\n async (lock) => {\n // Immediately return whether we\n // acquired the lock or not\n hasLock = !!lock;\n resolvehasLock();\n\n // Then wait for the release to be called\n await new Promise<void>((r) => (releaseLock = r));\n },\n );\n });\n if (hasLock) {\n // If we have the lock, simply proceed with the regular streamer\n try {\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n } finally {\n // Release the lock when all done\n releaseLock();\n }\n }\n\n // Someone else has the lock,\n // so wait until the lock is released,\n // then just return from the cache\n releaseLock();\n await window.navigator.locks.request(lockKey, () => {});\n\n // TODO: the arguments here are brittle\n // at some point, refactor things\n const inboxUrl = args[1];\n const objectSchema = args[5] ?? {};\n const cacheVersion = args[6];\n const cacheNumSeen = args[7];\n\n const cache = await this.cache;\n const cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (!cachedMessageIds) {\n throw new Error(\"Cache not found\");\n }\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cachedMessageIds.messageIds.length,\n version: cachedMessageIds.version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n protected async *messageStreamer<Schema extends JSONSchema>(\n messageIdsCacheKey_: Promise<string>,\n inboxUrl: string,\n type: \"export\" | \"query\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n objectSchema: Schema = {} as Schema,\n cacheVersion?: string,\n cacheNumSeen: number = 0,\n ): MessageStream<Schema> {\n const validator = await compileGraffitiObjectSchema(objectSchema);\n const messageIdsCacheKey = await messageIdsCacheKey_;\n const cache = await this.cache;\n\n let cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds?.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n // If we are rate-limited, wait\n let waitTil = cachedMessageIds?.waitTil;\n await waitFor(waitTil);\n\n // See if the cursor is still active by\n // requesting an initial batch of messages\n const cachedCursor = cachedMessageIds?.cursor;\n let firstResponse: Response | undefined = undefined;\n try {\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n cachedCursor,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n } catch (e) {\n if (!(e instanceof GraffitiErrorCursorExpired && cachedCursor)) {\n console.error(\n \"Unexpected error in stream, waiting 5 seconds before continuing...\",\n );\n await new Promise((resolve) => setTimeout(resolve, 5000));\n throw e;\n }\n\n // The cursor is stale\n await cache.messageIds.del(messageIdsCacheKey);\n if (cacheVersion === undefined) {\n // The query is not a continuation\n // so we can effectively ignore the error\n cachedMessageIds = undefined;\n } else {\n // Otherwise propogate it up so the\n // consumer can clear their message history\n throw e;\n }\n }\n\n if (firstResponse !== undefined && cachedMessageIds) {\n // Cursor is valid! Yield from the cache\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n }\n\n if (firstResponse === undefined) {\n // The cursor was stale: try again\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n }\n\n // Continue streaming results\n let response = firstResponse;\n let cursor: string;\n const version = cachedMessageIds?.version ?? crypto.randomUUID();\n let messageIds = cachedMessageIds?.messageIds ?? [];\n while (true) {\n const blob = await response.blob();\n const decoded = dagCborDecode(await blob.arrayBuffer());\n const {\n results,\n hasMore,\n cursor: nextCursor,\n } = MessageResultSchema.parse(decoded);\n cursor = nextCursor;\n\n const labeledMessages: LabeledMessage<Schema>[] = results.map(\n (result) => {\n const object =\n result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY];\n if (!validator(object)) {\n throw new Error(\"Server returned data that does not match schema\");\n }\n return {\n ...result,\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n ...result[LABELED_MESSAGE_MESSAGE_KEY],\n [MESSAGE_OBJECT_KEY]: object,\n },\n };\n },\n );\n\n // First cache the messages with their labels\n await Promise.all(\n labeledMessages.map((m: LabeledMessageBase) =>\n cache.messages.set(\n getMessageCacheKey(inboxUrl, m[LABELED_MESSAGE_ID_KEY]),\n m,\n ),\n ),\n );\n // Then store all the messageids\n messageIds = [\n ...messageIds,\n ...labeledMessages.map(\n (m: LabeledMessageBase) => m[LABELED_MESSAGE_ID_KEY],\n ),\n ];\n await cache.messageIds.set(messageIdsCacheKey, {\n cursor,\n version,\n messageIds,\n waitTil,\n });\n\n // Update how many we've seen\n cacheNumSeen += labeledMessages.length;\n\n // Return the values\n for (const m of labeledMessages) yield m;\n\n if (!hasMore) break;\n\n // Otherwise get another response (after waiting for rate-limit)\n await waitFor(waitTil);\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n undefined, // Body is never past the first time\n inboxToken,\n cursor,\n );\n response = out.response;\n waitTil = out.waitTil;\n }\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cacheNumSeen,\n version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n query<Schema extends JSONSchema>(\n inboxUrl: string,\n tags: Uint8Array[],\n objectSchema: Schema,\n inboxToken?: string | null,\n ): MessageStream<Schema> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const body = dagCborEncode({\n tags,\n schema: objectSchema,\n });\n\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"query\", body);\n return this.lockedMessageStreamer<Schema>(\n messageIdsCacheKey,\n inboxUrl,\n \"query\",\n new Uint8Array(body),\n inboxToken,\n objectSchema,\n );\n }\n\n continueQuery(\n inboxUrl: string,\n cursor: string,\n inboxToken?: string | null,\n ): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const decodedCursor = JSON.parse(cursor);\n const { messageIdsCacheKey, numSeen, objectSchema, version } =\n CursorSchema.parse(decodedCursor);\n\n return this.lockedMessageStreamer<{}>(\n Promise.resolve(messageIdsCacheKey),\n inboxUrl,\n \"query\",\n undefined,\n inboxToken,\n objectSchema,\n version,\n numSeen,\n );\n }\n\n export(inboxUrl: string, inboxToken: string): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"export\");\n return this.lockedMessageStreamer<{}>(\n messageIdsCacheKey,\n inboxUrl,\n \"export\",\n undefined,\n inboxToken,\n );\n }\n}\n\nconst GraffitiObjectSchema = strictObject({\n value: looseObject({}),\n channels: array(string()),\n allowed: optional(nullable(array(url()))),\n url: url(),\n actor: url(),\n});\nexport const Uint8ArraySchema = custom<Uint8Array>(\n (v): v is Uint8Array => v instanceof Uint8Array,\n);\nexport const TagsSchema = array(Uint8ArraySchema);\n\nexport const MESSAGE_TAGS_KEY = \"t\";\nexport const MESSAGE_OBJECT_KEY = \"o\";\nexport const MESSAGE_METADATA_KEY = \"m\";\nexport const MessageBaseSchema = strictObject({\n [MESSAGE_TAGS_KEY]: TagsSchema,\n [MESSAGE_OBJECT_KEY]: GraffitiObjectSchema,\n [MESSAGE_METADATA_KEY]: Uint8ArraySchema,\n});\ntype MessageBase = infer_<typeof MessageBaseSchema>;\n\nexport const LABELED_MESSAGE_ID_KEY = \"id\";\nexport const LABELED_MESSAGE_MESSAGE_KEY = \"m\";\nexport const LABELED_MESSAGE_LABEL_KEY = \"l\";\nexport const LabeledMessageBaseSchema = strictObject({\n [LABELED_MESSAGE_ID_KEY]: string(),\n [LABELED_MESSAGE_MESSAGE_KEY]: MessageBaseSchema,\n [LABELED_MESSAGE_LABEL_KEY]: number(),\n});\ntype LabeledMessageBase = infer_<typeof LabeledMessageBaseSchema>;\n\nexport type Message<Schema extends JSONSchema> = MessageBase & {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n};\nexport type LabeledMessage<Schema extends JSONSchema> = LabeledMessageBase & {\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n };\n};\n\nconst SendResponseSchema = strictObject({ id: string() });\n\nconst MessageResultSchema = strictObject({\n results: array(LabeledMessageBaseSchema),\n hasMore: boolean(),\n cursor: string(),\n});\n\nconst CursorSchema = strictObject({\n messageIdsCacheKey: string(),\n version: string(),\n numSeen: int().check(nonnegative()),\n objectSchema: union([looseObject({}), boolean()]),\n});\n\nexport interface MessageStream<\n Schema extends JSONSchema,\n> extends AsyncGenerator<LabeledMessage<Schema>, string> {}\n\ntype CacheQueryValue = {\n cursor: string;\n version: string;\n messageIds: string[];\n waitTil?: number;\n};\n\nasync function canUseIDB(): Promise<boolean> {\n try {\n if (!globalThis.indexedDB) return false;\n\n // Small probe database\n await new Promise<void>((resolve, reject) => {\n const req = indexedDB.open(\"__idb_probe__\", 1);\n req.onupgradeneeded = () => req.result.createObjectStore(\"k\");\n req.onsuccess = () => {\n req.result.close();\n resolve();\n };\n req.onerror = () => reject(req.error);\n });\n\n return true;\n } catch {\n return false;\n }\n}\n\ntype Cache = {\n messages: {\n get(k: string): Promise<LabeledMessageBase | null | undefined>;\n set(k: string, value: LabeledMessageBase | null): Promise<void>;\n del(k: string): Promise<void>;\n };\n messageIds: {\n get(k: string): Promise<CacheQueryValue | undefined>;\n set(k: string, value: CacheQueryValue): Promise<void>;\n del(k: string): Promise<void>;\n };\n};\n\nfunction getMessageCacheKey(inboxUrl: string, messageId: string) {\n return `${encodeURIComponent(inboxUrl)}:${encodeURIComponent(messageId)}`;\n}\nasync function getMessageIdsCacheKey(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body?: Uint8Array,\n): Promise<string> {\n const cacheIdData = dagCborEncode({\n inboxUrl,\n type,\n body: body ?? null,\n });\n return crypto.subtle\n .digest(\"SHA-256\", new Uint8Array(cacheIdData))\n .then((bytes) =>\n Array.from(new Uint8Array(bytes))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\"),\n );\n}\n\nasync function resetCacheDB() {\n await new Promise<void>((resolve) => {\n const req = indexedDB.deleteDatabase(\"graffiti-inbox-cache\");\n req.onsuccess = () => resolve();\n req.onerror = () => resolve(); // best effort\n req.onblocked = () => resolve(); // best effort\n });\n}\n\nasync function createCache(): Promise<Cache> {\n if (await canUseIDB()) {\n const { openDB } = await import(\"idb\");\n const db = await openDB(\"graffiti-inbox-cache\", 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(\"m\")) db.createObjectStore(\"m\");\n if (!db.objectStoreNames.contains(\"q\")) db.createObjectStore(\"q\");\n },\n });\n\n return {\n messages: {\n get: async (k) => {\n try {\n return db.get(\"m\", k);\n } catch (error) {\n console.error(\"Error getting message from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"m\", v, k);\n },\n del: (k) => db.delete(\"m\", k),\n },\n messageIds: {\n get: async (k) => {\n try {\n return await db.get(\"q\", k);\n } catch (error) {\n console.error(\"Error getting message IDs from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"q\", v, k);\n },\n del: (k) => db.delete(\"q\", k),\n },\n };\n }\n\n const m = new Map<string, LabeledMessageBase | null>();\n const q = new Map<string, CacheQueryValue>();\n\n return {\n messages: {\n get: async (k) => m.get(k),\n set: async (k, v) => void m.set(k, v),\n del: async (k) => void m.delete(k),\n },\n messageIds: {\n get: async (k) => q.get(k),\n set: async (k, v) => void q.set(k, v),\n del: async (k) => void q.delete(k),\n },\n };\n}\n\nasync function waitFor(waitTil?: number) {\n if (waitTil !== undefined) {\n const waitFor = waitTil - Date.now();\n if (waitFor > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitFor));\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["url", "dagCborEncode", "dagCborDecode", "db", "waitFor"]
|
|
7
7
|
}
|
|
@@ -177,11 +177,7 @@ class GraffitiDecentralized {
|
|
|
177
177
|
);
|
|
178
178
|
input?.setAttribute("value", proposedHandle);
|
|
179
179
|
input?.addEventListener("focus", () => input?.select());
|
|
180
|
-
|
|
181
|
-
setTimeout(() => r(), 0);
|
|
182
|
-
}).then(() => {
|
|
183
|
-
input?.focus();
|
|
184
|
-
});
|
|
180
|
+
setTimeout(() => input?.focus(), 0);
|
|
185
181
|
template?.querySelector("#graffiti-login-handle-form")?.addEventListener("submit", async (e) => {
|
|
186
182
|
e.preventDefault();
|
|
187
183
|
input?.setAttribute("disabled", "true");
|
|
@@ -222,11 +218,12 @@ class GraffitiDecentralized {
|
|
|
222
218
|
e.preventDefault();
|
|
223
219
|
this.login_("");
|
|
224
220
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
221
|
+
setTimeout(
|
|
222
|
+
() => template?.querySelector(
|
|
223
|
+
"#graffiti-login-new"
|
|
224
|
+
)?.focus(),
|
|
225
|
+
0
|
|
226
|
+
);
|
|
230
227
|
}
|
|
231
228
|
const createUrl = new URL(this.identityCreatorEndpoint);
|
|
232
229
|
createUrl.searchParams.set(
|
|
@@ -759,7 +756,7 @@ class GraffitiDecentralized {
|
|
|
759
756
|
if (itResult.done) return itResult.value;
|
|
760
757
|
const result = itResult.value;
|
|
761
758
|
const label = result.l;
|
|
762
|
-
if (label !== MESSAGE_LABEL_VALID && label !== MESSAGE_LABEL_UNLABELED)
|
|
759
|
+
if (label !== MESSAGE_LABEL_VALID && label !== MESSAGE_LABEL_UNLABELED && label !== MESSAGE_LABEL_TRASH)
|
|
763
760
|
continue;
|
|
764
761
|
const messageId = result.id;
|
|
765
762
|
const { o: object, m: metadataBytes, t: receivedTags } = result.m;
|
|
@@ -792,6 +789,33 @@ class GraffitiDecentralized {
|
|
|
792
789
|
announcements
|
|
793
790
|
};
|
|
794
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
|
|
798
|
+
);
|
|
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) {
|
|
802
|
+
this.inboxes.label(
|
|
803
|
+
inboxEndpoint,
|
|
804
|
+
tombstonedMessageId,
|
|
805
|
+
MESSAGE_LABEL_TRASH,
|
|
806
|
+
inboxToken
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
yield {
|
|
810
|
+
messageId,
|
|
811
|
+
tombstone: true,
|
|
812
|
+
object,
|
|
813
|
+
storageBucketKey,
|
|
814
|
+
allowedTickets,
|
|
815
|
+
tags: receivedTags,
|
|
816
|
+
announcements
|
|
817
|
+
};
|
|
818
|
+
continue;
|
|
795
819
|
}
|
|
796
820
|
let validationError = void 0;
|
|
797
821
|
try {
|
|
@@ -838,7 +862,11 @@ class GraffitiDecentralized {
|
|
|
838
862
|
if (tombstonedMessageId) {
|
|
839
863
|
if (validationError instanceof import_api.GraffitiErrorNotFound) {
|
|
840
864
|
this.inboxes.get(inboxEndpoint, tombstonedMessageId, inboxToken).then((result2) => {
|
|
841
|
-
if (
|
|
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
|
+
) {
|
|
842
870
|
this.inboxes.label(
|
|
843
871
|
inboxEndpoint,
|
|
844
872
|
tombstonedMessageId,
|