@graffiti-garden/implementation-decentralized 0.0.1

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.
Files changed (193) hide show
  1. package/LICENSE +674 -0
  2. package/dist/1-services/1-authorization.d.ts +37 -0
  3. package/dist/1-services/1-authorization.d.ts.map +1 -0
  4. package/dist/1-services/2-dids-tests.d.ts +2 -0
  5. package/dist/1-services/2-dids-tests.d.ts.map +1 -0
  6. package/dist/1-services/2-dids.d.ts +9 -0
  7. package/dist/1-services/2-dids.d.ts.map +1 -0
  8. package/dist/1-services/3-storage-buckets-tests.d.ts +2 -0
  9. package/dist/1-services/3-storage-buckets-tests.d.ts.map +1 -0
  10. package/dist/1-services/3-storage-buckets.d.ts +11 -0
  11. package/dist/1-services/3-storage-buckets.d.ts.map +1 -0
  12. package/dist/1-services/4-inboxes-tests.d.ts +2 -0
  13. package/dist/1-services/4-inboxes-tests.d.ts.map +1 -0
  14. package/dist/1-services/4-inboxes.d.ts +87 -0
  15. package/dist/1-services/4-inboxes.d.ts.map +1 -0
  16. package/dist/1-services/utilities.d.ts +7 -0
  17. package/dist/1-services/utilities.d.ts.map +1 -0
  18. package/dist/2-primitives/1-string-encoding-tests.d.ts +2 -0
  19. package/dist/2-primitives/1-string-encoding-tests.d.ts.map +1 -0
  20. package/dist/2-primitives/1-string-encoding.d.ts +6 -0
  21. package/dist/2-primitives/1-string-encoding.d.ts.map +1 -0
  22. package/dist/2-primitives/2-content-addresses-tests.d.ts +2 -0
  23. package/dist/2-primitives/2-content-addresses-tests.d.ts.map +1 -0
  24. package/dist/2-primitives/2-content-addresses.d.ts +8 -0
  25. package/dist/2-primitives/2-content-addresses.d.ts.map +1 -0
  26. package/dist/2-primitives/3-channel-attestations-tests.d.ts +2 -0
  27. package/dist/2-primitives/3-channel-attestations-tests.d.ts.map +1 -0
  28. package/dist/2-primitives/3-channel-attestations.d.ts +13 -0
  29. package/dist/2-primitives/3-channel-attestations.d.ts.map +1 -0
  30. package/dist/2-primitives/4-allowed-attestations-tests.d.ts +2 -0
  31. package/dist/2-primitives/4-allowed-attestations-tests.d.ts.map +1 -0
  32. package/dist/2-primitives/4-allowed-attestations.d.ts +9 -0
  33. package/dist/2-primitives/4-allowed-attestations.d.ts.map +1 -0
  34. package/dist/3-protocol/1-sessions.d.ts +81 -0
  35. package/dist/3-protocol/1-sessions.d.ts.map +1 -0
  36. package/dist/3-protocol/2-handles-tests.d.ts +2 -0
  37. package/dist/3-protocol/2-handles-tests.d.ts.map +1 -0
  38. package/dist/3-protocol/2-handles.d.ts +13 -0
  39. package/dist/3-protocol/2-handles.d.ts.map +1 -0
  40. package/dist/3-protocol/3-object-encoding-tests.d.ts +2 -0
  41. package/dist/3-protocol/3-object-encoding-tests.d.ts.map +1 -0
  42. package/dist/3-protocol/3-object-encoding.d.ts +43 -0
  43. package/dist/3-protocol/3-object-encoding.d.ts.map +1 -0
  44. package/dist/3-protocol/4-graffiti.d.ts +79 -0
  45. package/dist/3-protocol/4-graffiti.d.ts.map +1 -0
  46. package/dist/3-protocol/login-dialog.html.d.ts +2 -0
  47. package/dist/3-protocol/login-dialog.html.d.ts.map +1 -0
  48. package/dist/browser/ajv-QBSREQSI.js +9 -0
  49. package/dist/browser/ajv-QBSREQSI.js.map +7 -0
  50. package/dist/browser/build-BXWPS7VK.js +2 -0
  51. package/dist/browser/build-BXWPS7VK.js.map +7 -0
  52. package/dist/browser/chunk-RFBBAUMM.js +2 -0
  53. package/dist/browser/chunk-RFBBAUMM.js.map +7 -0
  54. package/dist/browser/graffiti-KV3G3O72-URO7SJIJ.js +2 -0
  55. package/dist/browser/graffiti-KV3G3O72-URO7SJIJ.js.map +7 -0
  56. package/dist/browser/index.js +16 -0
  57. package/dist/browser/index.js.map +7 -0
  58. package/dist/browser/login-dialog.html-XUWYDNNI.js +44 -0
  59. package/dist/browser/login-dialog.html-XUWYDNNI.js.map +7 -0
  60. package/dist/browser/rock-salt-LI7DAH66-KPFEBIBO.js +2 -0
  61. package/dist/browser/rock-salt-LI7DAH66-KPFEBIBO.js.map +7 -0
  62. package/dist/browser/style-YUTCEBZV-RWYJV575.js +287 -0
  63. package/dist/browser/style-YUTCEBZV-RWYJV575.js.map +7 -0
  64. package/dist/cjs/1-services/1-authorization.js +317 -0
  65. package/dist/cjs/1-services/1-authorization.js.map +7 -0
  66. package/dist/cjs/1-services/2-dids-tests.js +44 -0
  67. package/dist/cjs/1-services/2-dids-tests.js.map +7 -0
  68. package/dist/cjs/1-services/2-dids.js +47 -0
  69. package/dist/cjs/1-services/2-dids.js.map +7 -0
  70. package/dist/cjs/1-services/3-storage-buckets-tests.js +123 -0
  71. package/dist/cjs/1-services/3-storage-buckets-tests.js.map +7 -0
  72. package/dist/cjs/1-services/3-storage-buckets.js +148 -0
  73. package/dist/cjs/1-services/3-storage-buckets.js.map +7 -0
  74. package/dist/cjs/1-services/4-inboxes-tests.js +145 -0
  75. package/dist/cjs/1-services/4-inboxes-tests.js.map +7 -0
  76. package/dist/cjs/1-services/4-inboxes.js +539 -0
  77. package/dist/cjs/1-services/4-inboxes.js.map +7 -0
  78. package/dist/cjs/1-services/utilities.js +75 -0
  79. package/dist/cjs/1-services/utilities.js.map +7 -0
  80. package/dist/cjs/2-primitives/1-string-encoding-tests.js +50 -0
  81. package/dist/cjs/2-primitives/1-string-encoding-tests.js.map +7 -0
  82. package/dist/cjs/2-primitives/1-string-encoding.js +46 -0
  83. package/dist/cjs/2-primitives/1-string-encoding.js.map +7 -0
  84. package/dist/cjs/2-primitives/2-content-addresses-tests.js +62 -0
  85. package/dist/cjs/2-primitives/2-content-addresses-tests.js.map +7 -0
  86. package/dist/cjs/2-primitives/2-content-addresses.js +53 -0
  87. package/dist/cjs/2-primitives/2-content-addresses.js.map +7 -0
  88. package/dist/cjs/2-primitives/3-channel-attestations-tests.js +130 -0
  89. package/dist/cjs/2-primitives/3-channel-attestations-tests.js.map +7 -0
  90. package/dist/cjs/2-primitives/3-channel-attestations.js +84 -0
  91. package/dist/cjs/2-primitives/3-channel-attestations.js.map +7 -0
  92. package/dist/cjs/2-primitives/4-allowed-attestations-tests.js +96 -0
  93. package/dist/cjs/2-primitives/4-allowed-attestations-tests.js.map +7 -0
  94. package/dist/cjs/2-primitives/4-allowed-attestations.js +68 -0
  95. package/dist/cjs/2-primitives/4-allowed-attestations.js.map +7 -0
  96. package/dist/cjs/3-protocol/1-sessions.js +473 -0
  97. package/dist/cjs/3-protocol/1-sessions.js.map +7 -0
  98. package/dist/cjs/3-protocol/2-handles-tests.js +39 -0
  99. package/dist/cjs/3-protocol/2-handles-tests.js.map +7 -0
  100. package/dist/cjs/3-protocol/2-handles.js +65 -0
  101. package/dist/cjs/3-protocol/2-handles.js.map +7 -0
  102. package/dist/cjs/3-protocol/3-object-encoding-tests.js +253 -0
  103. package/dist/cjs/3-protocol/3-object-encoding-tests.js.map +7 -0
  104. package/dist/cjs/3-protocol/3-object-encoding.js +287 -0
  105. package/dist/cjs/3-protocol/3-object-encoding.js.map +7 -0
  106. package/dist/cjs/3-protocol/4-graffiti.js +937 -0
  107. package/dist/cjs/3-protocol/4-graffiti.js.map +7 -0
  108. package/dist/cjs/3-protocol/login-dialog.html.js +67 -0
  109. package/dist/cjs/3-protocol/login-dialog.html.js.map +7 -0
  110. package/dist/cjs/index.js +32 -0
  111. package/dist/cjs/index.js.map +7 -0
  112. package/dist/cjs/index.spec.js +130 -0
  113. package/dist/cjs/index.spec.js.map +7 -0
  114. package/dist/esm/1-services/1-authorization.js +304 -0
  115. package/dist/esm/1-services/1-authorization.js.map +7 -0
  116. package/dist/esm/1-services/2-dids-tests.js +24 -0
  117. package/dist/esm/1-services/2-dids-tests.js.map +7 -0
  118. package/dist/esm/1-services/2-dids.js +27 -0
  119. package/dist/esm/1-services/2-dids.js.map +7 -0
  120. package/dist/esm/1-services/3-storage-buckets-tests.js +103 -0
  121. package/dist/esm/1-services/3-storage-buckets-tests.js.map +7 -0
  122. package/dist/esm/1-services/3-storage-buckets.js +132 -0
  123. package/dist/esm/1-services/3-storage-buckets.js.map +7 -0
  124. package/dist/esm/1-services/4-inboxes-tests.js +125 -0
  125. package/dist/esm/1-services/4-inboxes-tests.js.map +7 -0
  126. package/dist/esm/1-services/4-inboxes.js +533 -0
  127. package/dist/esm/1-services/4-inboxes.js.map +7 -0
  128. package/dist/esm/1-services/utilities.js +60 -0
  129. package/dist/esm/1-services/utilities.js.map +7 -0
  130. package/dist/esm/2-primitives/1-string-encoding-tests.js +33 -0
  131. package/dist/esm/2-primitives/1-string-encoding-tests.js.map +7 -0
  132. package/dist/esm/2-primitives/1-string-encoding.js +26 -0
  133. package/dist/esm/2-primitives/1-string-encoding.js.map +7 -0
  134. package/dist/esm/2-primitives/2-content-addresses-tests.js +45 -0
  135. package/dist/esm/2-primitives/2-content-addresses-tests.js.map +7 -0
  136. package/dist/esm/2-primitives/2-content-addresses.js +33 -0
  137. package/dist/esm/2-primitives/2-content-addresses.js.map +7 -0
  138. package/dist/esm/2-primitives/3-channel-attestations-tests.js +116 -0
  139. package/dist/esm/2-primitives/3-channel-attestations-tests.js.map +7 -0
  140. package/dist/esm/2-primitives/3-channel-attestations.js +69 -0
  141. package/dist/esm/2-primitives/3-channel-attestations.js.map +7 -0
  142. package/dist/esm/2-primitives/4-allowed-attestations-tests.js +82 -0
  143. package/dist/esm/2-primitives/4-allowed-attestations-tests.js.map +7 -0
  144. package/dist/esm/2-primitives/4-allowed-attestations.js +51 -0
  145. package/dist/esm/2-primitives/4-allowed-attestations.js.map +7 -0
  146. package/dist/esm/3-protocol/1-sessions.js +465 -0
  147. package/dist/esm/3-protocol/1-sessions.js.map +7 -0
  148. package/dist/esm/3-protocol/2-handles-tests.js +19 -0
  149. package/dist/esm/3-protocol/2-handles-tests.js.map +7 -0
  150. package/dist/esm/3-protocol/2-handles.js +45 -0
  151. package/dist/esm/3-protocol/2-handles.js.map +7 -0
  152. package/dist/esm/3-protocol/3-object-encoding-tests.js +248 -0
  153. package/dist/esm/3-protocol/3-object-encoding-tests.js.map +7 -0
  154. package/dist/esm/3-protocol/3-object-encoding.js +280 -0
  155. package/dist/esm/3-protocol/3-object-encoding.js.map +7 -0
  156. package/dist/esm/3-protocol/4-graffiti.js +957 -0
  157. package/dist/esm/3-protocol/4-graffiti.js.map +7 -0
  158. package/dist/esm/3-protocol/login-dialog.html.js +47 -0
  159. package/dist/esm/3-protocol/login-dialog.html.js.map +7 -0
  160. package/dist/esm/index.js +14 -0
  161. package/dist/esm/index.js.map +7 -0
  162. package/dist/esm/index.spec.js +133 -0
  163. package/dist/esm/index.spec.js.map +7 -0
  164. package/dist/index.d.ts +10 -0
  165. package/dist/index.d.ts.map +1 -0
  166. package/dist/index.spec.d.ts +2 -0
  167. package/dist/index.spec.d.ts.map +1 -0
  168. package/package.json +62 -0
  169. package/src/1-services/1-authorization.ts +399 -0
  170. package/src/1-services/2-dids-tests.ts +24 -0
  171. package/src/1-services/2-dids.ts +30 -0
  172. package/src/1-services/3-storage-buckets-tests.ts +121 -0
  173. package/src/1-services/3-storage-buckets.ts +183 -0
  174. package/src/1-services/4-inboxes-tests.ts +154 -0
  175. package/src/1-services/4-inboxes.ts +722 -0
  176. package/src/1-services/utilities.ts +65 -0
  177. package/src/2-primitives/1-string-encoding-tests.ts +33 -0
  178. package/src/2-primitives/1-string-encoding.ts +33 -0
  179. package/src/2-primitives/2-content-addresses-tests.ts +46 -0
  180. package/src/2-primitives/2-content-addresses.ts +42 -0
  181. package/src/2-primitives/3-channel-attestations-tests.ts +125 -0
  182. package/src/2-primitives/3-channel-attestations.ts +95 -0
  183. package/src/2-primitives/4-allowed-attestations-tests.ts +86 -0
  184. package/src/2-primitives/4-allowed-attestations.ts +69 -0
  185. package/src/3-protocol/1-sessions.ts +601 -0
  186. package/src/3-protocol/2-handles-tests.ts +17 -0
  187. package/src/3-protocol/2-handles.ts +60 -0
  188. package/src/3-protocol/3-object-encoding-tests.ts +269 -0
  189. package/src/3-protocol/3-object-encoding.ts +396 -0
  190. package/src/3-protocol/4-graffiti.ts +1265 -0
  191. package/src/3-protocol/login-dialog.html.ts +43 -0
  192. package/src/index.spec.ts +158 -0
  193. package/src/index.ts +16 -0
@@ -0,0 +1,465 @@
1
+ import { DecentralizedIdentifiers } from "../1-services/2-dids";
2
+ import {
3
+ InitializedEventDetailSchema,
4
+ LoginEventDetailSchema,
5
+ LogoutEventDetailSchema
6
+ } from "../1-services/1-authorization";
7
+ import { StorageBuckets } from "../1-services/3-storage-buckets";
8
+ import {
9
+ extend,
10
+ array,
11
+ string,
12
+ object,
13
+ url,
14
+ tuple,
15
+ enum as enum_
16
+ } from "zod/mini";
17
+ const DID_SERVICE_TYPE_GRAFFITI_INBOX = "GraffitiInbox";
18
+ const DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET = "GraffitiStorageBucket";
19
+ const DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX = "#graffitiPersonalInbox";
20
+ const DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET = "#graffitiStorageBucket";
21
+ const DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX = "#graffitiSharedInbox_";
22
+ class Sessions {
23
+ constructor(services) {
24
+ this.services = services;
25
+ const initializedPromise = new Promise((resolve) => {
26
+ this.services.authorization.eventTarget.addEventListener(
27
+ "initialized",
28
+ (e) => {
29
+ if (!(e instanceof CustomEvent)) return;
30
+ const parsed = InitializedEventDetailSchema.safeParse(e.detail);
31
+ if (!parsed.success) return;
32
+ const error = parsed.data?.error;
33
+ if (error) console.log(error);
34
+ resolve();
35
+ }
36
+ );
37
+ });
38
+ this.services.authorization.eventTarget.addEventListener(
39
+ "login",
40
+ this.onLogin.bind(this)
41
+ );
42
+ this.services.authorization.eventTarget.addEventListener(
43
+ "logout",
44
+ this.onLogout.bind(this)
45
+ );
46
+ let loginPromise;
47
+ if (typeof window !== "undefined") {
48
+ const actorEncoded = new URLSearchParams(window.location.search).get(
49
+ "actor"
50
+ );
51
+ if (actorEncoded) {
52
+ try {
53
+ const actor = decodeURIComponent(actorEncoded);
54
+ const url2 = new URL(window.location.toString());
55
+ url2.searchParams.delete("actor");
56
+ window.history.replaceState({}, "", url2.toString());
57
+ loginPromise = this.login(actor);
58
+ } catch (error) {
59
+ console.error("Error decoding actor:", error);
60
+ }
61
+ }
62
+ }
63
+ (async () => {
64
+ await new Promise((resolve) => setTimeout(resolve, 0));
65
+ await loginPromise;
66
+ for (const session of this.loggedInSessions) {
67
+ const loginEvent = new CustomEvent("login", {
68
+ detail: { session: { actor: session.actor } }
69
+ });
70
+ this.sessionEvents.dispatchEvent(loginEvent);
71
+ }
72
+ await initializedPromise;
73
+ const initializedEvent = new CustomEvent(
74
+ "initialized"
75
+ );
76
+ this.sessionEvents.dispatchEvent(initializedEvent);
77
+ })();
78
+ }
79
+ sessionEvents = new EventTarget();
80
+ inProgressLogin = void 0;
81
+ inProgressLogout = void 0;
82
+ async login(actor) {
83
+ try {
84
+ await this.login_(actor);
85
+ } catch (e) {
86
+ const loginEvent = new CustomEvent("login", {
87
+ detail: {
88
+ error: e instanceof Error ? e : new Error(String(e)),
89
+ session: { actor }
90
+ }
91
+ });
92
+ this.sessionEvents.dispatchEvent(loginEvent);
93
+ }
94
+ }
95
+ async login_(actor) {
96
+ const existingSession = this.loggedInSessions.find(
97
+ (session2) => session2.actor === actor
98
+ );
99
+ if (existingSession) {
100
+ this.sessionEvents.dispatchEvent(
101
+ new CustomEvent("login", { detail: { session: { actor } } })
102
+ );
103
+ return;
104
+ }
105
+ const actorDocument = await this.services.dids.resolve(actor);
106
+ const services = actorDocument.service;
107
+ if (!services) {
108
+ throw new Error(`No services found in actor document for ${actor}`);
109
+ }
110
+ const storageBucketService = services.find(
111
+ (service) => service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET && service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET
112
+ );
113
+ const personalInboxService = services.find(
114
+ (service) => service.id === DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX && service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX
115
+ );
116
+ const sharedInboxServices = services.filter(
117
+ (service) => service.id.match(
118
+ new RegExp(`^${DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX}\\d+$`)
119
+ ) && service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX
120
+ );
121
+ if (!personalInboxService || !storageBucketService || sharedInboxServices.length === 0) {
122
+ throw new Error(
123
+ `Required services not found in actor document for ${actor}`
124
+ );
125
+ }
126
+ const storageBucketEndpoint = serviceToEndpoint(storageBucketService);
127
+ const personalInboxEndpoint = serviceToEndpoint(personalInboxService);
128
+ const sharedInboxEndpoints = sharedInboxServices.map(serviceToEndpoint);
129
+ const servicesWithTypes = [
130
+ { endpoint: storageBucketEndpoint, type: "bucket" },
131
+ { endpoint: personalInboxEndpoint, type: "personal-inbox" },
132
+ ...sharedInboxEndpoints.map(
133
+ (endpoint) => ({
134
+ endpoint,
135
+ type: "shared-inbox"
136
+ })
137
+ )
138
+ ];
139
+ const servicesWithAuthorizationEndpoints = await Promise.all(
140
+ servicesWithTypes.map(async ({ endpoint, type }) => {
141
+ const authorizationEndpoint = await (type === "bucket" ? this.services.storageBuckets.getAuthorizationEndpoint(endpoint) : this.services.inboxes.getAuthorizationEndpoint(endpoint));
142
+ return { endpoint, authorizationEndpoint, type };
143
+ })
144
+ );
145
+ const servicesByAuthorizationMap = /* @__PURE__ */ new Map();
146
+ servicesWithAuthorizationEndpoints.forEach(
147
+ ({ authorizationEndpoint, endpoint, type }) => {
148
+ if (!servicesByAuthorizationMap.has(authorizationEndpoint)) {
149
+ servicesByAuthorizationMap.set(authorizationEndpoint, []);
150
+ }
151
+ servicesByAuthorizationMap.get(authorizationEndpoint).push({ endpoint, type });
152
+ }
153
+ );
154
+ const servicesByAuthorization = [...servicesByAuthorizationMap.entries()];
155
+ const session = { actor };
156
+ const inProgressLogin = {
157
+ ...session,
158
+ tokens: [],
159
+ servicesByAuthorization
160
+ };
161
+ if (typeof window !== "undefined") {
162
+ window.localStorage.setItem(
163
+ LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,
164
+ JSON.stringify(inProgressLogin)
165
+ );
166
+ } else {
167
+ this.inProgressLogin = inProgressLogin;
168
+ }
169
+ const [firstAuthorizationEndpoint, firstServices] = servicesByAuthorization[0];
170
+ await this.services.authorization.login(
171
+ firstAuthorizationEndpoint,
172
+ actor,
173
+ firstServices.map((s) => s.endpoint)
174
+ );
175
+ }
176
+ async onLogin(event) {
177
+ if (!(event instanceof CustomEvent)) return;
178
+ const parsed = LoginEventDetailSchema.safeParse(event.detail);
179
+ if (!parsed.success) return;
180
+ const actor = parsed.data.loginId;
181
+ try {
182
+ await this.onLogin_(parsed.data);
183
+ } catch (e) {
184
+ const LoginEvent = new CustomEvent("login", {
185
+ detail: {
186
+ error: e instanceof Error ? e : new Error(String(e)),
187
+ session: { actor }
188
+ }
189
+ });
190
+ this.sessionEvents.dispatchEvent(LoginEvent);
191
+ }
192
+ }
193
+ async onLogin_(loginDetail) {
194
+ if (loginDetail.error) throw loginDetail.error;
195
+ const token = loginDetail.token;
196
+ const actor = loginDetail.loginId;
197
+ let inProgressLogin;
198
+ if (typeof window !== "undefined") {
199
+ const inProgressLoginString = window.localStorage.getItem(
200
+ LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY
201
+ );
202
+ if (!inProgressLoginString) {
203
+ throw new Error("No in-progress login found");
204
+ }
205
+ const json = JSON.parse(inProgressLoginString);
206
+ inProgressLogin = InProgressSchema.parse(json);
207
+ } else {
208
+ if (!this.inProgressLogin) {
209
+ throw new Error("No in-progress login found");
210
+ }
211
+ inProgressLogin = this.inProgressLogin;
212
+ }
213
+ if (inProgressLogin.actor !== actor) {
214
+ throw new Error("Actor mismatch in login response - concurrent logins?");
215
+ }
216
+ inProgressLogin.tokens.push(token);
217
+ if (inProgressLogin.tokens.length === inProgressLogin.servicesByAuthorization.length) {
218
+ if (typeof window === "undefined") {
219
+ this.inProgressLogin = void 0;
220
+ } else {
221
+ window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY);
222
+ }
223
+ const services = inProgressLogin.servicesByAuthorization.flatMap(
224
+ ([authorizationEndpoint, services2], index) => services2.map((service) => ({
225
+ token: inProgressLogin.tokens[index],
226
+ serviceEndpoint: service.endpoint,
227
+ authorizationEndpoint,
228
+ type: service.type
229
+ }))
230
+ );
231
+ const session = {
232
+ ...inProgressLogin,
233
+ storageBucket: services.find((s) => s.type === "bucket"),
234
+ personalInbox: services.find((s) => s.type === "personal-inbox"),
235
+ sharedInboxes: services.filter((s) => s.type === "shared-inbox")
236
+ };
237
+ const sessions = this.loggedInSessions;
238
+ sessions.push(session);
239
+ this.loggedInSessions = sessions;
240
+ const loginEvent = new CustomEvent("login", {
241
+ detail: { session: { actor } }
242
+ });
243
+ this.sessionEvents.dispatchEvent(loginEvent);
244
+ } else {
245
+ if (typeof window !== "undefined") {
246
+ window.localStorage.setItem(
247
+ LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,
248
+ JSON.stringify(inProgressLogin)
249
+ );
250
+ } else {
251
+ this.inProgressLogin = inProgressLogin;
252
+ }
253
+ const [authorizationEndpoint, services] = inProgressLogin.servicesByAuthorization[inProgressLogin.tokens.length];
254
+ await this.services.authorization.login(
255
+ authorizationEndpoint,
256
+ actor,
257
+ services.map((s) => s.endpoint)
258
+ );
259
+ }
260
+ }
261
+ async logout(actor) {
262
+ try {
263
+ await this.logout_(actor);
264
+ } catch (e) {
265
+ const logoutEvent = new CustomEvent("logout", {
266
+ detail: {
267
+ error: e instanceof Error ? e : new Error(String(e)),
268
+ actor
269
+ }
270
+ });
271
+ this.sessionEvents.dispatchEvent(logoutEvent);
272
+ }
273
+ }
274
+ async logout_(actor) {
275
+ const session = this.loggedInSessions.find(
276
+ (session2) => session2.actor === actor
277
+ );
278
+ if (!session) {
279
+ throw new Error(`No session found for actor ${actor}`);
280
+ }
281
+ this.loggedInSessions = this.loggedInSessions.filter(
282
+ (session2) => session2.actor !== actor
283
+ );
284
+ const token = session.tokens.pop();
285
+ if (!token) {
286
+ throw new Error("No tokens found in session");
287
+ }
288
+ if (typeof window !== "undefined") {
289
+ window.localStorage.setItem(
290
+ LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,
291
+ JSON.stringify(session)
292
+ );
293
+ } else {
294
+ this.inProgressLogout = session;
295
+ }
296
+ const [authorizationEndpoint, _] = session.servicesByAuthorization[session.tokens.length];
297
+ await this.services.authorization.logout(
298
+ authorizationEndpoint,
299
+ actor,
300
+ token
301
+ );
302
+ }
303
+ async onLogout(event) {
304
+ if (!(event instanceof CustomEvent)) return;
305
+ const parsed = LogoutEventDetailSchema.safeParse(event.detail);
306
+ if (!parsed.success) return;
307
+ const actor = parsed.data.logoutId;
308
+ try {
309
+ await this.onLogout_(parsed.data);
310
+ } catch (e) {
311
+ const logoutEvent = new CustomEvent("logout", {
312
+ detail: {
313
+ error: e instanceof Error ? e : new Error(String(e)),
314
+ actor
315
+ }
316
+ });
317
+ this.sessionEvents.dispatchEvent(logoutEvent);
318
+ }
319
+ }
320
+ async onLogout_(logoutDetail) {
321
+ if (logoutDetail.error) throw logoutDetail.error;
322
+ const actor = logoutDetail.logoutId;
323
+ let inProgressLogout;
324
+ if (typeof window !== "undefined") {
325
+ const inProgressLogoutString = window.localStorage.getItem(
326
+ LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY
327
+ );
328
+ if (!inProgressLogoutString) {
329
+ throw new Error("No in-progress logout found");
330
+ }
331
+ const json = JSON.parse(inProgressLogoutString);
332
+ inProgressLogout = InProgressSchema.parse(json);
333
+ } else {
334
+ if (!this.inProgressLogout) {
335
+ throw new Error("No in-progress logout found");
336
+ }
337
+ inProgressLogout = this.inProgressLogout;
338
+ }
339
+ if (inProgressLogout.actor !== actor) {
340
+ throw new Error(
341
+ "Actor mismatch in logout response - concurrent logouts?"
342
+ );
343
+ }
344
+ const token = inProgressLogout.tokens.pop();
345
+ if (!token) {
346
+ if (typeof window === "undefined") {
347
+ this.inProgressLogout = void 0;
348
+ } else {
349
+ window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY);
350
+ }
351
+ const logoutEvent = new CustomEvent("logout", {
352
+ detail: { actor }
353
+ });
354
+ this.sessionEvents.dispatchEvent(logoutEvent);
355
+ } else {
356
+ if (typeof window !== "undefined") {
357
+ window.localStorage.setItem(
358
+ LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,
359
+ JSON.stringify(inProgressLogout)
360
+ );
361
+ } else {
362
+ this.inProgressLogout = inProgressLogout;
363
+ }
364
+ const [authorizationEndpoint, _] = inProgressLogout.servicesByAuthorization[inProgressLogout.tokens.length];
365
+ await this.services.authorization.logout(
366
+ authorizationEndpoint,
367
+ actor,
368
+ token
369
+ );
370
+ }
371
+ }
372
+ get loggedInSessions() {
373
+ if (typeof window === "undefined") return loggedInSessions_;
374
+ const data = window.localStorage.getItem(
375
+ LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY
376
+ );
377
+ if (!data) return [];
378
+ let json;
379
+ try {
380
+ json = JSON.parse(data);
381
+ } catch {
382
+ console.error("Error parsing stored session data");
383
+ window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);
384
+ return [];
385
+ }
386
+ const parsed = array(StoredSessionSchema).safeParse(json);
387
+ if (!parsed.success) {
388
+ console.error("Stored session data is invalid");
389
+ window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);
390
+ return [];
391
+ }
392
+ return parsed.data;
393
+ }
394
+ set loggedInSessions(sessions) {
395
+ if (typeof window === "undefined") {
396
+ loggedInSessions_ = sessions;
397
+ return;
398
+ }
399
+ window.localStorage.setItem(
400
+ LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,
401
+ JSON.stringify(sessions)
402
+ );
403
+ }
404
+ resolveSession(session) {
405
+ const resolvedSession = this.loggedInSessions.find(
406
+ (s) => s.actor === session.actor
407
+ );
408
+ if (!resolvedSession) {
409
+ const logoutEvent = new CustomEvent("logout", {
410
+ detail: { actor: session.actor }
411
+ });
412
+ this.sessionEvents.dispatchEvent(logoutEvent);
413
+ throw new Error("Not logged in");
414
+ }
415
+ return resolvedSession;
416
+ }
417
+ }
418
+ let loggedInSessions_ = [];
419
+ const LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY = "graffiti-login-in-progress";
420
+ const LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY = "graffiti-logout-in-progress";
421
+ const LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY = "graffiti-sessions-logged-in";
422
+ const GraffitiSessionSchema = object({
423
+ actor: url()
424
+ });
425
+ const ServiceSessionSchema = object({
426
+ token: string(),
427
+ serviceEndpoint: url(),
428
+ authorizationEndpoint: url()
429
+ });
430
+ const ServicesByAuthorizationSchema = array(
431
+ tuple([
432
+ url(),
433
+ // Authorization endpoint
434
+ array(
435
+ object({
436
+ endpoint: url(),
437
+ // Service endpoint
438
+ type: enum_(["bucket", "personal-inbox", "shared-inbox"])
439
+ })
440
+ )
441
+ ])
442
+ );
443
+ const InProgressSchema = extend(GraffitiSessionSchema, {
444
+ tokens: array(string()),
445
+ servicesByAuthorization: ServicesByAuthorizationSchema
446
+ });
447
+ const StoredSessionSchema = extend(InProgressSchema, {
448
+ storageBucket: ServiceSessionSchema,
449
+ personalInbox: ServiceSessionSchema,
450
+ sharedInboxes: array(ServiceSessionSchema)
451
+ });
452
+ function serviceToEndpoint(service) {
453
+ if (typeof service.serviceEndpoint === "string")
454
+ return service.serviceEndpoint;
455
+ throw new Error(`Service endpoint for ${service.id} is not a string`);
456
+ }
457
+ export {
458
+ DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX,
459
+ DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX,
460
+ DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET,
461
+ DID_SERVICE_TYPE_GRAFFITI_INBOX,
462
+ DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,
463
+ Sessions
464
+ };
465
+ //# sourceMappingURL=1-sessions.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/3-protocol/1-sessions.ts"],
4
+ "sourcesContent": ["import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSession,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\nimport { DecentralizedIdentifiers } from \"../1-services/2-dids\";\nimport {\n InitializedEventDetailSchema,\n LoginEventDetailSchema,\n LogoutEventDetailSchema,\n type Authorization,\n} from \"../1-services/1-authorization\";\nimport { StorageBuckets } from \"../1-services/3-storage-buckets\";\nimport type { Inboxes } from \"../1-services/4-inboxes\";\nimport type { Service } from \"did-resolver\";\nimport {\n type infer as infer_,\n extend,\n array,\n string,\n object,\n url,\n tuple,\n enum as enum_,\n} from \"zod/mini\";\n\nexport const DID_SERVICE_TYPE_GRAFFITI_INBOX = \"GraffitiInbox\";\nexport const DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET = \"GraffitiStorageBucket\";\nexport const DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX = \"#graffitiPersonalInbox\";\nexport const DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET = \"#graffitiStorageBucket\";\nexport const DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX =\n \"#graffitiSharedInbox_\";\n\nexport class Sessions {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n constructor(\n protected readonly services: {\n readonly dids: DecentralizedIdentifiers;\n readonly authorization: Authorization;\n readonly storageBuckets: StorageBuckets;\n readonly inboxes: Inboxes;\n },\n ) {\n const initializedPromise = new Promise<void>((resolve) => {\n this.services.authorization.eventTarget.addEventListener(\n \"initialized\",\n (e) => {\n if (!(e instanceof CustomEvent)) return;\n const parsed = InitializedEventDetailSchema.safeParse(e.detail);\n if (!parsed.success) return;\n const error = parsed.data?.error;\n if (error) console.log(error);\n resolve();\n },\n );\n });\n this.services.authorization.eventTarget.addEventListener(\n \"login\",\n this.onLogin.bind(this),\n );\n this.services.authorization.eventTarget.addEventListener(\n \"logout\",\n this.onLogout.bind(this),\n );\n\n // Handle account registration redirect immediately,\n // to prevent SPA routers from hijacking the URL too soon\n let loginPromise: Promise<void> | undefined;\n if (typeof window !== \"undefined\") {\n const actorEncoded = new URLSearchParams(window.location.search).get(\n \"actor\",\n );\n if (actorEncoded) {\n try {\n // Get the actor\n const actor = decodeURIComponent(actorEncoded);\n // Strip it from the URL\n const url = new URL(window.location.toString());\n url.searchParams.delete(\"actor\");\n window.history.replaceState({}, \"\", url.toString());\n // Complete the login\n loginPromise = this.login(actor);\n } catch (error) {\n console.error(\"Error decoding actor:\", error);\n }\n }\n }\n\n (async () => {\n // Allow listeners to be added before dispatching events\n await new Promise((resolve) => setTimeout(resolve, 0));\n\n // Wait for login to complete, if there\n await loginPromise;\n\n for (const session of this.loggedInSessions) {\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor: session.actor } },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n }\n\n await initializedPromise;\n\n // Send own initialized event\n const initializedEvent: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n );\n this.sessionEvents.dispatchEvent(initializedEvent);\n })();\n }\n\n protected inProgressLogin: infer_<typeof InProgressSchema> | undefined =\n undefined;\n protected inProgressLogout: infer_<typeof InProgressSchema> | undefined =\n undefined;\n\n async login(actor: string) {\n try {\n await this.login_(actor);\n } catch (e) {\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n session: { actor },\n },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n }\n }\n protected async login_(actor: string) {\n // First look to see if we're already logged in\n const existingSession = this.loggedInSessions.find(\n (session) => session.actor === actor,\n );\n if (existingSession) {\n this.sessionEvents.dispatchEvent(\n new CustomEvent(\"login\", { detail: { session: { actor } } }),\n );\n return;\n }\n\n const actorDocument = await this.services.dids.resolve(actor);\n\n const services = actorDocument.service;\n if (!services) {\n throw new Error(`No services found in actor document for ${actor}`);\n }\n\n const storageBucketService = services.find(\n (service) =>\n service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&\n service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,\n );\n const personalInboxService = services.find(\n (service) =>\n service.id === DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX &&\n service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX,\n );\n const sharedInboxServices = services.filter(\n (service) =>\n service.id.match(\n new RegExp(`^${DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX}\\\\d+$`),\n ) && service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX,\n );\n\n if (\n !personalInboxService ||\n !storageBucketService ||\n sharedInboxServices.length === 0\n ) {\n throw new Error(\n `Required services not found in actor document for ${actor}`,\n );\n }\n\n // Massage the services into a list of endpoints with types\n const storageBucketEndpoint: string =\n serviceToEndpoint(storageBucketService);\n const personalInboxEndpoint: string =\n serviceToEndpoint(personalInboxService);\n const sharedInboxEndpoints: string[] =\n sharedInboxServices.map(serviceToEndpoint);\n const servicesWithTypes = [\n { endpoint: storageBucketEndpoint, type: \"bucket\" } as const,\n { endpoint: personalInboxEndpoint, type: \"personal-inbox\" } as const,\n ...sharedInboxEndpoints.map(\n (endpoint) =>\n ({\n endpoint,\n type: \"shared-inbox\",\n }) as const,\n ),\n ];\n\n // Fetch the authorization endpoints for each service\n const servicesWithAuthorizationEndpoints = await Promise.all(\n servicesWithTypes.map(async ({ endpoint, type }) => {\n const authorizationEndpoint = await (type === \"bucket\"\n ? this.services.storageBuckets.getAuthorizationEndpoint(endpoint)\n : this.services.inboxes.getAuthorizationEndpoint(endpoint));\n return { endpoint, authorizationEndpoint, type };\n }),\n );\n\n // Group the endpoints according to their authorization endpoints\n const servicesByAuthorizationMap: Map<\n string,\n {\n endpoint: string;\n type: \"bucket\" | \"personal-inbox\" | \"shared-inbox\";\n }[]\n > = new Map();\n servicesWithAuthorizationEndpoints.forEach(\n ({ authorizationEndpoint, endpoint, type }) => {\n if (!servicesByAuthorizationMap.has(authorizationEndpoint)) {\n servicesByAuthorizationMap.set(authorizationEndpoint, []);\n }\n servicesByAuthorizationMap\n .get(authorizationEndpoint)!\n .push({ endpoint, type });\n },\n );\n const servicesByAuthorization = [...servicesByAuthorizationMap.entries()];\n\n const session: GraffitiSession = { actor };\n\n const inProgressLogin: infer_<typeof InProgressSchema> = {\n ...session,\n tokens: [],\n servicesByAuthorization,\n };\n\n if (typeof window !== \"undefined\") {\n // Store the in-progress session in localStorage\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n JSON.stringify(inProgressLogin),\n );\n } else {\n this.inProgressLogin = inProgressLogin;\n }\n\n // Start the login process with the first endpoint\n const [firstAuthorizationEndpoint, firstServices] =\n servicesByAuthorization[0];\n await this.services.authorization.login(\n firstAuthorizationEndpoint,\n actor,\n firstServices.map((s) => s.endpoint),\n );\n }\n\n protected async onLogin(event: unknown) {\n if (!(event instanceof CustomEvent)) return;\n const parsed = LoginEventDetailSchema.safeParse(event.detail);\n if (!parsed.success) return;\n\n const actor = parsed.data.loginId;\n\n try {\n await this.onLogin_(parsed.data);\n } catch (e) {\n const LoginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n session: { actor },\n },\n });\n this.sessionEvents.dispatchEvent(LoginEvent);\n }\n }\n protected async onLogin_(loginDetail: infer_<typeof LoginEventDetailSchema>) {\n if (loginDetail.error) throw loginDetail.error;\n\n const token = loginDetail.token;\n const actor = loginDetail.loginId;\n\n // Lookup the in-progress session\n let inProgressLogin: infer_<typeof InProgressSchema>;\n if (typeof window !== \"undefined\") {\n const inProgressLoginString = window.localStorage.getItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n );\n if (!inProgressLoginString) {\n throw new Error(\"No in-progress login found\");\n }\n\n const json = JSON.parse(inProgressLoginString);\n inProgressLogin = InProgressSchema.parse(json);\n } else {\n if (!this.inProgressLogin) {\n throw new Error(\"No in-progress login found\");\n }\n inProgressLogin = this.inProgressLogin;\n }\n\n if (inProgressLogin.actor !== actor) {\n throw new Error(\"Actor mismatch in login response - concurrent logins?\");\n }\n\n inProgressLogin.tokens.push(token);\n\n if (\n inProgressLogin.tokens.length ===\n inProgressLogin.servicesByAuthorization.length\n ) {\n // Login complete!\n if (typeof window === \"undefined\") {\n this.inProgressLogin = undefined;\n } else {\n window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY);\n }\n\n // Build the completed session\n const services = inProgressLogin.servicesByAuthorization.flatMap(\n ([authorizationEndpoint, services], index) =>\n services.map((service) => ({\n token: inProgressLogin.tokens[index],\n serviceEndpoint: service.endpoint,\n authorizationEndpoint,\n type: service.type,\n })),\n );\n\n const session: StoredSession = {\n ...inProgressLogin,\n storageBucket: services.find((s) => s.type === \"bucket\")!,\n personalInbox: services.find((s) => s.type === \"personal-inbox\")!,\n sharedInboxes: services.filter((s) => s.type === \"shared-inbox\")!,\n };\n\n // Store the completed session\n const sessions = this.loggedInSessions;\n sessions.push(session);\n this.loggedInSessions = sessions;\n\n // Return the completed session\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor } },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n } else {\n // Store the in progress and continue\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n JSON.stringify(inProgressLogin),\n );\n } else {\n this.inProgressLogin = inProgressLogin;\n }\n\n // Continue to the next authorization endpoint\n const [authorizationEndpoint, services] =\n inProgressLogin.servicesByAuthorization[inProgressLogin.tokens.length];\n await this.services.authorization.login(\n authorizationEndpoint,\n actor,\n services.map((s) => s.endpoint),\n );\n }\n }\n\n async logout(actor: string) {\n try {\n await this.logout_(actor);\n } catch (e) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n actor,\n },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n }\n }\n protected async logout_(actor: string) {\n const session = this.loggedInSessions.find(\n (session) => session.actor === actor,\n );\n if (!session) {\n throw new Error(`No session found for actor ${actor}`);\n }\n\n // Remove the session(s)\n this.loggedInSessions = this.loggedInSessions.filter(\n (session) => session.actor !== actor,\n );\n\n // Begin the logout\n const token = session.tokens.pop();\n if (!token) {\n throw new Error(\"No tokens found in session\");\n }\n // Store the in progress logout\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n JSON.stringify(session),\n );\n } else {\n this.inProgressLogout = session;\n }\n const [authorizationEndpoint, _] =\n session.servicesByAuthorization[session.tokens.length];\n await this.services.authorization.logout(\n authorizationEndpoint,\n actor,\n token,\n );\n }\n\n protected async onLogout(event: unknown) {\n if (!(event instanceof CustomEvent)) return;\n const parsed = LogoutEventDetailSchema.safeParse(event.detail);\n if (!parsed.success) return;\n\n const actor = parsed.data.logoutId;\n\n try {\n await this.onLogout_(parsed.data);\n } catch (e) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n actor,\n },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n }\n }\n protected async onLogout_(\n logoutDetail: infer_<typeof LogoutEventDetailSchema>,\n ) {\n if (logoutDetail.error) throw logoutDetail.error;\n\n const actor = logoutDetail.logoutId;\n\n // Lookup the in-progress session\n let inProgressLogout: infer_<typeof InProgressSchema>;\n if (typeof window !== \"undefined\") {\n const inProgressLogoutString = window.localStorage.getItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n );\n if (!inProgressLogoutString) {\n throw new Error(\"No in-progress logout found\");\n }\n\n const json = JSON.parse(inProgressLogoutString);\n inProgressLogout = InProgressSchema.parse(json);\n } else {\n if (!this.inProgressLogout) {\n throw new Error(\"No in-progress logout found\");\n }\n inProgressLogout = this.inProgressLogout;\n }\n\n if (inProgressLogout.actor !== actor) {\n throw new Error(\n \"Actor mismatch in logout response - concurrent logouts?\",\n );\n }\n\n const token = inProgressLogout.tokens.pop();\n if (!token) {\n // Logout complete\n if (typeof window === \"undefined\") {\n this.inProgressLogout = undefined;\n } else {\n window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY);\n }\n\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: { actor },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n } else {\n // Store the in progress and continue\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n JSON.stringify(inProgressLogout),\n );\n } else {\n this.inProgressLogout = inProgressLogout;\n }\n\n // Continue to the next authorization endpoint\n const [authorizationEndpoint, _] =\n inProgressLogout.servicesByAuthorization[\n inProgressLogout.tokens.length\n ];\n await this.services.authorization.logout(\n authorizationEndpoint,\n actor,\n token,\n );\n }\n }\n\n protected get loggedInSessions(): StoredSession[] {\n if (typeof window === \"undefined\") return loggedInSessions_;\n\n const data = window.localStorage.getItem(\n LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,\n );\n if (!data) return [];\n\n let json: unknown;\n try {\n json = JSON.parse(data);\n } catch {\n console.error(\"Error parsing stored session data\");\n window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);\n return [];\n }\n\n const parsed = array(StoredSessionSchema).safeParse(json);\n if (!parsed.success) {\n console.error(\"Stored session data is invalid\");\n window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);\n return [];\n }\n return parsed.data;\n }\n protected set loggedInSessions(sessions: StoredSession[]) {\n if (typeof window === \"undefined\") {\n loggedInSessions_ = sessions;\n return;\n }\n\n window.localStorage.setItem(\n LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,\n JSON.stringify(sessions),\n );\n }\n\n resolveSession(session: GraffitiSession): StoredSession {\n const resolvedSession = this.loggedInSessions.find(\n (s) => s.actor === session.actor,\n );\n if (!resolvedSession) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: { actor: session.actor },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n throw new Error(\"Not logged in\");\n }\n return resolvedSession;\n }\n}\nlet loggedInSessions_: StoredSession[] = [];\n\nconst LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY = \"graffiti-login-in-progress\";\nconst LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY = \"graffiti-logout-in-progress\";\nconst LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY = \"graffiti-sessions-logged-in\";\n\nconst GraffitiSessionSchema = object({\n actor: url(),\n});\n\nconst ServiceSessionSchema = object({\n token: string(),\n serviceEndpoint: url(),\n authorizationEndpoint: url(),\n});\n\nconst ServicesByAuthorizationSchema = array(\n tuple([\n url(), // Authorization endpoint\n array(\n object({\n endpoint: url(), // Service endpoint\n type: enum_([\"bucket\", \"personal-inbox\", \"shared-inbox\"]),\n }),\n ),\n ]),\n);\n\nconst InProgressSchema = extend(GraffitiSessionSchema, {\n tokens: array(string()),\n servicesByAuthorization: ServicesByAuthorizationSchema,\n});\n\nconst StoredSessionSchema = extend(InProgressSchema, {\n storageBucket: ServiceSessionSchema,\n personalInbox: ServiceSessionSchema,\n sharedInboxes: array(ServiceSessionSchema),\n});\n\ntype StoredSession = infer_<typeof StoredSessionSchema>;\n\nfunction serviceToEndpoint(service: Service): string {\n if (typeof service.serviceEndpoint === \"string\")\n return service.serviceEndpoint;\n throw new Error(`Service endpoint for ${service.id} is not a string`);\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["url", "session", "services"]
7
+ }
@@ -0,0 +1,19 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { Handles } from "./2-handles";
3
+ import { DecentralizedIdentifiers } from "../1-services/2-dids";
4
+ function handleTests(handle) {
5
+ describe("Handles", async () => {
6
+ const handles = new Handles({
7
+ dids: new DecentralizedIdentifiers()
8
+ });
9
+ test("handleToActor and actorToHandle", async () => {
10
+ const actor = await handles.handleToActor(handle);
11
+ const resolvedHandle = await handles.actorToHandle(actor);
12
+ expect(resolvedHandle).toBe(handle);
13
+ });
14
+ });
15
+ }
16
+ export {
17
+ handleTests
18
+ };
19
+ //# sourceMappingURL=2-handles-tests.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/3-protocol/2-handles-tests.ts"],
4
+ "sourcesContent": ["import { describe, expect, test } from \"vitest\";\nimport { Handles } from \"./2-handles\";\nimport { DecentralizedIdentifiers } from \"../1-services/2-dids\";\n\nexport function handleTests(handle: string) {\n describe(\"Handles\", async () => {\n const handles = new Handles({\n dids: new DecentralizedIdentifiers(),\n });\n\n test(\"handleToActor and actorToHandle\", async () => {\n const actor = await handles.handleToActor(handle);\n const resolvedHandle = await handles.actorToHandle(actor);\n expect(resolvedHandle).toBe(handle);\n });\n });\n}\n"],
5
+ "mappings": "AAAA,SAAS,UAAU,QAAQ,YAAY;AACvC,SAAS,eAAe;AACxB,SAAS,gCAAgC;AAElC,SAAS,YAAY,QAAgB;AAC1C,WAAS,WAAW,YAAY;AAC9B,UAAM,UAAU,IAAI,QAAQ;AAAA,MAC1B,MAAM,IAAI,yBAAyB;AAAA,IACrC,CAAC;AAED,SAAK,mCAAmC,YAAY;AAClD,YAAM,QAAQ,MAAM,QAAQ,cAAc,MAAM;AAChD,YAAM,iBAAiB,MAAM,QAAQ,cAAc,KAAK;AACxD,aAAO,cAAc,EAAE,KAAK,MAAM;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,45 @@
1
+ import { GraffitiErrorNotFound } from "@graffiti-garden/api";
2
+ import { DecentralizedIdentifiers } from "../1-services/2-dids";
3
+ const HANDLE_DID_PREFIX = "did:web:";
4
+ class Handles {
5
+ constructor(services) {
6
+ this.services = services;
7
+ }
8
+ actorToHandle = async (actor) => {
9
+ const actorDocument = await this.services.dids.resolve(actor);
10
+ const handleDid = actorDocument.alsoKnownAs?.at(0);
11
+ if (!handleDid) {
12
+ throw new GraffitiErrorNotFound(
13
+ `Handle for actor DID ${actor} not found`
14
+ );
15
+ }
16
+ if (!handleDid.startsWith(HANDLE_DID_PREFIX)) {
17
+ throw new Error(`Handle DID ${handleDid} is not a valid handle`);
18
+ }
19
+ const handle = handleDid.slice(HANDLE_DID_PREFIX.length);
20
+ const handleDocument = await this.services.dids.resolve(handleDid);
21
+ if (!handleDocument.alsoKnownAs || !handleDocument.alsoKnownAs.includes(actor)) {
22
+ throw new Error(`Handle ${handle} does not match actor ${actor}`);
23
+ }
24
+ return handle;
25
+ };
26
+ handleToActor = async (handle) => {
27
+ const handleDid = `${HANDLE_DID_PREFIX}${handle}`;
28
+ const handleDocument = await this.services.dids.resolve(handleDid);
29
+ const actor = handleDocument.alsoKnownAs?.at(0);
30
+ if (!actor) {
31
+ throw new GraffitiErrorNotFound(
32
+ `Actor for handle DID ${handleDid} not found`
33
+ );
34
+ }
35
+ const actorDocument = await this.services.dids.resolve(actor);
36
+ if (!actorDocument.alsoKnownAs || !actorDocument.alsoKnownAs.includes(handleDid)) {
37
+ throw new Error(`Actor ${actor} does not match handle ${handle}`);
38
+ }
39
+ return actor;
40
+ };
41
+ }
42
+ export {
43
+ Handles
44
+ };
45
+ //# sourceMappingURL=2-handles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/3-protocol/2-handles.ts"],
4
+ "sourcesContent": ["import type { Graffiti } from \"@graffiti-garden/api\";\nimport { GraffitiErrorNotFound } from \"@graffiti-garden/api\";\nimport { DecentralizedIdentifiers } from \"../1-services/2-dids\";\n\n// Handles used a fixed method\nconst HANDLE_DID_PREFIX = \"did:web:\";\n\nexport class Handles {\n constructor(\n protected readonly services: { dids: DecentralizedIdentifiers },\n ) {}\n\n actorToHandle: Graffiti[\"actorToHandle\"] = async (actor) => {\n const actorDocument = await this.services.dids.resolve(actor);\n\n const handleDid = actorDocument.alsoKnownAs?.at(0);\n if (!handleDid) {\n throw new GraffitiErrorNotFound(\n `Handle for actor DID ${actor} not found`,\n );\n }\n if (!handleDid.startsWith(HANDLE_DID_PREFIX)) {\n throw new Error(`Handle DID ${handleDid} is not a valid handle`);\n }\n\n const handle = handleDid.slice(HANDLE_DID_PREFIX.length);\n\n const handleDocument = await this.services.dids.resolve(handleDid);\n if (\n !handleDocument.alsoKnownAs ||\n !handleDocument.alsoKnownAs.includes(actor)\n ) {\n throw new Error(`Handle ${handle} does not match actor ${actor}`);\n }\n\n return handle;\n };\n\n handleToActor: Graffiti[\"handleToActor\"] = async (handle) => {\n const handleDid = `${HANDLE_DID_PREFIX}${handle}`;\n const handleDocument = await this.services.dids.resolve(handleDid);\n\n const actor = handleDocument.alsoKnownAs?.at(0);\n if (!actor) {\n throw new GraffitiErrorNotFound(\n `Actor for handle DID ${handleDid} not found`,\n );\n }\n\n const actorDocument = await this.services.dids.resolve(actor);\n if (\n !actorDocument.alsoKnownAs ||\n !actorDocument.alsoKnownAs.includes(handleDid)\n ) {\n throw new Error(`Actor ${actor} does not match handle ${handle}`);\n }\n\n return actor;\n };\n}\n"],
5
+ "mappings": "AACA,SAAS,6BAA6B;AACtC,SAAS,gCAAgC;AAGzC,MAAM,oBAAoB;AAEnB,MAAM,QAAQ;AAAA,EACnB,YACqB,UACnB;AADmB;AAAA,EAClB;AAAA,EAEH,gBAA2C,OAAO,UAAU;AAC1D,UAAM,gBAAgB,MAAM,KAAK,SAAS,KAAK,QAAQ,KAAK;AAE5D,UAAM,YAAY,cAAc,aAAa,GAAG,CAAC;AACjD,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,CAAC,UAAU,WAAW,iBAAiB,GAAG;AAC5C,YAAM,IAAI,MAAM,cAAc,SAAS,wBAAwB;AAAA,IACjE;AAEA,UAAM,SAAS,UAAU,MAAM,kBAAkB,MAAM;AAEvD,UAAM,iBAAiB,MAAM,KAAK,SAAS,KAAK,QAAQ,SAAS;AACjE,QACE,CAAC,eAAe,eAChB,CAAC,eAAe,YAAY,SAAS,KAAK,GAC1C;AACA,YAAM,IAAI,MAAM,UAAU,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClE;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,gBAA2C,OAAO,WAAW;AAC3D,UAAM,YAAY,GAAG,iBAAiB,GAAG,MAAM;AAC/C,UAAM,iBAAiB,MAAM,KAAK,SAAS,KAAK,QAAQ,SAAS;AAEjE,UAAM,QAAQ,eAAe,aAAa,GAAG,CAAC;AAC9C,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,wBAAwB,SAAS;AAAA,MACnC;AAAA,IACF;AAEA,UAAM,gBAAgB,MAAM,KAAK,SAAS,KAAK,QAAQ,KAAK;AAC5D,QACE,CAAC,cAAc,eACf,CAAC,cAAc,YAAY,SAAS,SAAS,GAC7C;AACA,YAAM,IAAI,MAAM,SAAS,KAAK,0BAA0B,MAAM,EAAE;AAAA,IAClE;AAEA,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }