@graffiti-garden/api 1.0.4 → 1.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.
- package/dist/browser/ajv-Z3ABRV2J.js +9 -0
- package/dist/browser/ajv-Z3ABRV2J.js.map +7 -0
- package/dist/browser/chunk-YVLJX3HV.js +2 -0
- package/dist/browser/chunk-YVLJX3HV.js.map +7 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/index.js.map +7 -0
- package/dist/cjs/1-api.js +26 -0
- package/dist/cjs/1-api.js.map +7 -0
- package/dist/cjs/2-types.js +41 -0
- package/dist/cjs/2-types.js.map +7 -0
- package/dist/cjs/3-errors.js +71 -0
- package/dist/cjs/3-errors.js.map +7 -0
- package/dist/cjs/4-utilities.js +84 -0
- package/dist/cjs/4-utilities.js.map +7 -0
- package/dist/cjs/5-runtime-types.js +181 -0
- package/dist/cjs/5-runtime-types.js.map +7 -0
- package/dist/cjs/index.js +23 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/esm/1-api.js +6 -0
- package/dist/esm/1-api.js.map +7 -0
- package/dist/esm/2-types.js +21 -0
- package/dist/esm/2-types.js.map +7 -0
- package/dist/esm/3-errors.js +51 -0
- package/dist/esm/3-errors.js.map +7 -0
- package/dist/esm/4-utilities.js +54 -0
- package/dist/esm/4-utilities.js.map +7 -0
- package/dist/esm/5-runtime-types.js +174 -0
- package/dist/esm/5-runtime-types.js.map +7 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/src/1-api.d.ts +1 -1
- package/dist/src/1-api.d.ts.map +1 -1
- package/dist/src/4-utilities.d.ts +3 -1
- package/dist/src/4-utilities.d.ts.map +1 -1
- package/dist/src/5-runtime-types.d.ts +55 -0
- package/dist/src/5-runtime-types.d.ts.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/tests/discover.d.ts.map +1 -1
- package/dist/tests/utils.d.ts +1 -0
- package/dist/tests/utils.d.ts.map +1 -1
- package/dist/tests.mjs +11 -7
- package/dist/tests.mjs.map +2 -2
- package/package.json +10 -7
- package/src/1-api.ts +1 -1
- package/src/4-utilities.ts +28 -0
- package/src/5-runtime-types.ts +205 -0
- package/src/index.ts +1 -0
- package/tests/crud.ts +4 -4
- package/tests/discover.ts +4 -3
- package/tests/media.ts +2 -2
- package/tests/utils.ts +3 -0
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -7
- package/dist/index.mjs +0 -2
- package/dist/index.mjs.map +0 -7
package/dist/tests.mjs
CHANGED
|
@@ -15,6 +15,9 @@ function randomString() {
|
|
|
15
15
|
const str = Array.from(array).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
16
16
|
return str + "\u{1F469}\u{1F3FD}\u200D\u2764\uFE0F\u200D\u{1F48B}\u200D\u{1F469}\u{1F3FB}\u{1FAF1}\u{1F3FC}\u200D\u{1FAF2}\u{1F3FF}";
|
|
17
17
|
}
|
|
18
|
+
function randomUrl() {
|
|
19
|
+
return "test:" + randomString();
|
|
20
|
+
}
|
|
18
21
|
function randomValue() {
|
|
19
22
|
return {
|
|
20
23
|
[randomString()]: randomString()
|
|
@@ -62,7 +65,7 @@ var graffitiCRUDTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
62
65
|
session2 = await useSession2();
|
|
63
66
|
});
|
|
64
67
|
it("get nonexistant object", async () => {
|
|
65
|
-
await expect(graffiti.get(
|
|
68
|
+
await expect(graffiti.get(randomUrl(), {})).rejects.toThrow(
|
|
66
69
|
GraffitiErrorNotFound
|
|
67
70
|
);
|
|
68
71
|
});
|
|
@@ -196,7 +199,7 @@ var graffitiCRUDTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
196
199
|
const value = {
|
|
197
200
|
um: "hi"
|
|
198
201
|
};
|
|
199
|
-
const allowed = [
|
|
202
|
+
const allowed = [randomUrl()];
|
|
200
203
|
const channels = [randomString()];
|
|
201
204
|
const posted = await graffiti.post(
|
|
202
205
|
{ value, allowed, channels },
|
|
@@ -219,7 +222,7 @@ var graffitiCRUDTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
219
222
|
const value = {
|
|
220
223
|
um: "hi"
|
|
221
224
|
};
|
|
222
|
-
const allowed = [
|
|
225
|
+
const allowed = [randomUrl(), session2.actor, randomUrl()];
|
|
223
226
|
const channels = [randomString()];
|
|
224
227
|
const posted = await graffiti.post(
|
|
225
228
|
{
|
|
@@ -294,7 +297,7 @@ var graffitiDiscoverTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
294
297
|
});
|
|
295
298
|
it2("discover not allowed", async () => {
|
|
296
299
|
const object = randomPostObject();
|
|
297
|
-
object.allowed = [
|
|
300
|
+
object.allowed = [randomUrl(), randomUrl()];
|
|
298
301
|
const posted = await graffiti.post(object, session1);
|
|
299
302
|
const iteratorSession1 = graffiti.discover(
|
|
300
303
|
object.channels,
|
|
@@ -314,7 +317,7 @@ var graffitiDiscoverTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
314
317
|
});
|
|
315
318
|
it2("discover allowed", async () => {
|
|
316
319
|
const object = randomPostObject();
|
|
317
|
-
object.allowed = [
|
|
320
|
+
object.allowed = [randomUrl(), session2.actor, randomUrl()];
|
|
318
321
|
const posted = await graffiti.post(object, session1);
|
|
319
322
|
const iteratorSession2 = graffiti.discover(
|
|
320
323
|
object.channels,
|
|
@@ -358,7 +361,7 @@ var graffitiDiscoverTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
358
361
|
});
|
|
359
362
|
it2("discover schema allowed, as and not as owner", async () => {
|
|
360
363
|
const object = randomPostObject();
|
|
361
|
-
object.allowed = [
|
|
364
|
+
object.allowed = [randomUrl(), session2.actor, randomUrl()];
|
|
362
365
|
await graffiti.post(object, session1);
|
|
363
366
|
const iteratorSession1 = graffiti.discover(
|
|
364
367
|
object.channels,
|
|
@@ -750,7 +753,7 @@ var graffitiMediaTests = (useGraffiti, useSession1, useSession2) => {
|
|
|
750
753
|
it3("allowed", async () => {
|
|
751
754
|
const text = randomString();
|
|
752
755
|
const data = new Blob([text], { type: "text/plain" });
|
|
753
|
-
const allowed = [
|
|
756
|
+
const allowed = [randomUrl(), session2.actor, randomUrl()];
|
|
754
757
|
const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);
|
|
755
758
|
const media = await graffiti.getMedia(mediaUrl, {}, session1);
|
|
756
759
|
expect3(await media.data.text()).toEqual(text);
|
|
@@ -777,6 +780,7 @@ export {
|
|
|
777
780
|
nextStreamValue,
|
|
778
781
|
randomPostObject,
|
|
779
782
|
randomString,
|
|
783
|
+
randomUrl,
|
|
780
784
|
randomValue
|
|
781
785
|
};
|
|
782
786
|
//# sourceMappingURL=tests.mjs.map
|
package/dist/tests.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../tests/crud.ts", "../tests/utils.ts", "../tests/discover.ts", "../tests/media.ts"],
|
|
4
|
-
"sourcesContent": ["import { it, expect, describe, beforeAll } from \"vitest\";\nimport type {\n Graffiti,\n GraffitiSession,\n JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorInvalidSchema,\n GraffitiErrorForbidden,\n} from \"@graffiti-garden/api\";\nimport { randomPostObject, randomString } from \"./utils\";\n\nexport const graffitiCRUDTests = (\n useGraffiti: () => Pick<Graffiti, \"post\" | \"get\" | \"delete\">,\n useSession1: () => GraffitiSession | Promise<GraffitiSession>,\n useSession2: () => GraffitiSession | Promise<GraffitiSession>,\n) => {\n describe.concurrent(\n \"CRUD\",\n {\n timeout: 20000,\n },\n () => {\n let graffiti: ReturnType<typeof useGraffiti>;\n let session: GraffitiSession;\n let session1: GraffitiSession;\n let session2: GraffitiSession;\n beforeAll(async () => {\n graffiti = useGraffiti();\n session1 = await useSession1();\n session = session1;\n session2 = await useSession2();\n });\n\n it(\"get nonexistant object\", async () => {\n await expect(graffiti.get(randomString(), {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"post, get, delete\", async () => {\n const value = {\n something: \"hello, world~ c:\",\n };\n const channels = [randomString(), randomString()];\n\n // Post the object\n const previous = await graffiti.post<{}>({ value, channels }, session);\n expect(previous.value).toEqual(value);\n expect(previous.channels).toEqual(channels);\n expect(previous.allowed).toEqual(undefined);\n expect(previous.actor).toEqual(session.actor);\n\n // Get it back\n const gotten = await graffiti.get(previous, {});\n expect(gotten.value).toEqual(value);\n expect(gotten.channels).toEqual([]);\n expect(gotten.allowed).toBeUndefined();\n expect(gotten.url).toEqual(previous.url);\n expect(gotten.actor).toEqual(previous.actor);\n\n // Delete it\n const deleted = await graffiti.delete(gotten, session);\n expect(deleted.value).toEqual(value);\n expect(deleted.channels).toEqual(channels);\n expect(deleted.allowed).toBeUndefined();\n expect(deleted.actor).toEqual(session.actor);\n expect(deleted.url).toEqual(previous.url);\n\n // Get is not found\n await expect(graffiti.get(gotten, {})).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n\n // Delete it again\n await expect(graffiti.delete(gotten, session)).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"post then delete with wrong actor\", async () => {\n const posted = await graffiti.post<{}>(\n { value: {}, channels: [] },\n session2,\n );\n\n await expect(graffiti.delete(posted, session1)).rejects.toThrow(\n GraffitiErrorForbidden,\n );\n });\n\n it(\"post and get with schema\", async () => {\n const schema = {\n properties: {\n value: {\n properties: {\n something: {\n type: \"string\",\n },\n another: {\n type: \"array\",\n items: {\n type: \"number\",\n },\n },\n deeper: {\n type: \"object\",\n properties: {\n deepProp: {\n type: \"string\",\n },\n },\n required: [\"deepProp\"],\n },\n },\n required: [\"another\", \"deeper\"],\n },\n },\n } as const satisfies JSONSchema;\n\n const goodValue = {\n something: \"hello\",\n another: [1, 2, 3],\n deeper: {\n deepProp: \"hello\",\n },\n };\n\n const posted = await graffiti.post<typeof schema>(\n {\n value: goodValue,\n channels: [],\n },\n session,\n );\n const gotten = await graffiti.get(posted, schema);\n\n expect(gotten.value.something).toEqual(goodValue.something);\n expect(gotten.value.another).toEqual(goodValue.another);\n expect(gotten.value.another[0]).toEqual(1);\n expect(gotten.value.deeper.deepProp).toEqual(goodValue.deeper.deepProp);\n });\n\n it(\"post and get with invalid schema\", async () => {\n const posted = await graffiti.post<{}>(\n { value: {}, channels: [] },\n session,\n );\n await expect(\n graffiti.get(posted, {\n properties: {\n value: {\n //@ts-ignore\n type: \"asdf\",\n },\n },\n }),\n ).rejects.toThrow(GraffitiErrorInvalidSchema);\n });\n\n it(\"post and get with wrong schema\", async () => {\n const posted = await graffiti.post<{}>(\n {\n value: {\n hello: \"world\",\n },\n channels: [],\n },\n session,\n );\n\n await expect(\n graffiti.get(posted, {\n properties: {\n value: {\n properties: {\n hello: {\n type: \"number\",\n },\n },\n },\n },\n }),\n ).rejects.toThrow(GraffitiErrorSchemaMismatch);\n });\n\n it(\"post and get with empty access control\", async () => {\n const value = {\n um: \"hi\",\n };\n const allowed = [randomString()];\n const channels = [randomString()];\n const posted = await graffiti.post<{}>(\n { value, allowed, channels },\n session1,\n );\n\n // Get it with authenticated session\n const gotten = await graffiti.get(posted, {}, session1);\n expect(gotten.url).toEqual(posted.url);\n expect(gotten.actor).toEqual(session1.actor);\n expect(gotten.value).toEqual(value);\n expect(gotten.allowed).toEqual(allowed);\n expect(gotten.channels).toEqual(channels);\n\n // But not without session\n await expect(graffiti.get(posted, {})).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n\n // Or the wrong session\n await expect(graffiti.get(posted, {}, session2)).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"post and get with specific access control\", async () => {\n const value = {\n um: \"hi\",\n };\n const allowed = [randomString(), session2.actor, randomString()];\n const channels = [randomString()];\n const posted = await graffiti.post<{}>(\n {\n value,\n allowed,\n channels,\n },\n session1,\n );\n\n // Get it with authenticated session\n const gotten = await graffiti.get(posted, {}, session1);\n expect(gotten.url).toEqual(posted.url);\n expect(gotten.actor).toEqual(session1.actor);\n expect(gotten.value).toEqual(value);\n expect(gotten.allowed).toEqual(allowed);\n expect(gotten.channels).toEqual(channels);\n\n // But not without session\n await expect(graffiti.get(posted, {})).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n\n const gotten2 = await graffiti.get(posted, {}, session2);\n expect(gotten.url).toEqual(posted.url);\n expect(gotten.actor).toEqual(session1.actor);\n expect(gotten2.value).toEqual(value);\n // They should only see that is is private to them\n expect(gotten2.allowed).toEqual([session2.actor]);\n // And not see any channels\n expect(gotten2.channels).toEqual([]);\n });\n },\n );\n};\n", "import { assert } from \"vitest\";\nimport type {\n GraffitiPostObject,\n GraffitiObjectStream,\n JSONSchema,\n GraffitiObject,\n GraffitiObjectStreamReturn,\n GraffitiObjectStreamContinue,\n Graffiti,\n GraffitiSession,\n} from \"@graffiti-garden/api\";\n\nexport function randomString(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n const str = Array.from(array)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n // check for unicode support\n return str + \"\uD83D\uDC69\uD83C\uDFFD\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D\uD83D\uDC69\uD83C\uDFFB\uD83E\uDEF1\uD83C\uDFFC\u200D\uD83E\uDEF2\uD83C\uDFFF\";\n}\n\nexport function randomValue() {\n return {\n [randomString()]: randomString(),\n };\n}\n\nexport function randomPostObject(): GraffitiPostObject<{}> {\n return {\n value: randomValue(),\n channels: [randomString(), randomString()],\n };\n}\n\nexport async function nextStreamValue<Schema extends JSONSchema>(\n iterator: GraffitiObjectStream<Schema>,\n): Promise<GraffitiObject<Schema>> {\n const result = await iterator.next();\n assert(!result.done && !result.value.error, \"result has no value\");\n assert(!result.value.tombstone, \"result has been deleted!\");\n return result.value.object;\n}\n\nexport function continueStream<Schema extends JSONSchema>(\n graffiti: Pick<Graffiti, \"continueDiscover\">,\n streamReturn: GraffitiObjectStreamReturn<Schema>,\n type: \"cursor\" | \"continue\",\n session?: GraffitiSession | null,\n): GraffitiObjectStreamContinue<Schema> {\n if (type === \"cursor\") {\n return graffiti.continueDiscover(\n streamReturn.cursor,\n session,\n ) as unknown as GraffitiObjectStreamContinue<Schema>;\n } else {\n return streamReturn.continue();\n }\n}\n", "import { it, expect, describe, assert, beforeAll } from \"vitest\";\nimport type {\n Graffiti,\n GraffitiSession,\n JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorForbidden,\n GraffitiErrorInvalidSchema,\n GraffitiErrorNotFound,\n} from \"@graffiti-garden/api\";\nimport {\n randomString,\n nextStreamValue,\n randomPostObject,\n continueStream,\n} from \"./utils\";\n\nexport const graffitiDiscoverTests = (\n useGraffiti: () => Pick<\n Graffiti,\n \"discover\" | \"post\" | \"delete\" | \"continueDiscover\"\n >,\n useSession1: () => GraffitiSession | Promise<GraffitiSession>,\n useSession2: () => GraffitiSession | Promise<GraffitiSession>,\n) => {\n describe.concurrent(\"discover\", { timeout: 20000 }, () => {\n let graffiti: ReturnType<typeof useGraffiti>;\n let session: GraffitiSession;\n let session1: GraffitiSession;\n let session2: GraffitiSession;\n beforeAll(async () => {\n graffiti = useGraffiti();\n session1 = await useSession1();\n session = session1;\n session2 = await useSession2();\n });\n\n it(\"discover nothing\", async () => {\n const iterator = graffiti.discover([], {});\n expect(await iterator.next()).toHaveProperty(\"done\", true);\n });\n\n it(\"discover single\", async () => {\n const object = randomPostObject();\n\n const posted = await graffiti.post<{}>(object, session);\n\n const queryChannels = [randomString(), object.channels[0]];\n const iterator = graffiti.discover<{}>(queryChannels, {});\n const value = await nextStreamValue<{}>(iterator);\n expect(value.url).toEqual(posted.url);\n expect(value.value).toEqual(object.value);\n expect(value.channels).toEqual([object.channels[0]]);\n expect(value.allowed).toBeUndefined();\n expect(value.actor).toEqual(session.actor);\n const result2 = await iterator.next();\n expect(result2.done).toBe(true);\n });\n\n it(\"discover wrong channel\", async () => {\n const object = randomPostObject();\n await graffiti.post<{}>(object, session);\n const iterator = graffiti.discover([randomString()], {});\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover not allowed\", async () => {\n const object = randomPostObject();\n object.allowed = [randomString(), randomString()];\n const posted = await graffiti.post<{}>(object, session1);\n\n const iteratorSession1 = graffiti.discover<{}>(\n object.channels,\n {},\n session1,\n );\n const value = await nextStreamValue<{}>(iteratorSession1);\n expect(value.url).toEqual(posted.url);\n expect(value.value).toEqual(object.value);\n expect(value.channels).toEqual(object.channels);\n expect(value.allowed).toEqual(object.allowed);\n expect(value.actor).toEqual(session1.actor);\n\n const iteratorSession2 = graffiti.discover(object.channels, {}, session2);\n expect(await iteratorSession2.next()).toHaveProperty(\"done\", true);\n\n const iteratorNoSession = graffiti.discover(object.channels, {});\n expect(await iteratorNoSession.next()).toHaveProperty(\"done\", true);\n });\n\n it(\"discover allowed\", async () => {\n const object = randomPostObject();\n object.allowed = [randomString(), session2.actor, randomString()];\n const posted = await graffiti.post<{}>(object, session1);\n\n const iteratorSession2 = graffiti.discover<{}>(\n object.channels,\n {},\n session2,\n );\n const value = await nextStreamValue<{}>(iteratorSession2);\n expect(value.url).toEqual(posted.url);\n expect(value.value).toEqual(object.value);\n expect(value.allowed).toEqual([session2.actor]);\n expect(value.channels).toEqual(object.channels);\n expect(value.actor).toEqual(session1.actor);\n });\n\n it(\"discover bad schema\", async () => {\n const iterator = graffiti.discover([], {\n properties: {\n value: {\n //@ts-ignore\n type: \"asdf\",\n },\n },\n });\n\n await expect(iterator.next()).rejects.toThrow(GraffitiErrorInvalidSchema);\n });\n\n it(\"discover for actor\", async () => {\n const object1 = randomPostObject();\n const posted1 = await graffiti.post<{}>(object1, session1);\n\n const object2 = randomPostObject();\n object2.channels = object1.channels;\n const posted2 = await graffiti.post<{}>(object2, session2);\n\n const iterator = graffiti.discover<{}>(object1.channels, {\n properties: {\n actor: { const: posted1.actor },\n },\n });\n\n const value = await nextStreamValue<{}>(iterator);\n expect(value.url).toEqual(posted1.url);\n expect(value.url).not.toEqual(posted2.url);\n expect(value.value).toEqual(object1.value);\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover schema allowed, as and not as owner\", async () => {\n const object = randomPostObject();\n object.allowed = [randomString(), session2.actor, randomString()];\n await graffiti.post<{}>(object, session1);\n\n const iteratorSession1 = graffiti.discover<{}>(\n object.channels,\n {\n properties: {\n allowed: {\n minItems: 3,\n // Make sure session2.actor is in the allow list\n not: {\n items: {\n not: { const: session2.actor },\n },\n },\n },\n },\n },\n session1,\n );\n const value = await nextStreamValue<{}>(iteratorSession1);\n expect(value.value).toEqual(object.value);\n await expect(iteratorSession1.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n\n const iteratorSession2BigAllow = graffiti.discover(\n object.channels,\n {\n properties: {\n allowed: {\n minItems: 3,\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2PeekOther = graffiti.discover(\n object.channels,\n {\n properties: {\n allowed: {\n not: {\n items: {\n not: { const: object.allowed[0] },\n },\n },\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2SmallAllowPeekSelf = graffiti.discover<{}>(\n object.channels,\n {\n properties: {\n allowed: {\n maxItems: 1,\n not: {\n items: {\n not: { const: session2.actor },\n },\n },\n },\n },\n },\n session2,\n );\n const value2 = await nextStreamValue<{}>(\n iteratorSession2SmallAllowPeekSelf,\n );\n expect(value2.value).toEqual(object.value);\n await expect(\n iteratorSession2SmallAllowPeekSelf.next(),\n ).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover schema channels, as and not as owner\", async () => {\n const object = randomPostObject();\n object.channels = [randomString(), randomString(), randomString()];\n await graffiti.post<{}>(object, session1);\n\n const iteratorSession1 = graffiti.discover<{}>(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n channels: {\n minItems: 3,\n // Make sure channel 1 is in the allow list\n not: {\n items: {\n not: { const: object.channels[1] },\n },\n },\n },\n },\n },\n session1,\n );\n const value = await nextStreamValue<{}>(iteratorSession1);\n expect(value.value).toEqual(object.value);\n await expect(iteratorSession1.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n\n const iteratorSession2BigAllow = graffiti.discover(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n channels: {\n minItems: 3,\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2PeekOther = graffiti.discover(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n channels: {\n not: {\n items: {\n not: { const: object.channels[1] },\n },\n },\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2SmallAllowPeekSelf = graffiti.discover<{}>(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n allowed: {\n maxItems: 2,\n not: {\n items: {\n not: { const: object.channels[2] },\n },\n },\n },\n },\n },\n session2,\n );\n const value2 = await nextStreamValue<{}>(\n iteratorSession2SmallAllowPeekSelf,\n );\n expect(value2.value).toEqual(object.value);\n await expect(\n iteratorSession2SmallAllowPeekSelf.next(),\n ).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover query for empty allowed\", async () => {\n const publicO = randomPostObject();\n\n const publicSchema = {\n not: {\n required: [\"allowed\"],\n },\n } satisfies JSONSchema;\n\n await graffiti.post<{}>(publicO, session1);\n const iterator = graffiti.discover<{}>(\n publicO.channels,\n publicSchema,\n session1,\n );\n const value = await nextStreamValue<{}>(iterator);\n expect(value.value).toEqual(publicO.value);\n expect(value.allowed).toBeUndefined();\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n\n const restricted = randomPostObject();\n restricted.allowed = [];\n await graffiti.post<{}>(restricted, session1);\n const iterator2 = graffiti.discover(\n restricted.channels,\n publicSchema,\n session1,\n );\n await expect(iterator2.next()).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover query for values\", async () => {\n const object1 = randomPostObject();\n object1.value = { test: randomString() };\n await graffiti.post<{}>(object1, session);\n\n const object2 = randomPostObject();\n object2.channels = object1.channels;\n object2.value = { test: randomString(), something: randomString() };\n await graffiti.post<{}>(object2, session);\n\n const object3 = randomPostObject();\n object3.channels = object1.channels;\n object3.value = { other: randomString(), something: randomString() };\n await graffiti.post<{}>(object3, session);\n\n const counts = new Map<string, number>();\n for (const property of [\"test\", \"something\", \"other\"] as const) {\n let count = 0;\n for await (const result of graffiti.discover(object1.channels, {\n properties: {\n value: {\n required: [property],\n },\n },\n })) {\n assert(!result.error, \"result has error\");\n if (property in result.object.value) {\n count++;\n }\n }\n counts.set(property, count);\n }\n\n expect(counts.get(\"test\")).toBe(2);\n expect(counts.get(\"something\")).toBe(2);\n expect(counts.get(\"other\")).toBe(1);\n });\n\n for (const continueType of [\"cursor\", \"continue\"] as const) {\n describe(`continue discover with ${continueType}`, () => {\n it(\"discover for deleted content\", async () => {\n const object = randomPostObject();\n\n const posted = await graffiti.post<{}>(object, session);\n\n const iterator1 = graffiti.discover<{}>(object.channels, {});\n const value1 = await nextStreamValue<{}>(iterator1);\n expect(value1.value).toEqual(object.value);\n const returnValue = await iterator1.next();\n assert(returnValue.done, \"value2 is not done\");\n\n await graffiti.delete(posted, session);\n\n const iterator = graffiti.discover(object.channels, {});\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n\n const tombIterator = continueStream<{}>(\n graffiti,\n returnValue.value,\n continueType,\n );\n const value = await tombIterator.next();\n assert(!value.done && !value.value.error, \"value is done\");\n assert(value.value.tombstone, \"value is not tombstone\");\n expect(value.value.object.url).toEqual(posted.url);\n const returnValue2 = await tombIterator.next();\n assert(returnValue2.done, \"value2 is not done\");\n\n // Post another object\n const posted2 = await graffiti.post<{}>(object, session);\n const doubleContinueIterator = continueStream<{}>(\n graffiti,\n returnValue2.value,\n continueType,\n );\n const value2 = await doubleContinueIterator.next();\n assert(!value2.done && !value2.value.error, \"value2 is done\");\n assert(!value2.value.tombstone, \"value2 is tombstone\");\n expect(value2.value.object.url).toEqual(posted2.url);\n await expect(doubleContinueIterator.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n });\n\n it(\"continue with wrong actor\", async () => {\n const iterator = graffiti.discover<{}>([], {}, session1);\n const result = await iterator.next();\n assert(result.done, \"iterator is not done\");\n\n const continuation = continueStream<{}>(\n graffiti,\n result.value,\n continueType,\n session2,\n );\n await expect(continuation.next()).rejects.toThrow(\n GraffitiErrorForbidden,\n );\n });\n });\n }\n\n it(\"lookup non-existant cursor\", async () => {\n const iterator = graffiti.continueDiscover(randomString());\n await expect(iterator.next()).rejects.toThrow(GraffitiErrorNotFound);\n });\n });\n};\n", "import {\n GraffitiErrorNotAcceptable,\n GraffitiErrorNotFound,\n GraffitiErrorTooLarge,\n type Graffiti,\n type GraffitiSession,\n} from \"@graffiti-garden/api\";\nimport { it, expect, describe, beforeAll } from \"vitest\";\nimport { randomString } from \"./utils\";\n\nexport const graffitiMediaTests = (\n useGraffiti: () => Pick<Graffiti, \"postMedia\" | \"getMedia\" | \"deleteMedia\">,\n useSession1: () => GraffitiSession | Promise<GraffitiSession>,\n useSession2: () => GraffitiSession | Promise<GraffitiSession>,\n) => {\n describe.concurrent(\n \"media\",\n {\n timeout: 20000,\n },\n () => {\n let graffiti: ReturnType<typeof useGraffiti>;\n let session: GraffitiSession;\n let session1: GraffitiSession;\n let session2: GraffitiSession;\n beforeAll(async () => {\n graffiti = useGraffiti();\n session1 = await useSession1();\n session = session1;\n session2 = await useSession2();\n });\n\n it(\"post, get, delete media\", async () => {\n // Post media\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n // Get the media back\n const media = await graffiti.getMedia(mediaUrl, {});\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toBeUndefined();\n expect(media.actor).toBe(session.actor);\n\n // Delete the media\n await graffiti.deleteMedia(mediaUrl, session);\n\n // Try to get the media again\n await expect(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"acceptable type\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n const media = await graffiti.getMedia(mediaUrl, {\n types: [\"application/json\", \"text/*\"],\n });\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toBeUndefined();\n expect(media.actor).toBe(session.actor);\n });\n\n it(\"unacceptable type\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n await expect(\n graffiti.getMedia(mediaUrl, {\n types: [\"image/*\"],\n }),\n ).rejects.toThrow(GraffitiErrorNotAcceptable);\n });\n\n it(\"acceptable size\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n const media = await graffiti.getMedia(mediaUrl, {\n maxBytes: data.size,\n });\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toBeUndefined();\n expect(media.actor).toBe(session.actor);\n });\n\n it(\"unacceptable size\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n await expect(\n graffiti.getMedia(mediaUrl, {\n maxBytes: data.size - 1,\n }),\n ).rejects.toThrow(GraffitiErrorTooLarge);\n });\n\n it(\"empty allowed\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const allowed: string[] = [];\n const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);\n\n // Get it with the authorized user\n const media = await graffiti.getMedia(mediaUrl, {}, session1);\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toEqual([]);\n expect(media.actor).toBe(session1.actor);\n\n // Get it with the unauthorized user\n await expect(graffiti.getMedia(mediaUrl, {}, session2)).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n\n // Get it without authorization\n await expect(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"allowed\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const allowed = [randomString(), session2.actor, randomString()];\n const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);\n\n // Get it with the authorized user\n const media = await graffiti.getMedia(mediaUrl, {}, session1);\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toEqual(allowed);\n expect(media.actor).toBe(session1.actor);\n\n // Get it with the allowed user\n const media2 = await graffiti.getMedia(mediaUrl, {}, session2);\n expect(await media2.data.text()).toEqual(text);\n expect(media2.data.type).toEqual(\"text/plain\");\n expect(media2.allowed).toEqual([session2.actor]);\n expect(media2.actor).toBe(session1.actor);\n\n // Get it without authorization\n await expect(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n },\n );\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { it, expect, describe, beforeAll } from \"vitest\";\nimport type {\n Graffiti,\n GraffitiSession,\n JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorInvalidSchema,\n GraffitiErrorForbidden,\n} from \"@graffiti-garden/api\";\nimport { randomPostObject, randomString, randomUrl } from \"./utils\";\n\nexport const graffitiCRUDTests = (\n useGraffiti: () => Pick<Graffiti, \"post\" | \"get\" | \"delete\">,\n useSession1: () => GraffitiSession | Promise<GraffitiSession>,\n useSession2: () => GraffitiSession | Promise<GraffitiSession>,\n) => {\n describe.concurrent(\n \"CRUD\",\n {\n timeout: 20000,\n },\n () => {\n let graffiti: ReturnType<typeof useGraffiti>;\n let session: GraffitiSession;\n let session1: GraffitiSession;\n let session2: GraffitiSession;\n beforeAll(async () => {\n graffiti = useGraffiti();\n session1 = await useSession1();\n session = session1;\n session2 = await useSession2();\n });\n\n it(\"get nonexistant object\", async () => {\n await expect(graffiti.get(randomUrl(), {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"post, get, delete\", async () => {\n const value = {\n something: \"hello, world~ c:\",\n };\n const channels = [randomString(), randomString()];\n\n // Post the object\n const previous = await graffiti.post<{}>({ value, channels }, session);\n expect(previous.value).toEqual(value);\n expect(previous.channels).toEqual(channels);\n expect(previous.allowed).toEqual(undefined);\n expect(previous.actor).toEqual(session.actor);\n\n // Get it back\n const gotten = await graffiti.get(previous, {});\n expect(gotten.value).toEqual(value);\n expect(gotten.channels).toEqual([]);\n expect(gotten.allowed).toBeUndefined();\n expect(gotten.url).toEqual(previous.url);\n expect(gotten.actor).toEqual(previous.actor);\n\n // Delete it\n const deleted = await graffiti.delete(gotten, session);\n expect(deleted.value).toEqual(value);\n expect(deleted.channels).toEqual(channels);\n expect(deleted.allowed).toBeUndefined();\n expect(deleted.actor).toEqual(session.actor);\n expect(deleted.url).toEqual(previous.url);\n\n // Get is not found\n await expect(graffiti.get(gotten, {})).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n\n // Delete it again\n await expect(graffiti.delete(gotten, session)).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"post then delete with wrong actor\", async () => {\n const posted = await graffiti.post<{}>(\n { value: {}, channels: [] },\n session2,\n );\n\n await expect(graffiti.delete(posted, session1)).rejects.toThrow(\n GraffitiErrorForbidden,\n );\n });\n\n it(\"post and get with schema\", async () => {\n const schema = {\n properties: {\n value: {\n properties: {\n something: {\n type: \"string\",\n },\n another: {\n type: \"array\",\n items: {\n type: \"number\",\n },\n },\n deeper: {\n type: \"object\",\n properties: {\n deepProp: {\n type: \"string\",\n },\n },\n required: [\"deepProp\"],\n },\n },\n required: [\"another\", \"deeper\"],\n },\n },\n } as const satisfies JSONSchema;\n\n const goodValue = {\n something: \"hello\",\n another: [1, 2, 3],\n deeper: {\n deepProp: \"hello\",\n },\n };\n\n const posted = await graffiti.post<typeof schema>(\n {\n value: goodValue,\n channels: [],\n },\n session,\n );\n const gotten = await graffiti.get(posted, schema);\n\n expect(gotten.value.something).toEqual(goodValue.something);\n expect(gotten.value.another).toEqual(goodValue.another);\n expect(gotten.value.another[0]).toEqual(1);\n expect(gotten.value.deeper.deepProp).toEqual(goodValue.deeper.deepProp);\n });\n\n it(\"post and get with invalid schema\", async () => {\n const posted = await graffiti.post<{}>(\n { value: {}, channels: [] },\n session,\n );\n await expect(\n graffiti.get(posted, {\n properties: {\n value: {\n //@ts-ignore\n type: \"asdf\",\n },\n },\n }),\n ).rejects.toThrow(GraffitiErrorInvalidSchema);\n });\n\n it(\"post and get with wrong schema\", async () => {\n const posted = await graffiti.post<{}>(\n {\n value: {\n hello: \"world\",\n },\n channels: [],\n },\n session,\n );\n\n await expect(\n graffiti.get(posted, {\n properties: {\n value: {\n properties: {\n hello: {\n type: \"number\",\n },\n },\n },\n },\n }),\n ).rejects.toThrow(GraffitiErrorSchemaMismatch);\n });\n\n it(\"post and get with empty access control\", async () => {\n const value = {\n um: \"hi\",\n };\n const allowed = [randomUrl()];\n const channels = [randomString()];\n const posted = await graffiti.post<{}>(\n { value, allowed, channels },\n session1,\n );\n\n // Get it with authenticated session\n const gotten = await graffiti.get(posted, {}, session1);\n expect(gotten.url).toEqual(posted.url);\n expect(gotten.actor).toEqual(session1.actor);\n expect(gotten.value).toEqual(value);\n expect(gotten.allowed).toEqual(allowed);\n expect(gotten.channels).toEqual(channels);\n\n // But not without session\n await expect(graffiti.get(posted, {})).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n\n // Or the wrong session\n await expect(graffiti.get(posted, {}, session2)).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"post and get with specific access control\", async () => {\n const value = {\n um: \"hi\",\n };\n const allowed = [randomUrl(), session2.actor, randomUrl()];\n const channels = [randomString()];\n const posted = await graffiti.post<{}>(\n {\n value,\n allowed,\n channels,\n },\n session1,\n );\n\n // Get it with authenticated session\n const gotten = await graffiti.get(posted, {}, session1);\n expect(gotten.url).toEqual(posted.url);\n expect(gotten.actor).toEqual(session1.actor);\n expect(gotten.value).toEqual(value);\n expect(gotten.allowed).toEqual(allowed);\n expect(gotten.channels).toEqual(channels);\n\n // But not without session\n await expect(graffiti.get(posted, {})).rejects.toBeInstanceOf(\n GraffitiErrorNotFound,\n );\n\n const gotten2 = await graffiti.get(posted, {}, session2);\n expect(gotten.url).toEqual(posted.url);\n expect(gotten.actor).toEqual(session1.actor);\n expect(gotten2.value).toEqual(value);\n // They should only see that is is private to them\n expect(gotten2.allowed).toEqual([session2.actor]);\n // And not see any channels\n expect(gotten2.channels).toEqual([]);\n });\n },\n );\n};\n", "import { assert } from \"vitest\";\nimport type {\n GraffitiPostObject,\n GraffitiObjectStream,\n JSONSchema,\n GraffitiObject,\n GraffitiObjectStreamReturn,\n GraffitiObjectStreamContinue,\n Graffiti,\n GraffitiSession,\n} from \"@graffiti-garden/api\";\n\nexport function randomString(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n const str = Array.from(array)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n // check for unicode support\n return str + \"\uD83D\uDC69\uD83C\uDFFD\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D\uD83D\uDC69\uD83C\uDFFB\uD83E\uDEF1\uD83C\uDFFC\u200D\uD83E\uDEF2\uD83C\uDFFF\";\n}\nexport function randomUrl(): string {\n return \"test:\" + randomString();\n}\n\nexport function randomValue() {\n return {\n [randomString()]: randomString(),\n };\n}\n\nexport function randomPostObject(): GraffitiPostObject<{}> {\n return {\n value: randomValue(),\n channels: [randomString(), randomString()],\n };\n}\n\nexport async function nextStreamValue<Schema extends JSONSchema>(\n iterator: GraffitiObjectStream<Schema>,\n): Promise<GraffitiObject<Schema>> {\n const result = await iterator.next();\n assert(!result.done && !result.value.error, \"result has no value\");\n assert(!result.value.tombstone, \"result has been deleted!\");\n return result.value.object;\n}\n\nexport function continueStream<Schema extends JSONSchema>(\n graffiti: Pick<Graffiti, \"continueDiscover\">,\n streamReturn: GraffitiObjectStreamReturn<Schema>,\n type: \"cursor\" | \"continue\",\n session?: GraffitiSession | null,\n): GraffitiObjectStreamContinue<Schema> {\n if (type === \"cursor\") {\n return graffiti.continueDiscover(\n streamReturn.cursor,\n session,\n ) as unknown as GraffitiObjectStreamContinue<Schema>;\n } else {\n return streamReturn.continue();\n }\n}\n", "import { it, expect, describe, assert, beforeAll } from \"vitest\";\nimport type {\n Graffiti,\n GraffitiSession,\n JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorForbidden,\n GraffitiErrorInvalidSchema,\n GraffitiErrorNotFound,\n} from \"@graffiti-garden/api\";\nimport {\n randomString,\n nextStreamValue,\n randomPostObject,\n continueStream,\n randomUrl,\n} from \"./utils\";\n\nexport const graffitiDiscoverTests = (\n useGraffiti: () => Pick<\n Graffiti,\n \"discover\" | \"post\" | \"delete\" | \"continueDiscover\"\n >,\n useSession1: () => GraffitiSession | Promise<GraffitiSession>,\n useSession2: () => GraffitiSession | Promise<GraffitiSession>,\n) => {\n describe.concurrent(\"discover\", { timeout: 20000 }, () => {\n let graffiti: ReturnType<typeof useGraffiti>;\n let session: GraffitiSession;\n let session1: GraffitiSession;\n let session2: GraffitiSession;\n beforeAll(async () => {\n graffiti = useGraffiti();\n session1 = await useSession1();\n session = session1;\n session2 = await useSession2();\n });\n\n it(\"discover nothing\", async () => {\n const iterator = graffiti.discover([], {});\n expect(await iterator.next()).toHaveProperty(\"done\", true);\n });\n\n it(\"discover single\", async () => {\n const object = randomPostObject();\n\n const posted = await graffiti.post<{}>(object, session);\n\n const queryChannels = [randomString(), object.channels[0]];\n const iterator = graffiti.discover<{}>(queryChannels, {});\n const value = await nextStreamValue<{}>(iterator);\n expect(value.url).toEqual(posted.url);\n expect(value.value).toEqual(object.value);\n expect(value.channels).toEqual([object.channels[0]]);\n expect(value.allowed).toBeUndefined();\n expect(value.actor).toEqual(session.actor);\n const result2 = await iterator.next();\n expect(result2.done).toBe(true);\n });\n\n it(\"discover wrong channel\", async () => {\n const object = randomPostObject();\n await graffiti.post<{}>(object, session);\n const iterator = graffiti.discover([randomString()], {});\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover not allowed\", async () => {\n const object = randomPostObject();\n object.allowed = [randomUrl(), randomUrl()];\n const posted = await graffiti.post<{}>(object, session1);\n\n const iteratorSession1 = graffiti.discover<{}>(\n object.channels,\n {},\n session1,\n );\n const value = await nextStreamValue<{}>(iteratorSession1);\n expect(value.url).toEqual(posted.url);\n expect(value.value).toEqual(object.value);\n expect(value.channels).toEqual(object.channels);\n expect(value.allowed).toEqual(object.allowed);\n expect(value.actor).toEqual(session1.actor);\n\n const iteratorSession2 = graffiti.discover(object.channels, {}, session2);\n expect(await iteratorSession2.next()).toHaveProperty(\"done\", true);\n\n const iteratorNoSession = graffiti.discover(object.channels, {});\n expect(await iteratorNoSession.next()).toHaveProperty(\"done\", true);\n });\n\n it(\"discover allowed\", async () => {\n const object = randomPostObject();\n object.allowed = [randomUrl(), session2.actor, randomUrl()];\n const posted = await graffiti.post<{}>(object, session1);\n\n const iteratorSession2 = graffiti.discover<{}>(\n object.channels,\n {},\n session2,\n );\n const value = await nextStreamValue<{}>(iteratorSession2);\n expect(value.url).toEqual(posted.url);\n expect(value.value).toEqual(object.value);\n expect(value.allowed).toEqual([session2.actor]);\n expect(value.channels).toEqual(object.channels);\n expect(value.actor).toEqual(session1.actor);\n });\n\n it(\"discover bad schema\", async () => {\n const iterator = graffiti.discover([], {\n properties: {\n value: {\n //@ts-ignore\n type: \"asdf\",\n },\n },\n });\n\n await expect(iterator.next()).rejects.toThrow(GraffitiErrorInvalidSchema);\n });\n\n it(\"discover for actor\", async () => {\n const object1 = randomPostObject();\n const posted1 = await graffiti.post<{}>(object1, session1);\n\n const object2 = randomPostObject();\n object2.channels = object1.channels;\n const posted2 = await graffiti.post<{}>(object2, session2);\n\n const iterator = graffiti.discover<{}>(object1.channels, {\n properties: {\n actor: { const: posted1.actor },\n },\n });\n\n const value = await nextStreamValue<{}>(iterator);\n expect(value.url).toEqual(posted1.url);\n expect(value.url).not.toEqual(posted2.url);\n expect(value.value).toEqual(object1.value);\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover schema allowed, as and not as owner\", async () => {\n const object = randomPostObject();\n object.allowed = [randomUrl(), session2.actor, randomUrl()];\n await graffiti.post<{}>(object, session1);\n\n const iteratorSession1 = graffiti.discover<{}>(\n object.channels,\n {\n properties: {\n allowed: {\n minItems: 3,\n // Make sure session2.actor is in the allow list\n not: {\n items: {\n not: { const: session2.actor },\n },\n },\n },\n },\n },\n session1,\n );\n const value = await nextStreamValue<{}>(iteratorSession1);\n expect(value.value).toEqual(object.value);\n await expect(iteratorSession1.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n\n const iteratorSession2BigAllow = graffiti.discover(\n object.channels,\n {\n properties: {\n allowed: {\n minItems: 3,\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2PeekOther = graffiti.discover(\n object.channels,\n {\n properties: {\n allowed: {\n not: {\n items: {\n not: { const: object.allowed[0] },\n },\n },\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2SmallAllowPeekSelf = graffiti.discover<{}>(\n object.channels,\n {\n properties: {\n allowed: {\n maxItems: 1,\n not: {\n items: {\n not: { const: session2.actor },\n },\n },\n },\n },\n },\n session2,\n );\n const value2 = await nextStreamValue<{}>(\n iteratorSession2SmallAllowPeekSelf,\n );\n expect(value2.value).toEqual(object.value);\n await expect(\n iteratorSession2SmallAllowPeekSelf.next(),\n ).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover schema channels, as and not as owner\", async () => {\n const object = randomPostObject();\n object.channels = [randomString(), randomString(), randomString()];\n await graffiti.post<{}>(object, session1);\n\n const iteratorSession1 = graffiti.discover<{}>(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n channels: {\n minItems: 3,\n // Make sure channel 1 is in the allow list\n not: {\n items: {\n not: { const: object.channels[1] },\n },\n },\n },\n },\n },\n session1,\n );\n const value = await nextStreamValue<{}>(iteratorSession1);\n expect(value.value).toEqual(object.value);\n await expect(iteratorSession1.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n\n const iteratorSession2BigAllow = graffiti.discover(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n channels: {\n minItems: 3,\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2BigAllow.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2PeekOther = graffiti.discover(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n channels: {\n not: {\n items: {\n not: { const: object.channels[1] },\n },\n },\n },\n },\n },\n session2,\n );\n await expect(iteratorSession2PeekOther.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n const iteratorSession2SmallAllowPeekSelf = graffiti.discover<{}>(\n [object.channels[0], object.channels[2]],\n {\n properties: {\n allowed: {\n maxItems: 2,\n not: {\n items: {\n not: { const: object.channels[2] },\n },\n },\n },\n },\n },\n session2,\n );\n const value2 = await nextStreamValue<{}>(\n iteratorSession2SmallAllowPeekSelf,\n );\n expect(value2.value).toEqual(object.value);\n await expect(\n iteratorSession2SmallAllowPeekSelf.next(),\n ).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover query for empty allowed\", async () => {\n const publicO = randomPostObject();\n\n const publicSchema = {\n not: {\n required: [\"allowed\"],\n },\n } satisfies JSONSchema;\n\n await graffiti.post<{}>(publicO, session1);\n const iterator = graffiti.discover<{}>(\n publicO.channels,\n publicSchema,\n session1,\n );\n const value = await nextStreamValue<{}>(iterator);\n expect(value.value).toEqual(publicO.value);\n expect(value.allowed).toBeUndefined();\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n\n const restricted = randomPostObject();\n restricted.allowed = [];\n await graffiti.post<{}>(restricted, session1);\n const iterator2 = graffiti.discover(\n restricted.channels,\n publicSchema,\n session1,\n );\n await expect(iterator2.next()).resolves.toHaveProperty(\"done\", true);\n });\n\n it(\"discover query for values\", async () => {\n const object1 = randomPostObject();\n object1.value = { test: randomString() };\n await graffiti.post<{}>(object1, session);\n\n const object2 = randomPostObject();\n object2.channels = object1.channels;\n object2.value = { test: randomString(), something: randomString() };\n await graffiti.post<{}>(object2, session);\n\n const object3 = randomPostObject();\n object3.channels = object1.channels;\n object3.value = { other: randomString(), something: randomString() };\n await graffiti.post<{}>(object3, session);\n\n const counts = new Map<string, number>();\n for (const property of [\"test\", \"something\", \"other\"] as const) {\n let count = 0;\n for await (const result of graffiti.discover(object1.channels, {\n properties: {\n value: {\n required: [property],\n },\n },\n })) {\n assert(!result.error, \"result has error\");\n if (property in result.object.value) {\n count++;\n }\n }\n counts.set(property, count);\n }\n\n expect(counts.get(\"test\")).toBe(2);\n expect(counts.get(\"something\")).toBe(2);\n expect(counts.get(\"other\")).toBe(1);\n });\n\n for (const continueType of [\"cursor\", \"continue\"] as const) {\n describe(`continue discover with ${continueType}`, () => {\n it(\"discover for deleted content\", async () => {\n const object = randomPostObject();\n\n const posted = await graffiti.post<{}>(object, session);\n\n const iterator1 = graffiti.discover<{}>(object.channels, {});\n const value1 = await nextStreamValue<{}>(iterator1);\n expect(value1.value).toEqual(object.value);\n const returnValue = await iterator1.next();\n assert(returnValue.done, \"value2 is not done\");\n\n await graffiti.delete(posted, session);\n\n const iterator = graffiti.discover(object.channels, {});\n await expect(iterator.next()).resolves.toHaveProperty(\"done\", true);\n\n const tombIterator = continueStream<{}>(\n graffiti,\n returnValue.value,\n continueType,\n );\n const value = await tombIterator.next();\n assert(!value.done && !value.value.error, \"value is done\");\n assert(value.value.tombstone, \"value is not tombstone\");\n expect(value.value.object.url).toEqual(posted.url);\n const returnValue2 = await tombIterator.next();\n assert(returnValue2.done, \"value2 is not done\");\n\n // Post another object\n const posted2 = await graffiti.post<{}>(object, session);\n const doubleContinueIterator = continueStream<{}>(\n graffiti,\n returnValue2.value,\n continueType,\n );\n const value2 = await doubleContinueIterator.next();\n assert(!value2.done && !value2.value.error, \"value2 is done\");\n assert(!value2.value.tombstone, \"value2 is tombstone\");\n expect(value2.value.object.url).toEqual(posted2.url);\n await expect(doubleContinueIterator.next()).resolves.toHaveProperty(\n \"done\",\n true,\n );\n });\n\n it(\"continue with wrong actor\", async () => {\n const iterator = graffiti.discover<{}>([], {}, session1);\n const result = await iterator.next();\n assert(result.done, \"iterator is not done\");\n\n const continuation = continueStream<{}>(\n graffiti,\n result.value,\n continueType,\n session2,\n );\n await expect(continuation.next()).rejects.toThrow(\n GraffitiErrorForbidden,\n );\n });\n });\n }\n\n it(\"lookup non-existant cursor\", async () => {\n const iterator = graffiti.continueDiscover(randomString());\n await expect(iterator.next()).rejects.toThrow(GraffitiErrorNotFound);\n });\n });\n};\n", "import {\n GraffitiErrorNotAcceptable,\n GraffitiErrorNotFound,\n GraffitiErrorTooLarge,\n type Graffiti,\n type GraffitiSession,\n} from \"@graffiti-garden/api\";\nimport { it, expect, describe, beforeAll } from \"vitest\";\nimport { randomString, randomUrl } from \"./utils\";\n\nexport const graffitiMediaTests = (\n useGraffiti: () => Pick<Graffiti, \"postMedia\" | \"getMedia\" | \"deleteMedia\">,\n useSession1: () => GraffitiSession | Promise<GraffitiSession>,\n useSession2: () => GraffitiSession | Promise<GraffitiSession>,\n) => {\n describe.concurrent(\n \"media\",\n {\n timeout: 20000,\n },\n () => {\n let graffiti: ReturnType<typeof useGraffiti>;\n let session: GraffitiSession;\n let session1: GraffitiSession;\n let session2: GraffitiSession;\n beforeAll(async () => {\n graffiti = useGraffiti();\n session1 = await useSession1();\n session = session1;\n session2 = await useSession2();\n });\n\n it(\"post, get, delete media\", async () => {\n // Post media\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n // Get the media back\n const media = await graffiti.getMedia(mediaUrl, {});\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toBeUndefined();\n expect(media.actor).toBe(session.actor);\n\n // Delete the media\n await graffiti.deleteMedia(mediaUrl, session);\n\n // Try to get the media again\n await expect(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"acceptable type\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n const media = await graffiti.getMedia(mediaUrl, {\n types: [\"application/json\", \"text/*\"],\n });\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toBeUndefined();\n expect(media.actor).toBe(session.actor);\n });\n\n it(\"unacceptable type\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n await expect(\n graffiti.getMedia(mediaUrl, {\n types: [\"image/*\"],\n }),\n ).rejects.toThrow(GraffitiErrorNotAcceptable);\n });\n\n it(\"acceptable size\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n const media = await graffiti.getMedia(mediaUrl, {\n maxBytes: data.size,\n });\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toBeUndefined();\n expect(media.actor).toBe(session.actor);\n });\n\n it(\"unacceptable size\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const mediaUrl = await graffiti.postMedia({ data }, session);\n\n await expect(\n graffiti.getMedia(mediaUrl, {\n maxBytes: data.size - 1,\n }),\n ).rejects.toThrow(GraffitiErrorTooLarge);\n });\n\n it(\"empty allowed\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const allowed: string[] = [];\n const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);\n\n // Get it with the authorized user\n const media = await graffiti.getMedia(mediaUrl, {}, session1);\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toEqual([]);\n expect(media.actor).toBe(session1.actor);\n\n // Get it with the unauthorized user\n await expect(graffiti.getMedia(mediaUrl, {}, session2)).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n\n // Get it without authorization\n await expect(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n\n it(\"allowed\", async () => {\n const text = randomString();\n const data = new Blob([text], { type: \"text/plain\" });\n const allowed = [randomUrl(), session2.actor, randomUrl()];\n const mediaUrl = await graffiti.postMedia({ data, allowed }, session1);\n\n // Get it with the authorized user\n const media = await graffiti.getMedia(mediaUrl, {}, session1);\n expect(await media.data.text()).toEqual(text);\n expect(media.data.type).toEqual(\"text/plain\");\n expect(media.allowed).toEqual(allowed);\n expect(media.actor).toBe(session1.actor);\n\n // Get it with the allowed user\n const media2 = await graffiti.getMedia(mediaUrl, {}, session2);\n expect(await media2.data.text()).toEqual(text);\n expect(media2.data.type).toEqual(\"text/plain\");\n expect(media2.allowed).toEqual([session2.actor]);\n expect(media2.actor).toBe(session1.actor);\n\n // Get it without authorization\n await expect(graffiti.getMedia(mediaUrl, {})).rejects.toThrow(\n GraffitiErrorNotFound,\n );\n });\n },\n );\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["it", "expect", "describe", "assert", "beforeAll", "GraffitiErrorForbidden", "GraffitiErrorInvalidSchema", "GraffitiErrorNotFound", "describe", "beforeAll", "it", "expect", "GraffitiErrorInvalidSchema", "assert", "GraffitiErrorForbidden", "GraffitiErrorNotFound", "GraffitiErrorNotFound", "it", "expect", "describe", "beforeAll", "describe", "beforeAll", "it", "expect", "GraffitiErrorNotFound"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graffiti-garden/api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "The heart of Graffiti",
|
|
5
5
|
"types": "./dist/src/index.d.ts",
|
|
6
|
-
"module": "./dist/index.
|
|
7
|
-
"main": "./dist/index.
|
|
6
|
+
"module": "./dist/esm/index.js",
|
|
7
|
+
"main": "./dist/cjs/index.js",
|
|
8
|
+
"browser": "./dist/browser/index.js",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
10
11
|
"import": {
|
|
11
12
|
"types": "./dist/src/index.d.ts",
|
|
12
|
-
"default": "./dist/index.
|
|
13
|
+
"default": "./dist/esm/index.js"
|
|
13
14
|
},
|
|
14
15
|
"require": {
|
|
15
16
|
"types": "./dist/src/index.d.ts",
|
|
16
|
-
"default": "./dist/index.
|
|
17
|
+
"default": "./dist/cjs/index.js"
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
20
|
"./tests": {
|
|
@@ -48,13 +49,15 @@
|
|
|
48
49
|
},
|
|
49
50
|
"homepage": "https://api.graffiti.garden/classes/Graffiti.html",
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@types/node": "^25.0.
|
|
52
|
+
"@types/node": "^25.0.9",
|
|
52
53
|
"tsx": "^4.21.0",
|
|
53
54
|
"typedoc": "^0.28.16",
|
|
54
55
|
"typescript": "^5.9.3",
|
|
55
56
|
"vitest": "^4.0.17"
|
|
56
57
|
},
|
|
57
58
|
"dependencies": {
|
|
58
|
-
"
|
|
59
|
+
"ajv": "^8.17.1",
|
|
60
|
+
"json-schema-to-ts": "^3.1.1",
|
|
61
|
+
"zod": "^4.3.5"
|
|
59
62
|
}
|
|
60
63
|
}
|
package/src/1-api.ts
CHANGED
package/src/4-utilities.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
GraffitiObject,
|
|
2
3
|
GraffitiObjectBase,
|
|
3
4
|
GraffitiObjectUrl,
|
|
4
5
|
GraffitiSession,
|
|
5
6
|
} from "./2-types";
|
|
7
|
+
import { GraffitiErrorInvalidSchema } from "./3-errors";
|
|
8
|
+
import type { JSONSchema } from "json-schema-to-ts";
|
|
9
|
+
import type Ajv from "ajv";
|
|
6
10
|
|
|
7
11
|
export function unpackObjectUrl(url: string | GraffitiObjectUrl) {
|
|
8
12
|
return typeof url === "string" ? url : url.url;
|
|
@@ -67,3 +71,27 @@ export function isMediaAcceptable(
|
|
|
67
71
|
);
|
|
68
72
|
});
|
|
69
73
|
}
|
|
74
|
+
|
|
75
|
+
let ajv: Ajv | undefined = undefined;
|
|
76
|
+
export async function compileGraffitiObjectSchema<Schema extends JSONSchema>(
|
|
77
|
+
schema: Schema,
|
|
78
|
+
) {
|
|
79
|
+
if (!ajv) {
|
|
80
|
+
const { default: Ajv } = await import("ajv");
|
|
81
|
+
ajv = new Ajv({ strict: false });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Force the validation guard because
|
|
86
|
+
// it is too big for the type checker.
|
|
87
|
+
// Fortunately json-schema-to-ts is
|
|
88
|
+
// well tested against ajv.
|
|
89
|
+
return ajv.compile(schema) as (
|
|
90
|
+
data: GraffitiObjectBase,
|
|
91
|
+
) => data is GraffitiObject<Schema>;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new GraffitiErrorInvalidSchema(
|
|
94
|
+
error instanceof Error ? error.message : String(error),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
looseObject,
|
|
3
|
+
array,
|
|
4
|
+
string,
|
|
5
|
+
url,
|
|
6
|
+
union,
|
|
7
|
+
instanceof as instanceof_,
|
|
8
|
+
int,
|
|
9
|
+
tuple,
|
|
10
|
+
nullable,
|
|
11
|
+
optional,
|
|
12
|
+
nonnegative,
|
|
13
|
+
extend,
|
|
14
|
+
} from "zod/mini";
|
|
15
|
+
import type { Graffiti } from "./1-api";
|
|
16
|
+
import type {
|
|
17
|
+
GraffitiObject,
|
|
18
|
+
GraffitiObjectStream,
|
|
19
|
+
GraffitiObjectStreamContinue,
|
|
20
|
+
GraffitiPostObject,
|
|
21
|
+
GraffitiSession,
|
|
22
|
+
} from "./2-types";
|
|
23
|
+
import type { JSONSchema } from "json-schema-to-ts";
|
|
24
|
+
|
|
25
|
+
export const GraffitiPostObjectSchema = looseObject({
|
|
26
|
+
value: looseObject({}),
|
|
27
|
+
channels: array(string()),
|
|
28
|
+
allowed: optional(nullable(array(url()))),
|
|
29
|
+
});
|
|
30
|
+
export const GraffitiObjectSchema = extend(GraffitiPostObjectSchema, {
|
|
31
|
+
url: url(),
|
|
32
|
+
actor: url(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const GraffitiObjectUrlSchema = union([
|
|
36
|
+
looseObject({
|
|
37
|
+
url: url(),
|
|
38
|
+
}),
|
|
39
|
+
url(),
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
export const GraffitiSessionSchema = looseObject({
|
|
43
|
+
actor: url(),
|
|
44
|
+
});
|
|
45
|
+
export const GraffitiOptionalSessionSchema = optional(
|
|
46
|
+
nullable(GraffitiSessionSchema),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export const GraffitiPostMediaSchema = looseObject({
|
|
50
|
+
data: instanceof_(Blob),
|
|
51
|
+
allowed: optional(nullable(array(url()))),
|
|
52
|
+
});
|
|
53
|
+
export const GraffitiMediaSchema = extend(GraffitiPostMediaSchema, {
|
|
54
|
+
actor: url(),
|
|
55
|
+
});
|
|
56
|
+
export const GraffitiMediaAcceptSchema = looseObject({
|
|
57
|
+
types: optional(array(string())),
|
|
58
|
+
maxBytes: optional(int().check(nonnegative())),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
async function* wrapGraffitiStream<Schema extends JSONSchema>(
|
|
62
|
+
stream: GraffitiObjectStream<Schema>,
|
|
63
|
+
): GraffitiObjectStream<Schema> {
|
|
64
|
+
while (true) {
|
|
65
|
+
const next = await stream.next();
|
|
66
|
+
if (next.done) {
|
|
67
|
+
const { cursor, continue: continue_ } = next.value;
|
|
68
|
+
return {
|
|
69
|
+
cursor,
|
|
70
|
+
continue: (...args) => {
|
|
71
|
+
const typedArgs = tuple([GraffitiOptionalSessionSchema]).parse(args);
|
|
72
|
+
return continue_(...typedArgs);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
} else {
|
|
76
|
+
yield next.value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function* wrapGraffitiContinueStream<Schema extends JSONSchema>(
|
|
81
|
+
stream: GraffitiObjectStreamContinue<Schema>,
|
|
82
|
+
): GraffitiObjectStreamContinue<Schema> {
|
|
83
|
+
while (true) {
|
|
84
|
+
const next = await stream.next();
|
|
85
|
+
if (next.done) {
|
|
86
|
+
const { cursor, continue: continue_ } = next.value;
|
|
87
|
+
return {
|
|
88
|
+
cursor,
|
|
89
|
+
continue: (...args) => {
|
|
90
|
+
const typedArgs = tuple([GraffitiOptionalSessionSchema]).parse(args);
|
|
91
|
+
return continue_(...typedArgs);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
} else {
|
|
95
|
+
yield next.value;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// @ts-ignore - infinite nesting issue
|
|
101
|
+
export class GraffitiRuntimeTypes implements Graffiti {
|
|
102
|
+
sessionEvents: Graffiti["sessionEvents"];
|
|
103
|
+
constructor(protected readonly graffiti: Graffiti) {
|
|
104
|
+
this.sessionEvents = this.graffiti.sessionEvents;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
login: Graffiti["login"] = (...args) => {
|
|
108
|
+
const typedArgs = tuple([optional(url())]).parse(args);
|
|
109
|
+
return this.graffiti.login(...typedArgs);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
logout: Graffiti["logout"] = (...args) => {
|
|
113
|
+
const typedArgs = tuple([GraffitiSessionSchema]).parse(args);
|
|
114
|
+
return this.graffiti.logout(...typedArgs);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
handleToActor: Graffiti["handleToActor"] = (...args) => {
|
|
118
|
+
const typedArgs = tuple([string()]).parse(args);
|
|
119
|
+
return this.graffiti.handleToActor(...typedArgs);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
actorToHandle: Graffiti["actorToHandle"] = (...args) => {
|
|
123
|
+
const typedArgs = tuple([url()]).parse(args);
|
|
124
|
+
return this.graffiti.actorToHandle(...typedArgs);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
async post<Schema extends JSONSchema>(
|
|
128
|
+
partialObject: GraffitiPostObject<Schema>,
|
|
129
|
+
session: GraffitiSession,
|
|
130
|
+
): Promise<GraffitiObject<Schema>> {
|
|
131
|
+
const typedArgs = tuple([
|
|
132
|
+
GraffitiPostObjectSchema,
|
|
133
|
+
GraffitiSessionSchema,
|
|
134
|
+
]).parse([partialObject, session]);
|
|
135
|
+
|
|
136
|
+
return (await this.graffiti.post<{}>(
|
|
137
|
+
...typedArgs,
|
|
138
|
+
)) as GraffitiObject<Schema>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get: Graffiti["get"] = (...args) => {
|
|
142
|
+
const typedArgs = tuple([
|
|
143
|
+
GraffitiObjectUrlSchema,
|
|
144
|
+
looseObject({}),
|
|
145
|
+
GraffitiOptionalSessionSchema,
|
|
146
|
+
]).parse(args);
|
|
147
|
+
|
|
148
|
+
return this.graffiti.get(...typedArgs) as Promise<
|
|
149
|
+
GraffitiObject<(typeof args)[1]>
|
|
150
|
+
>;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
delete: Graffiti["delete"] = (...args) => {
|
|
154
|
+
const typedArgs = tuple([
|
|
155
|
+
GraffitiObjectUrlSchema,
|
|
156
|
+
GraffitiSessionSchema,
|
|
157
|
+
]).parse(args);
|
|
158
|
+
return this.graffiti.delete(...typedArgs);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
postMedia: Graffiti["postMedia"] = (...args) => {
|
|
162
|
+
const typedArgs = tuple([
|
|
163
|
+
GraffitiPostMediaSchema,
|
|
164
|
+
GraffitiSessionSchema,
|
|
165
|
+
]).parse(args);
|
|
166
|
+
return this.graffiti.postMedia(...typedArgs);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
getMedia: Graffiti["getMedia"] = (...args) => {
|
|
170
|
+
const typedArgs = tuple([
|
|
171
|
+
url(),
|
|
172
|
+
GraffitiMediaAcceptSchema,
|
|
173
|
+
GraffitiOptionalSessionSchema,
|
|
174
|
+
]).parse(args);
|
|
175
|
+
|
|
176
|
+
return this.graffiti.getMedia(...typedArgs);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
deleteMedia: Graffiti["deleteMedia"] = (...args) => {
|
|
180
|
+
const typedArgs = tuple([url(), GraffitiSessionSchema]).parse(args);
|
|
181
|
+
|
|
182
|
+
return this.graffiti.deleteMedia(...typedArgs);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
discover: Graffiti["discover"] = (...args) => {
|
|
186
|
+
const typedArgs = tuple([
|
|
187
|
+
array(string()),
|
|
188
|
+
looseObject({}),
|
|
189
|
+
GraffitiOptionalSessionSchema,
|
|
190
|
+
]).parse(args);
|
|
191
|
+
const stream = this.graffiti.discover(...typedArgs) as GraffitiObjectStream<
|
|
192
|
+
(typeof args)[1]
|
|
193
|
+
>;
|
|
194
|
+
return wrapGraffitiStream(stream);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
continueDiscover: Graffiti["continueDiscover"] = (...args) => {
|
|
198
|
+
const typedArgs = tuple([string(), GraffitiOptionalSessionSchema]).parse(
|
|
199
|
+
args,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const stream = this.graffiti.continueDiscover(...typedArgs);
|
|
203
|
+
return wrapGraffitiContinueStream(stream);
|
|
204
|
+
};
|
|
205
|
+
}
|