@hraness/suite-accounts 0.9.20
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/LICENSE +21 -0
- package/README.md +496 -0
- package/compiler-foundation.css +1 -0
- package/dist/auth-client.js +16 -0
- package/dist/auth-proxy.js +757 -0
- package/dist/bearer-verifier.js +1119 -0
- package/dist/browser-session.js +208 -0
- package/dist/client-configuration.js +470 -0
- package/dist/convex-browser-auth-browser.js +923 -0
- package/dist/convex-browser-auth-server.js +846 -0
- package/dist/convex-browser-auth.js +597 -0
- package/dist/entitlements.js +502 -0
- package/dist/identity/functions.js +44 -0
- package/dist/identity/index.js +1716 -0
- package/dist/identity/return-targets.js +33 -0
- package/dist/index.js +1133 -0
- package/dist/oidc-device-code.js +253 -0
- package/dist/oidc-rp.js +2259 -0
- package/dist/oidc-session-policy.js +5 -0
- package/dist/oidc-surface-server.js +2332 -0
- package/dist/profile-form.js +1934 -0
- package/dist/profile.js +17 -0
- package/dist/public-config.js +462 -0
- package/dist/react.js +137 -0
- package/dist/receipt-verifier.js +619 -0
- package/dist/registry.js +327 -0
- package/dist/stylex-manifest.json +1 -0
- package/dist/stylex.css +435 -0
- package/dist/urls.js +391 -0
- package/package.json +263 -0
- package/portfolio-inventory.json +37 -0
- package/src/auth-client.ts +37 -0
- package/src/auth-proxy.ts +473 -0
- package/src/bearer-verifier.ts +478 -0
- package/src/browser-session.ts +260 -0
- package/src/client-configuration.ts +172 -0
- package/src/convex-browser-auth-browser.ts +238 -0
- package/src/convex-browser-auth-server.ts +492 -0
- package/src/convex-browser-auth.ts +208 -0
- package/src/convex-url.ts +74 -0
- package/src/entitlements.ts +317 -0
- package/src/identity/catalog.ts +227 -0
- package/src/identity/consumers.ts +53 -0
- package/src/identity/functions.ts +70 -0
- package/src/identity/identifiers.ts +82 -0
- package/src/identity/index.ts +10 -0
- package/src/identity/links.ts +280 -0
- package/src/identity/membership.ts +156 -0
- package/src/identity/principals.ts +301 -0
- package/src/identity/profiles-v2.ts +330 -0
- package/src/identity/profiles.ts +524 -0
- package/src/identity/return-targets.ts +20 -0
- package/src/identity/usernames.ts +101 -0
- package/src/identity/views.ts +289 -0
- package/src/immutable.ts +25 -0
- package/src/index.ts +7 -0
- package/src/oidc-continuation.ts +59 -0
- package/src/oidc-device-code.ts +401 -0
- package/src/oidc-rp.ts +2217 -0
- package/src/oidc-session-policy.ts +6 -0
- package/src/oidc-surface-server.ts +186 -0
- package/src/profile-form.css +1 -0
- package/src/profile-form.stylex.ts +226 -0
- package/src/profile-form.tsx +314 -0
- package/src/profile.ts +33 -0
- package/src/public-config.ts +294 -0
- package/src/public-profile-form-result.ts +82 -0
- package/src/public-profile-form.tsx +319 -0
- package/src/public-profile-links.ts +100 -0
- package/src/react.tsx +196 -0
- package/src/receipt-verifier.ts +273 -0
- package/src/registry.ts +600 -0
- package/src/urls.ts +234 -0
|
@@ -0,0 +1,1934 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/identity/consumers.ts
|
|
3
|
+
import { err, ok } from "@hraness/result";
|
|
4
|
+
|
|
5
|
+
// src/immutable.ts
|
|
6
|
+
function deepFreeze(value) {
|
|
7
|
+
const visited = new WeakSet;
|
|
8
|
+
function freezeOwned(current) {
|
|
9
|
+
if (current === null || typeof current !== "object")
|
|
10
|
+
return;
|
|
11
|
+
if (visited.has(current))
|
|
12
|
+
return;
|
|
13
|
+
visited.add(current);
|
|
14
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
15
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
16
|
+
if (descriptor !== undefined && "value" in descriptor) {
|
|
17
|
+
freezeOwned(descriptor.value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
Object.freeze(current);
|
|
21
|
+
}
|
|
22
|
+
freezeOwned(value);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/identity/consumers.ts
|
|
27
|
+
var SUITE_CONSUMER_IDS = deepFreeze([
|
|
28
|
+
"accounts",
|
|
29
|
+
"act60",
|
|
30
|
+
"elders",
|
|
31
|
+
"soundfish",
|
|
32
|
+
"oh-computer",
|
|
33
|
+
"draw-money",
|
|
34
|
+
"oprte",
|
|
35
|
+
"sponge"
|
|
36
|
+
]);
|
|
37
|
+
var LEGACY_SUITE_CONSUMER_IDS = deepFreeze([
|
|
38
|
+
"kitchen"
|
|
39
|
+
]);
|
|
40
|
+
function parseSuiteConsumerId(value) {
|
|
41
|
+
switch (value) {
|
|
42
|
+
case "accounts":
|
|
43
|
+
case "act60":
|
|
44
|
+
case "elders":
|
|
45
|
+
case "soundfish":
|
|
46
|
+
case "oh-computer":
|
|
47
|
+
case "draw-money":
|
|
48
|
+
case "oprte":
|
|
49
|
+
case "sponge":
|
|
50
|
+
return ok(value);
|
|
51
|
+
case "kitchen":
|
|
52
|
+
return ok("oprte");
|
|
53
|
+
default:
|
|
54
|
+
return err("invalid-consumer");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/registry.ts
|
|
59
|
+
var SUITE_ACCOUNTS_REMOTE_ENVIRONMENTS = deepFreeze([
|
|
60
|
+
"production"
|
|
61
|
+
]);
|
|
62
|
+
var accountsCookies = deepFreeze({
|
|
63
|
+
chunked: ["account_data", "session_data"],
|
|
64
|
+
names: [
|
|
65
|
+
"account_data",
|
|
66
|
+
"convex_jwt",
|
|
67
|
+
"dont_remember",
|
|
68
|
+
"session_data",
|
|
69
|
+
"session_token"
|
|
70
|
+
]
|
|
71
|
+
});
|
|
72
|
+
var consumerCookies = deepFreeze({
|
|
73
|
+
chunked: ["session_data"],
|
|
74
|
+
names: ["dont_remember", "session_data", "session_token"]
|
|
75
|
+
});
|
|
76
|
+
var SUITE_ACCOUNTS_DEPLOYMENTS = deepFreeze({
|
|
77
|
+
production: {
|
|
78
|
+
accountsOrigin: "https://account.hraness.com",
|
|
79
|
+
convexSiteUrl: "https://qualified-marmot-22.convex.site",
|
|
80
|
+
convexUrl: "https://qualified-marmot-22.convex.cloud"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
function unsupported(siteUrl) {
|
|
84
|
+
return { billingReturn: { kind: "unsupported" }, siteUrl };
|
|
85
|
+
}
|
|
86
|
+
function oidcSite(id, displayName, productionSiteUrl) {
|
|
87
|
+
return {
|
|
88
|
+
auth: { basePath: "/api/suite-auth", kind: "oidc-rp" },
|
|
89
|
+
displayName,
|
|
90
|
+
environments: {
|
|
91
|
+
production: unsupported(productionSiteUrl)
|
|
92
|
+
},
|
|
93
|
+
id
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var SUITE_ACCOUNTS_REGISTERED_CONSUMER_IDS = deepFreeze([
|
|
97
|
+
"accounts",
|
|
98
|
+
"act60",
|
|
99
|
+
"soundfish",
|
|
100
|
+
"oh-computer",
|
|
101
|
+
"draw-money",
|
|
102
|
+
"sponge"
|
|
103
|
+
]);
|
|
104
|
+
var SUITE_ACCOUNTS_CONSUMERS = deepFreeze({
|
|
105
|
+
accounts: {
|
|
106
|
+
auth: {
|
|
107
|
+
basePath: "/api/auth",
|
|
108
|
+
cookies: accountsCookies,
|
|
109
|
+
kind: "authority"
|
|
110
|
+
},
|
|
111
|
+
displayName: "Accounts",
|
|
112
|
+
environments: {
|
|
113
|
+
production: {
|
|
114
|
+
billingReturn: { kind: "supported", path: "/account" },
|
|
115
|
+
siteUrl: "https://account.hraness.com"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
id: "accounts"
|
|
119
|
+
},
|
|
120
|
+
act60: oidcSite("act60", "ACT60", "https://act60.me"),
|
|
121
|
+
soundfish: {
|
|
122
|
+
auth: { basePath: "/api/suite-auth", kind: "oidc-rp" },
|
|
123
|
+
displayName: "Soundfish",
|
|
124
|
+
environments: {
|
|
125
|
+
production: unsupported("https://sound.fish")
|
|
126
|
+
},
|
|
127
|
+
id: "soundfish"
|
|
128
|
+
},
|
|
129
|
+
"oh-computer": oidcSite("oh-computer", "Oh", "https://oh.computer"),
|
|
130
|
+
"draw-money": {
|
|
131
|
+
auth: {
|
|
132
|
+
basePath: "/api/auth",
|
|
133
|
+
cookies: consumerCookies,
|
|
134
|
+
kind: "proxy"
|
|
135
|
+
},
|
|
136
|
+
displayName: "Draw Money",
|
|
137
|
+
environments: {
|
|
138
|
+
production: unsupported("https://draw.money")
|
|
139
|
+
},
|
|
140
|
+
id: "draw-money"
|
|
141
|
+
},
|
|
142
|
+
sponge: oidcSite("sponge", "Sponge", "https://spongesearch.com")
|
|
143
|
+
});
|
|
144
|
+
var SUITE_ACCOUNTS_CURRENT_ORIGIN_OVERRIDES = deepFreeze({
|
|
145
|
+
sponge: {
|
|
146
|
+
production: unsupported("https://sponge.computer")
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
var SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS = deepFreeze([
|
|
150
|
+
"accounts",
|
|
151
|
+
"act60",
|
|
152
|
+
"soundfish",
|
|
153
|
+
"oh-computer",
|
|
154
|
+
"sponge",
|
|
155
|
+
"peopleblade",
|
|
156
|
+
"aicharts",
|
|
157
|
+
"hraness",
|
|
158
|
+
"soulscrape",
|
|
159
|
+
"platonik",
|
|
160
|
+
"ghostget",
|
|
161
|
+
"alt",
|
|
162
|
+
"algal"
|
|
163
|
+
]);
|
|
164
|
+
function currentOidcSite(id, displayName, productionSiteUrl) {
|
|
165
|
+
return {
|
|
166
|
+
auth: { basePath: "/api/suite-auth", kind: "oidc-rp" },
|
|
167
|
+
displayName,
|
|
168
|
+
environments: {
|
|
169
|
+
production: unsupported(productionSiteUrl)
|
|
170
|
+
},
|
|
171
|
+
id
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
var SUITE_ACCOUNTS_CURRENT_CONSUMERS = deepFreeze({
|
|
175
|
+
accounts: SUITE_ACCOUNTS_CONSUMERS.accounts,
|
|
176
|
+
act60: SUITE_ACCOUNTS_CONSUMERS.act60,
|
|
177
|
+
soundfish: SUITE_ACCOUNTS_CONSUMERS.soundfish,
|
|
178
|
+
"oh-computer": SUITE_ACCOUNTS_CONSUMERS["oh-computer"],
|
|
179
|
+
sponge: currentOidcSite("sponge", "Sponge", SUITE_ACCOUNTS_CURRENT_ORIGIN_OVERRIDES.sponge.production.siteUrl),
|
|
180
|
+
peopleblade: currentOidcSite("peopleblade", "PeopleBlade", "https://peopleblade.com"),
|
|
181
|
+
aicharts: currentOidcSite("aicharts", "AI Charts", "https://aicharts.io"),
|
|
182
|
+
hraness: currentOidcSite("hraness", "Hraness", "https://hraness.com"),
|
|
183
|
+
soulscrape: currentOidcSite("soulscrape", "Soulscrape", "https://soulscrape.com"),
|
|
184
|
+
platonik: currentOidcSite("platonik", "Platonik", "https://platonik.space"),
|
|
185
|
+
ghostget: currentOidcSite("ghostget", "Ghostget", "https://ghostget.com"),
|
|
186
|
+
alt: currentOidcSite("alt", "Alt", "https://alt.dog"),
|
|
187
|
+
algal: currentOidcSite("algal", "Algal", "https://algal.cloud")
|
|
188
|
+
});
|
|
189
|
+
var SUITE_ACCOUNTS_ACTIVE_CONSUMER_IDS = deepFreeze([
|
|
190
|
+
"accounts",
|
|
191
|
+
"act60",
|
|
192
|
+
"soundfish",
|
|
193
|
+
"oh-computer",
|
|
194
|
+
"sponge"
|
|
195
|
+
]);
|
|
196
|
+
var SUITE_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS = deepFreeze(SUITE_ACCOUNTS_REGISTERED_CONSUMER_IDS.filter((consumer) => SUITE_ACCOUNTS_CONSUMERS[consumer].auth.kind === "oidc-rp"));
|
|
197
|
+
function suiteAccountsConsumerRequiresEmailOtp(consumer) {
|
|
198
|
+
return SUITE_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS.includes(consumer);
|
|
199
|
+
}
|
|
200
|
+
var SUITE_ACCOUNTS_LINKED_OIDC_CONSUMER_IDS = deepFreeze([
|
|
201
|
+
"soundfish"
|
|
202
|
+
]);
|
|
203
|
+
var SUITE_ACCOUNTS_CURRENT_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS = deepFreeze(SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS.filter((consumer) => SUITE_ACCOUNTS_CURRENT_CONSUMERS[consumer].auth.kind === "oidc-rp"));
|
|
204
|
+
var SUITE_ACCOUNTS_CURRENT_LINKED_OIDC_CONSUMER_IDS = deepFreeze([
|
|
205
|
+
"soundfish",
|
|
206
|
+
"peopleblade",
|
|
207
|
+
"ghostget"
|
|
208
|
+
]);
|
|
209
|
+
var SUITE_ACCOUNTS_CURRENT_BROWSER_DEVICE_CODE_CONSUMER_IDS = deepFreeze([
|
|
210
|
+
"act60",
|
|
211
|
+
"soundfish",
|
|
212
|
+
"oh-computer",
|
|
213
|
+
"sponge",
|
|
214
|
+
"peopleblade",
|
|
215
|
+
"aicharts",
|
|
216
|
+
"hraness",
|
|
217
|
+
"soulscrape",
|
|
218
|
+
"platonik",
|
|
219
|
+
"ghostget",
|
|
220
|
+
"alt"
|
|
221
|
+
]);
|
|
222
|
+
var SUITE_ACCOUNTS_CURRENT_DEVICE_CLIENT_IDS = deepFreeze([
|
|
223
|
+
"algal-cli"
|
|
224
|
+
]);
|
|
225
|
+
var SUITE_ACCOUNTS_CURRENT_DEVICE_CLIENTS = deepFreeze({
|
|
226
|
+
"algal-cli": {
|
|
227
|
+
consumer: "algal",
|
|
228
|
+
displayName: "Algal CLI",
|
|
229
|
+
environments: ["production"],
|
|
230
|
+
id: "algal-cli"
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
function isSuiteAccountsConsumerId(value) {
|
|
234
|
+
return typeof value === "string" && SUITE_CONSUMER_IDS.includes(value);
|
|
235
|
+
}
|
|
236
|
+
function isSuiteAccountsRegisteredConsumerId(value) {
|
|
237
|
+
return typeof value === "string" && SUITE_ACCOUNTS_REGISTERED_CONSUMER_IDS.includes(value);
|
|
238
|
+
}
|
|
239
|
+
function isSuiteAccountsOidcConsumerId(value) {
|
|
240
|
+
return isSuiteAccountsRegisteredConsumerId(value) && SUITE_ACCOUNTS_CONSUMERS[value].auth.kind === "oidc-rp";
|
|
241
|
+
}
|
|
242
|
+
function isSuiteAccountsLinkedOidcConsumerId(value) {
|
|
243
|
+
return SUITE_ACCOUNTS_LINKED_OIDC_CONSUMER_IDS.includes(value);
|
|
244
|
+
}
|
|
245
|
+
function isSuiteAccountsOAuthConsumerId(value) {
|
|
246
|
+
return isSuiteAccountsRegisteredConsumerId(value) && SUITE_ACCOUNTS_CONSUMERS[value].auth.kind === "oidc-rp";
|
|
247
|
+
}
|
|
248
|
+
function isSuiteAccountsCurrentConsumerId(value) {
|
|
249
|
+
return typeof value === "string" && SUITE_ACCOUNTS_CURRENT_CONSUMER_IDS.includes(value);
|
|
250
|
+
}
|
|
251
|
+
function isSuiteAccountsCurrentOidcConsumerId(value) {
|
|
252
|
+
return getSuiteAccountsCurrentConsumer(value).auth.kind === "oidc-rp";
|
|
253
|
+
}
|
|
254
|
+
function isSuiteAccountsCurrentLinkedOidcConsumerId(value) {
|
|
255
|
+
return SUITE_ACCOUNTS_CURRENT_LINKED_OIDC_CONSUMER_IDS.includes(value);
|
|
256
|
+
}
|
|
257
|
+
function isSuiteAccountsCurrentOAuthConsumerId(value) {
|
|
258
|
+
return getSuiteAccountsCurrentConsumer(value).auth.kind === "oidc-rp";
|
|
259
|
+
}
|
|
260
|
+
function isSuiteAccountsCurrentBrowserDeviceCodeConsumerId(value) {
|
|
261
|
+
return typeof value === "string" && SUITE_ACCOUNTS_CURRENT_BROWSER_DEVICE_CODE_CONSUMER_IDS.includes(value);
|
|
262
|
+
}
|
|
263
|
+
function isSuiteAccountsCurrentDeviceClientId(value) {
|
|
264
|
+
return typeof value === "string" && SUITE_ACCOUNTS_CURRENT_DEVICE_CLIENT_IDS.includes(value);
|
|
265
|
+
}
|
|
266
|
+
function suiteAccountsCurrentConsumerRequiresEmailOtp(consumer) {
|
|
267
|
+
return SUITE_ACCOUNTS_CURRENT_EMAIL_OTP_REQUIRED_OIDC_CONSUMER_IDS.includes(consumer);
|
|
268
|
+
}
|
|
269
|
+
function getSuiteAccountsConsumer(consumer) {
|
|
270
|
+
return SUITE_ACCOUNTS_CONSUMERS[consumer];
|
|
271
|
+
}
|
|
272
|
+
function getSuiteAccountsConsumerEnvironment(consumer, environment) {
|
|
273
|
+
if (!isSuiteAccountsRegisteredConsumerId(consumer))
|
|
274
|
+
return null;
|
|
275
|
+
const registration = getSuiteAccountsConsumer(consumer);
|
|
276
|
+
return registration.environments[environment] ?? null;
|
|
277
|
+
}
|
|
278
|
+
function getSuiteAccountsCurrentConsumer(consumer) {
|
|
279
|
+
return SUITE_ACCOUNTS_CURRENT_CONSUMERS[consumer];
|
|
280
|
+
}
|
|
281
|
+
function getSuiteAccountsCurrentConsumerEnvironment(consumer, environment) {
|
|
282
|
+
if (!isSuiteAccountsCurrentConsumerId(consumer))
|
|
283
|
+
return null;
|
|
284
|
+
return getSuiteAccountsCurrentConsumer(consumer).environments[environment] ?? null;
|
|
285
|
+
}
|
|
286
|
+
function isSuiteAccountsActiveConsumerId(value) {
|
|
287
|
+
return SUITE_ACCOUNTS_ACTIVE_CONSUMER_IDS.includes(value);
|
|
288
|
+
}
|
|
289
|
+
function getSuiteAccountsDeployment(environment) {
|
|
290
|
+
return SUITE_ACCOUNTS_DEPLOYMENTS[environment];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/public-profile-form.tsx
|
|
294
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
295
|
+
|
|
296
|
+
// src/identity/profiles.ts
|
|
297
|
+
import { err as err2, isRecord, ok as ok2 } from "@hraness/result";
|
|
298
|
+
var SUITE_PROFILE_NAME_MAX_LENGTH = 120;
|
|
299
|
+
var SUITE_PROFILE_BIO_MAX_LENGTH = 1000;
|
|
300
|
+
var SUITE_PROFILE_URL_MAX_LENGTH = 2048;
|
|
301
|
+
var SUITE_COMMUNITY_APPLICATION_STATUSES = deepFreeze([
|
|
302
|
+
"submitted",
|
|
303
|
+
"accepted",
|
|
304
|
+
"declined",
|
|
305
|
+
"withdrawn"
|
|
306
|
+
]);
|
|
307
|
+
var PROFILE_LINK_KEYS = [
|
|
308
|
+
"bluesky",
|
|
309
|
+
"instagram",
|
|
310
|
+
"linkedin",
|
|
311
|
+
"telegram",
|
|
312
|
+
"website",
|
|
313
|
+
"x"
|
|
314
|
+
];
|
|
315
|
+
function exactKeys(value, expected) {
|
|
316
|
+
const actual = Object.keys(value).sort();
|
|
317
|
+
const sortedExpected = [...expected].sort();
|
|
318
|
+
return actual.length === sortedExpected.length && actual.every((key, index) => key === sortedExpected[index]);
|
|
319
|
+
}
|
|
320
|
+
function hasInvalidSingleLineControl(value) {
|
|
321
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
322
|
+
const code = value.charCodeAt(index);
|
|
323
|
+
if (code <= 31 || code >= 127 && code <= 159)
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
function hasInvalidBioControl(value) {
|
|
329
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
330
|
+
const code = value.charCodeAt(index);
|
|
331
|
+
if (code <= 31 && code !== 10 || code >= 127 && code <= 159) {
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
function normalizedName(value) {
|
|
338
|
+
if (typeof value !== "string") {
|
|
339
|
+
return err2({ field: "name", reason: "required" });
|
|
340
|
+
}
|
|
341
|
+
if (hasInvalidSingleLineControl(value)) {
|
|
342
|
+
return err2({ field: "name", reason: "invalid" });
|
|
343
|
+
}
|
|
344
|
+
const name = value.trim().replace(/\s+/gu, " ");
|
|
345
|
+
if (name.length === 0) {
|
|
346
|
+
return err2({ field: "name", reason: "required" });
|
|
347
|
+
}
|
|
348
|
+
return name.length <= SUITE_PROFILE_NAME_MAX_LENGTH ? ok2(name) : err2({ field: "name", reason: "too_long" });
|
|
349
|
+
}
|
|
350
|
+
function normalizedProfileViewName(value) {
|
|
351
|
+
if (typeof value !== "string") {
|
|
352
|
+
return err2({ field: "name", reason: "invalid" });
|
|
353
|
+
}
|
|
354
|
+
if (hasInvalidSingleLineControl(value)) {
|
|
355
|
+
return err2({ field: "name", reason: "invalid" });
|
|
356
|
+
}
|
|
357
|
+
const name = value.trim().replace(/\s+/gu, " ");
|
|
358
|
+
return name.length <= SUITE_PROFILE_NAME_MAX_LENGTH ? ok2(name) : err2({ field: "name", reason: "too_long" });
|
|
359
|
+
}
|
|
360
|
+
function normalizedBio(value) {
|
|
361
|
+
if (typeof value !== "string") {
|
|
362
|
+
return err2({ field: "bio", reason: "invalid" });
|
|
363
|
+
}
|
|
364
|
+
const normalizedNewlines = value.replaceAll(`\r
|
|
365
|
+
`, `
|
|
366
|
+
`).replaceAll("\r", `
|
|
367
|
+
`);
|
|
368
|
+
if (hasInvalidBioControl(normalizedNewlines)) {
|
|
369
|
+
return err2({ field: "bio", reason: "invalid" });
|
|
370
|
+
}
|
|
371
|
+
const bio = normalizedNewlines.trim();
|
|
372
|
+
return bio.length <= SUITE_PROFILE_BIO_MAX_LENGTH ? ok2(bio) : err2({ field: "bio", reason: "too_long" });
|
|
373
|
+
}
|
|
374
|
+
function parsedHttpsUrl(value, options) {
|
|
375
|
+
if (value.length === 0 || value.length > SUITE_PROFILE_URL_MAX_LENGTH || hasInvalidSingleLineControl(value) || value.trim() !== value) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
let url;
|
|
379
|
+
try {
|
|
380
|
+
url = new URL(value);
|
|
381
|
+
} catch {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.hash !== "" || url.port !== "" || !options.allowQuery && url.search !== "" || options.hosts !== undefined && !options.hosts.has(url.hostname.toLowerCase())) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
return url;
|
|
388
|
+
}
|
|
389
|
+
function simpleHandle(value, pattern) {
|
|
390
|
+
const withoutAt = value.startsWith("@") ? value.slice(1) : value;
|
|
391
|
+
return pattern.test(withoutAt) ? withoutAt.toLowerCase() : null;
|
|
392
|
+
}
|
|
393
|
+
function exactPathSegments(url) {
|
|
394
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
395
|
+
return url.pathname === `/${segments.join("/")}` || url.pathname === `/${segments.join("/")}/` ? segments : null;
|
|
396
|
+
}
|
|
397
|
+
var X_HOSTS = new Set(["x.com", "www.x.com", "twitter.com", "www.twitter.com"]);
|
|
398
|
+
var INSTAGRAM_HOSTS = new Set(["instagram.com", "www.instagram.com"]);
|
|
399
|
+
var TELEGRAM_HOSTS = new Set([
|
|
400
|
+
"t.me",
|
|
401
|
+
"www.t.me",
|
|
402
|
+
"telegram.me",
|
|
403
|
+
"www.telegram.me"
|
|
404
|
+
]);
|
|
405
|
+
var BLUESKY_HOSTS = new Set(["bsky.app", "www.bsky.app"]);
|
|
406
|
+
var LINKEDIN_HOSTS = new Set(["linkedin.com", "www.linkedin.com"]);
|
|
407
|
+
function normalizedX(value) {
|
|
408
|
+
const enteredHandle = simpleHandle(value, /^[A-Za-z0-9_]{1,15}$/u);
|
|
409
|
+
if (enteredHandle !== null)
|
|
410
|
+
return `https://x.com/${enteredHandle}`;
|
|
411
|
+
const url = parsedHttpsUrl(value, { allowQuery: false, hosts: X_HOSTS });
|
|
412
|
+
const segments = url === null ? null : exactPathSegments(url);
|
|
413
|
+
const handle = segments?.length === 1 ? simpleHandle(segments[0], /^[A-Za-z0-9_]{1,15}$/u) : null;
|
|
414
|
+
return handle === null ? null : `https://x.com/${handle}`;
|
|
415
|
+
}
|
|
416
|
+
function normalizedInstagram(value) {
|
|
417
|
+
const pattern = /^(?!.*\.\.)[A-Za-z0-9](?:[A-Za-z0-9._]{0,28}[A-Za-z0-9_])?$/u;
|
|
418
|
+
const enteredHandle = simpleHandle(value, pattern);
|
|
419
|
+
if (enteredHandle !== null) {
|
|
420
|
+
return `https://www.instagram.com/${enteredHandle}`;
|
|
421
|
+
}
|
|
422
|
+
const url = parsedHttpsUrl(value, {
|
|
423
|
+
allowQuery: false,
|
|
424
|
+
hosts: INSTAGRAM_HOSTS
|
|
425
|
+
});
|
|
426
|
+
const segments = url === null ? null : exactPathSegments(url);
|
|
427
|
+
const handle = segments?.length === 1 ? simpleHandle(segments[0], pattern) : null;
|
|
428
|
+
return handle === null ? null : `https://www.instagram.com/${handle}`;
|
|
429
|
+
}
|
|
430
|
+
function normalizedTelegram(value) {
|
|
431
|
+
const enteredHandle = simpleHandle(value, /^[A-Za-z][A-Za-z0-9_]{4,31}$/u);
|
|
432
|
+
if (enteredHandle !== null)
|
|
433
|
+
return `https://t.me/${enteredHandle}`;
|
|
434
|
+
const url = parsedHttpsUrl(value, {
|
|
435
|
+
allowQuery: false,
|
|
436
|
+
hosts: TELEGRAM_HOSTS
|
|
437
|
+
});
|
|
438
|
+
const segments = url === null ? null : exactPathSegments(url);
|
|
439
|
+
const handle = segments?.length === 1 ? simpleHandle(segments[0], /^[A-Za-z][A-Za-z0-9_]{4,31}$/u) : null;
|
|
440
|
+
return handle === null ? null : `https://t.me/${handle}`;
|
|
441
|
+
}
|
|
442
|
+
function validBlueskyHandle(value) {
|
|
443
|
+
if (value.length < 3 || value.length > 253 || !value.includes(".") || value.startsWith(".") || value.endsWith(".")) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
const labels = value.split(".");
|
|
447
|
+
return labels.every((label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label));
|
|
448
|
+
}
|
|
449
|
+
function normalizedBluesky(value) {
|
|
450
|
+
const withoutAt = value.startsWith("@") ? value.slice(1) : value;
|
|
451
|
+
const enteredHandle = withoutAt.toLowerCase();
|
|
452
|
+
if (validBlueskyHandle(enteredHandle)) {
|
|
453
|
+
return `https://bsky.app/profile/${enteredHandle}`;
|
|
454
|
+
}
|
|
455
|
+
const url = parsedHttpsUrl(value, {
|
|
456
|
+
allowQuery: false,
|
|
457
|
+
hosts: BLUESKY_HOSTS
|
|
458
|
+
});
|
|
459
|
+
const segments = url === null ? null : exactPathSegments(url);
|
|
460
|
+
const handle = segments?.length === 2 && segments[0] === "profile" ? segments[1].toLowerCase() : null;
|
|
461
|
+
return handle !== null && validBlueskyHandle(handle) ? `https://bsky.app/profile/${handle}` : null;
|
|
462
|
+
}
|
|
463
|
+
function normalizedLinkedIn(value) {
|
|
464
|
+
const url = parsedHttpsUrl(value, {
|
|
465
|
+
allowQuery: false,
|
|
466
|
+
hosts: LINKEDIN_HOSTS
|
|
467
|
+
});
|
|
468
|
+
const segments = url === null ? null : exactPathSegments(url);
|
|
469
|
+
if (segments?.length !== 2 || segments[0] !== "in" || !/^[A-Za-z0-9][A-Za-z0-9-]{1,99}$/u.test(segments[1])) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
return `https://www.linkedin.com/in/${segments[1].toLowerCase()}`;
|
|
473
|
+
}
|
|
474
|
+
function normalizedWebsite(value) {
|
|
475
|
+
const url = parsedHttpsUrl(value, { allowQuery: true });
|
|
476
|
+
return url === null ? null : url.href;
|
|
477
|
+
}
|
|
478
|
+
function normalizeSuiteProfileLink(key, value) {
|
|
479
|
+
if (value === null)
|
|
480
|
+
return ok2(null);
|
|
481
|
+
if (typeof value !== "string") {
|
|
482
|
+
return err2({ field: key, reason: "invalid" });
|
|
483
|
+
}
|
|
484
|
+
const trimmed = value.trim();
|
|
485
|
+
if (trimmed.length === 0)
|
|
486
|
+
return ok2(null);
|
|
487
|
+
const normalized = (() => {
|
|
488
|
+
switch (key) {
|
|
489
|
+
case "x":
|
|
490
|
+
return normalizedX(trimmed);
|
|
491
|
+
case "linkedin":
|
|
492
|
+
return normalizedLinkedIn(trimmed);
|
|
493
|
+
case "bluesky":
|
|
494
|
+
return normalizedBluesky(trimmed);
|
|
495
|
+
case "instagram":
|
|
496
|
+
return normalizedInstagram(trimmed);
|
|
497
|
+
case "telegram":
|
|
498
|
+
return normalizedTelegram(trimmed);
|
|
499
|
+
case "website":
|
|
500
|
+
return normalizedWebsite(trimmed);
|
|
501
|
+
}
|
|
502
|
+
})();
|
|
503
|
+
return normalized === null ? err2({ field: key, reason: "invalid" }) : ok2(normalized);
|
|
504
|
+
}
|
|
505
|
+
function parsedLinks(value, canonicalOnly) {
|
|
506
|
+
if (!isRecord(value) || !exactKeys(value, PROFILE_LINK_KEYS)) {
|
|
507
|
+
return err2({ field: "profile", reason: "invalid" });
|
|
508
|
+
}
|
|
509
|
+
const links = {
|
|
510
|
+
bluesky: null,
|
|
511
|
+
instagram: null,
|
|
512
|
+
linkedin: null,
|
|
513
|
+
telegram: null,
|
|
514
|
+
website: null,
|
|
515
|
+
x: null
|
|
516
|
+
};
|
|
517
|
+
for (const key of PROFILE_LINK_KEYS) {
|
|
518
|
+
const parsed = normalizeSuiteProfileLink(key, value[key]);
|
|
519
|
+
if (!parsed.ok)
|
|
520
|
+
return parsed;
|
|
521
|
+
if (canonicalOnly && parsed.value !== value[key]) {
|
|
522
|
+
return err2({ field: key, reason: "invalid" });
|
|
523
|
+
}
|
|
524
|
+
links[key] = parsed.value;
|
|
525
|
+
}
|
|
526
|
+
return ok2(links);
|
|
527
|
+
}
|
|
528
|
+
function nonnegativeInteger(value) {
|
|
529
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
530
|
+
}
|
|
531
|
+
function parsedEmail(value) {
|
|
532
|
+
return typeof value === "string" && value.length <= 320 && value.trim() === value && !hasInvalidSingleLineControl(value) && /^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(value) ? value : null;
|
|
533
|
+
}
|
|
534
|
+
function parseSuiteProfileUpdateRequest(value) {
|
|
535
|
+
if (!isRecord(value) || !exactKeys(value, ["bio", "expectedRevision", "links", "name"])) {
|
|
536
|
+
return err2({ field: "profile", reason: "invalid" });
|
|
537
|
+
}
|
|
538
|
+
const name = normalizedName(value["name"]);
|
|
539
|
+
if (!name.ok)
|
|
540
|
+
return name;
|
|
541
|
+
const bio = normalizedBio(value["bio"]);
|
|
542
|
+
if (!bio.ok)
|
|
543
|
+
return bio;
|
|
544
|
+
const links = parsedLinks(value["links"], false);
|
|
545
|
+
if (!links.ok)
|
|
546
|
+
return links;
|
|
547
|
+
const expectedRevision = nonnegativeInteger(value["expectedRevision"]);
|
|
548
|
+
if (expectedRevision === null) {
|
|
549
|
+
return err2({ field: "expectedRevision", reason: "invalid" });
|
|
550
|
+
}
|
|
551
|
+
return ok2({
|
|
552
|
+
bio: bio.value,
|
|
553
|
+
expectedRevision,
|
|
554
|
+
links: links.value,
|
|
555
|
+
name: name.value
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function parseSuiteProfileView(value) {
|
|
559
|
+
if (!isRecord(value) || !exactKeys(value, ["bio", "email", "links", "name", "revision"])) {
|
|
560
|
+
return err2({ field: "profile", reason: "invalid" });
|
|
561
|
+
}
|
|
562
|
+
const name = normalizedProfileViewName(value["name"]);
|
|
563
|
+
const bio = normalizedBio(value["bio"]);
|
|
564
|
+
const links = parsedLinks(value["links"], true);
|
|
565
|
+
const email = parsedEmail(value["email"]);
|
|
566
|
+
const revision = nonnegativeInteger(value["revision"]);
|
|
567
|
+
if (!name.ok)
|
|
568
|
+
return name;
|
|
569
|
+
if (!bio.ok)
|
|
570
|
+
return bio;
|
|
571
|
+
if (!links.ok)
|
|
572
|
+
return links;
|
|
573
|
+
if (email === null)
|
|
574
|
+
return err2({ field: "email", reason: "invalid" });
|
|
575
|
+
if (revision === null) {
|
|
576
|
+
return err2({ field: "expectedRevision", reason: "invalid" });
|
|
577
|
+
}
|
|
578
|
+
if (name.value !== value["name"] || bio.value !== value["bio"]) {
|
|
579
|
+
return err2({ field: "profile", reason: "invalid" });
|
|
580
|
+
}
|
|
581
|
+
return ok2({
|
|
582
|
+
bio: bio.value,
|
|
583
|
+
email,
|
|
584
|
+
links: links.value,
|
|
585
|
+
name: name.value,
|
|
586
|
+
revision
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function isApplicationStatus(value) {
|
|
590
|
+
return typeof value === "string" && SUITE_COMMUNITY_APPLICATION_STATUSES.includes(value);
|
|
591
|
+
}
|
|
592
|
+
function parsedApplication(value) {
|
|
593
|
+
if (value === null)
|
|
594
|
+
return ok2(null);
|
|
595
|
+
if (!isRecord(value) || !exactKeys(value, [
|
|
596
|
+
"community",
|
|
597
|
+
"status",
|
|
598
|
+
"submittedAtMs",
|
|
599
|
+
"updatedAtMs"
|
|
600
|
+
]) || value["community"] !== "oh-computer" || !isApplicationStatus(value["status"])) {
|
|
601
|
+
return err2({ field: "application", reason: "invalid" });
|
|
602
|
+
}
|
|
603
|
+
const submittedAtMs = nonnegativeInteger(value["submittedAtMs"]);
|
|
604
|
+
const updatedAtMs = nonnegativeInteger(value["updatedAtMs"]);
|
|
605
|
+
if (submittedAtMs === null || updatedAtMs === null || updatedAtMs < submittedAtMs) {
|
|
606
|
+
return err2({ field: "application", reason: "invalid" });
|
|
607
|
+
}
|
|
608
|
+
return ok2({
|
|
609
|
+
community: "oh-computer",
|
|
610
|
+
status: value["status"],
|
|
611
|
+
submittedAtMs,
|
|
612
|
+
updatedAtMs
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
function parseSuiteCommunityProfileView(value) {
|
|
616
|
+
if (!isRecord(value) || !exactKeys(value, ["application", "profile"])) {
|
|
617
|
+
return err2({ field: "profile", reason: "invalid" });
|
|
618
|
+
}
|
|
619
|
+
const application = parsedApplication(value["application"]);
|
|
620
|
+
if (!application.ok)
|
|
621
|
+
return application;
|
|
622
|
+
const profile = parseSuiteProfileView(value["profile"]);
|
|
623
|
+
return profile.ok ? ok2({ application: application.value, profile: profile.value }) : profile;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// src/identity/profiles-v2.ts
|
|
627
|
+
import { err as err5, ok as ok5 } from "@hraness/result";
|
|
628
|
+
|
|
629
|
+
// src/identity/identifiers.ts
|
|
630
|
+
import { err as err3, ok as ok3 } from "@hraness/result";
|
|
631
|
+
var suiteAccountIdPattern = /^acct_[0-9a-f]{32}$/u;
|
|
632
|
+
var suiteInvoiceRefPattern = /^invref_[0-9a-f]{32}$/u;
|
|
633
|
+
var uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
634
|
+
function parseSuiteAccountId(value) {
|
|
635
|
+
return typeof value === "string" && suiteAccountIdPattern.test(value) ? ok3(value) : err3("invalid-suite-account-id");
|
|
636
|
+
}
|
|
637
|
+
function parseSuiteInvoiceRef(value) {
|
|
638
|
+
return typeof value === "string" && suiteInvoiceRefPattern.test(value) ? ok3(value) : err3("invalid-suite-invoice-ref");
|
|
639
|
+
}
|
|
640
|
+
function generateSuiteAccountId(randomUuid = () => crypto.randomUUID()) {
|
|
641
|
+
const uuid = randomUuid();
|
|
642
|
+
if (!uuidPattern.test(uuid)) {
|
|
643
|
+
throw new TypeError("The suite account ID source did not return a UUID.");
|
|
644
|
+
}
|
|
645
|
+
const candidate = `acct_${uuid.replaceAll("-", "").toLowerCase()}`;
|
|
646
|
+
const parsed = parseSuiteAccountId(candidate);
|
|
647
|
+
if (!parsed.ok) {
|
|
648
|
+
throw new TypeError("The suite account ID source produced an invalid value.");
|
|
649
|
+
}
|
|
650
|
+
return parsed.value;
|
|
651
|
+
}
|
|
652
|
+
function generateSuiteInvoiceRef(randomUuid = () => crypto.randomUUID()) {
|
|
653
|
+
const uuid = randomUuid();
|
|
654
|
+
if (!uuidPattern.test(uuid)) {
|
|
655
|
+
throw new TypeError("The suite invoice reference source did not return a UUID.");
|
|
656
|
+
}
|
|
657
|
+
const candidate = `invref_${uuid.replaceAll("-", "").toLowerCase()}`;
|
|
658
|
+
const parsed = parseSuiteInvoiceRef(candidate);
|
|
659
|
+
if (!parsed.ok) {
|
|
660
|
+
throw new TypeError("The suite invoice reference source produced an invalid value.");
|
|
661
|
+
}
|
|
662
|
+
return parsed.value;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/identity/usernames.ts
|
|
666
|
+
import { err as err4, ok as ok4 } from "@hraness/result";
|
|
667
|
+
var SUITE_USERNAME_MIN_LENGTH = 3;
|
|
668
|
+
var SUITE_USERNAME_MAX_LENGTH = 24;
|
|
669
|
+
var suiteUsernamePattern = /^[a-z0-9](?:[a-z0-9]|[-_](?=[a-z0-9]))*[a-z0-9]$/u;
|
|
670
|
+
var reservedSuiteUsernames = new Set([
|
|
671
|
+
"account",
|
|
672
|
+
"accounts",
|
|
673
|
+
"admin",
|
|
674
|
+
"api",
|
|
675
|
+
"auth",
|
|
676
|
+
"billing",
|
|
677
|
+
"design",
|
|
678
|
+
"docs",
|
|
679
|
+
"help",
|
|
680
|
+
"hraness",
|
|
681
|
+
"login",
|
|
682
|
+
"logout",
|
|
683
|
+
"new",
|
|
684
|
+
"newsletter",
|
|
685
|
+
"party",
|
|
686
|
+
"place",
|
|
687
|
+
"preview",
|
|
688
|
+
"pub",
|
|
689
|
+
"root",
|
|
690
|
+
"settings",
|
|
691
|
+
"social-image",
|
|
692
|
+
"source",
|
|
693
|
+
"sources",
|
|
694
|
+
"support",
|
|
695
|
+
"system",
|
|
696
|
+
"user",
|
|
697
|
+
"users",
|
|
698
|
+
"www"
|
|
699
|
+
]);
|
|
700
|
+
function validateCanonicalSuiteUsername(value) {
|
|
701
|
+
if (value.length < SUITE_USERNAME_MIN_LENGTH) {
|
|
702
|
+
return err4("suite-username-too-short");
|
|
703
|
+
}
|
|
704
|
+
if (value.length > SUITE_USERNAME_MAX_LENGTH) {
|
|
705
|
+
return err4("suite-username-too-long");
|
|
706
|
+
}
|
|
707
|
+
if (!suiteUsernamePattern.test(value)) {
|
|
708
|
+
return err4("invalid-suite-username");
|
|
709
|
+
}
|
|
710
|
+
return ok4(value);
|
|
711
|
+
}
|
|
712
|
+
function normalizeSuiteUsername(value) {
|
|
713
|
+
if (typeof value !== "string")
|
|
714
|
+
return err4("invalid-suite-username");
|
|
715
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
716
|
+
const code = value.charCodeAt(index);
|
|
717
|
+
if (code > 127 || code < 32 || code === 127) {
|
|
718
|
+
return err4("invalid-suite-username");
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
const trimmed = value.trim();
|
|
722
|
+
const parsed = validateCanonicalSuiteUsername(trimmed.toLowerCase());
|
|
723
|
+
if (!parsed.ok)
|
|
724
|
+
return parsed;
|
|
725
|
+
return reservedSuiteUsernames.has(parsed.value) ? err4("suite-username-reserved") : parsed;
|
|
726
|
+
}
|
|
727
|
+
function parseSuiteUsername(value) {
|
|
728
|
+
if (typeof value !== "string")
|
|
729
|
+
return err4("invalid-suite-username");
|
|
730
|
+
const parsed = validateCanonicalSuiteUsername(value);
|
|
731
|
+
return parsed.ok && parsed.value === value ? parsed : err4(parsed.ok ? "invalid-suite-username" : parsed.error);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/identity/profiles-v2.ts
|
|
735
|
+
var LINK_KEYS = [
|
|
736
|
+
"bluesky",
|
|
737
|
+
"github",
|
|
738
|
+
"instagram",
|
|
739
|
+
"linkedin",
|
|
740
|
+
"telegram",
|
|
741
|
+
"website",
|
|
742
|
+
"x"
|
|
743
|
+
];
|
|
744
|
+
var PUBLIC_KEYS = [
|
|
745
|
+
"schemaVersion",
|
|
746
|
+
"accountId",
|
|
747
|
+
"username",
|
|
748
|
+
"name",
|
|
749
|
+
"bio",
|
|
750
|
+
"links",
|
|
751
|
+
"avatarRef",
|
|
752
|
+
"revision"
|
|
753
|
+
];
|
|
754
|
+
var EDITOR_KEYS = [...PUBLIC_KEYS, "publication"];
|
|
755
|
+
var UPDATE_KEYS = [
|
|
756
|
+
"schemaVersion",
|
|
757
|
+
"expectedRevision",
|
|
758
|
+
"name",
|
|
759
|
+
"bio",
|
|
760
|
+
"links",
|
|
761
|
+
"avatarRef",
|
|
762
|
+
"publication"
|
|
763
|
+
];
|
|
764
|
+
var MAX_INPUT_TEXT_LENGTH = 32768;
|
|
765
|
+
var AVATAR_PATTERN = /^avref_(?!0{64}$)[0-9a-f]{64}$/u;
|
|
766
|
+
function issue(field = "profile", reason = "invalid") {
|
|
767
|
+
return deepFreeze(err5({ field, reason }));
|
|
768
|
+
}
|
|
769
|
+
function success(value) {
|
|
770
|
+
return deepFreeze(ok5(value));
|
|
771
|
+
}
|
|
772
|
+
function snapshot(value, keys) {
|
|
773
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
const prototype = Object.getPrototypeOf(value);
|
|
777
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
778
|
+
return null;
|
|
779
|
+
const actual = Reflect.ownKeys(value);
|
|
780
|
+
if (actual.length !== keys.length)
|
|
781
|
+
return null;
|
|
782
|
+
const copy = Object.create(null);
|
|
783
|
+
for (const key of actual) {
|
|
784
|
+
if (typeof key !== "string" || !keys.includes(key))
|
|
785
|
+
return null;
|
|
786
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
787
|
+
if (descriptor === undefined || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) {
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
const field = descriptor.value;
|
|
791
|
+
copy[key] = field;
|
|
792
|
+
}
|
|
793
|
+
return copy;
|
|
794
|
+
}
|
|
795
|
+
function nonnegativeInteger2(value) {
|
|
796
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
797
|
+
}
|
|
798
|
+
function parseSuiteAvatarRef(value) {
|
|
799
|
+
return typeof value === "string" && value.length === 70 && AVATAR_PATTERN.test(value) ? success(value) : issue("avatarRef");
|
|
800
|
+
}
|
|
801
|
+
function optionalAvatar(value) {
|
|
802
|
+
return value === null ? success(null) : parseSuiteAvatarRef(value);
|
|
803
|
+
}
|
|
804
|
+
function legacyIssue(error) {
|
|
805
|
+
const field = error.field === "application" || error.field === "email" ? "profile" : error.field;
|
|
806
|
+
return issue(field, error.reason);
|
|
807
|
+
}
|
|
808
|
+
function normalizeSuiteProfileLinkV2(key, value) {
|
|
809
|
+
if (typeof key !== "string" || !LINK_KEYS.some((candidate) => candidate === key)) {
|
|
810
|
+
return issue();
|
|
811
|
+
}
|
|
812
|
+
const field = key;
|
|
813
|
+
if (value !== null && typeof value !== "string")
|
|
814
|
+
return issue(field);
|
|
815
|
+
if (typeof value === "string" && value.length > SUITE_PROFILE_URL_MAX_LENGTH) {
|
|
816
|
+
return issue(field, "too_long");
|
|
817
|
+
}
|
|
818
|
+
if (field === "github") {
|
|
819
|
+
if (value === null || value === "")
|
|
820
|
+
return success(null);
|
|
821
|
+
if (/[^\x21-\x7e]/u.test(value))
|
|
822
|
+
return issue("github");
|
|
823
|
+
const match = /^https:\/\/(?:www\.)?github\.com\/([A-Za-z0-9_-]{1,100})\/?$/iu.exec(value);
|
|
824
|
+
return match === null || match[0] !== value ? issue("github") : success(`https://github.com/${match[1].toLowerCase()}`);
|
|
825
|
+
}
|
|
826
|
+
const parsed = normalizeSuiteProfileLink(field, value);
|
|
827
|
+
if (!parsed.ok)
|
|
828
|
+
return legacyIssue(parsed.error);
|
|
829
|
+
if (parsed.value !== null && parsed.value.length > SUITE_PROFILE_URL_MAX_LENGTH) {
|
|
830
|
+
return issue(field, "too_long");
|
|
831
|
+
}
|
|
832
|
+
return success(parsed.value);
|
|
833
|
+
}
|
|
834
|
+
function parsedContent(value, canonical) {
|
|
835
|
+
for (const field of ["name", "bio"]) {
|
|
836
|
+
if (typeof value[field] === "string" && value[field].length > MAX_INPUT_TEXT_LENGTH) {
|
|
837
|
+
return issue(field, "too_long");
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
const inputLinks = snapshot(value["links"], LINK_KEYS);
|
|
841
|
+
if (inputLinks === null)
|
|
842
|
+
return issue();
|
|
843
|
+
const links = {
|
|
844
|
+
bluesky: null,
|
|
845
|
+
github: null,
|
|
846
|
+
instagram: null,
|
|
847
|
+
linkedin: null,
|
|
848
|
+
telegram: null,
|
|
849
|
+
website: null,
|
|
850
|
+
x: null
|
|
851
|
+
};
|
|
852
|
+
for (const key of LINK_KEYS) {
|
|
853
|
+
const result = normalizeSuiteProfileLinkV2(key, inputLinks[key]);
|
|
854
|
+
if (!result.ok)
|
|
855
|
+
return result;
|
|
856
|
+
if (canonical && result.value !== inputLinks[key])
|
|
857
|
+
return issue(key);
|
|
858
|
+
links[key] = result.value;
|
|
859
|
+
}
|
|
860
|
+
const parsed = parseSuiteProfileUpdateRequest({
|
|
861
|
+
expectedRevision: 0,
|
|
862
|
+
name: value["name"],
|
|
863
|
+
bio: value["bio"],
|
|
864
|
+
links: {
|
|
865
|
+
bluesky: links.bluesky,
|
|
866
|
+
instagram: links.instagram,
|
|
867
|
+
linkedin: links.linkedin,
|
|
868
|
+
telegram: links.telegram,
|
|
869
|
+
website: links.website,
|
|
870
|
+
x: links.x
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
if (!parsed.ok)
|
|
874
|
+
return legacyIssue(parsed.error);
|
|
875
|
+
if (canonical && (parsed.value.name !== value["name"] || parsed.value.bio !== value["bio"])) {
|
|
876
|
+
return issue();
|
|
877
|
+
}
|
|
878
|
+
return success({ name: parsed.value.name, bio: parsed.value.bio, links });
|
|
879
|
+
}
|
|
880
|
+
function parseView(input, editor) {
|
|
881
|
+
try {
|
|
882
|
+
const value = snapshot(input, editor ? EDITOR_KEYS : PUBLIC_KEYS);
|
|
883
|
+
if (value === null)
|
|
884
|
+
return issue();
|
|
885
|
+
if (value["schemaVersion"] !== 2)
|
|
886
|
+
return issue("schemaVersion");
|
|
887
|
+
const accountId = parseSuiteAccountId(value["accountId"]);
|
|
888
|
+
if (!accountId.ok)
|
|
889
|
+
return issue("accountId");
|
|
890
|
+
const username = editor && value["username"] === null ? success(null) : parseSuiteUsername(value["username"]);
|
|
891
|
+
if (!username.ok)
|
|
892
|
+
return issue("username");
|
|
893
|
+
const revision = value["revision"];
|
|
894
|
+
if (!nonnegativeInteger2(revision) || !editor && revision === 0)
|
|
895
|
+
return issue("revision");
|
|
896
|
+
const avatarRef = optionalAvatar(value["avatarRef"]);
|
|
897
|
+
if (!avatarRef.ok)
|
|
898
|
+
return avatarRef;
|
|
899
|
+
const publication = value["publication"];
|
|
900
|
+
if (editor && publication !== "private" && publication !== "published") {
|
|
901
|
+
return issue("publication");
|
|
902
|
+
}
|
|
903
|
+
if (editor && publication === "published" && username.value === null)
|
|
904
|
+
return issue("username");
|
|
905
|
+
let content;
|
|
906
|
+
if (revision === 0) {
|
|
907
|
+
const links = snapshot(value["links"], LINK_KEYS);
|
|
908
|
+
if (value["name"] !== "" || value["bio"] !== "" || avatarRef.value !== null || publication !== "private" || links === null || LINK_KEYS.some((key) => links[key] !== null))
|
|
909
|
+
return issue();
|
|
910
|
+
content = {
|
|
911
|
+
name: "",
|
|
912
|
+
bio: "",
|
|
913
|
+
links: {
|
|
914
|
+
bluesky: null,
|
|
915
|
+
github: null,
|
|
916
|
+
instagram: null,
|
|
917
|
+
linkedin: null,
|
|
918
|
+
telegram: null,
|
|
919
|
+
website: null,
|
|
920
|
+
x: null
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
} else {
|
|
924
|
+
const parsed = parsedContent(value, true);
|
|
925
|
+
if (!parsed.ok)
|
|
926
|
+
return parsed;
|
|
927
|
+
content = parsed.value;
|
|
928
|
+
}
|
|
929
|
+
const profile = {
|
|
930
|
+
schemaVersion: 2,
|
|
931
|
+
accountId: accountId.value,
|
|
932
|
+
...content,
|
|
933
|
+
avatarRef: avatarRef.value,
|
|
934
|
+
revision
|
|
935
|
+
};
|
|
936
|
+
if (editor && (publication === "private" || publication === "published")) {
|
|
937
|
+
return success({ ...profile, username: username.value, publication });
|
|
938
|
+
}
|
|
939
|
+
if (username.value === null)
|
|
940
|
+
return issue("username");
|
|
941
|
+
return success({ ...profile, username: username.value });
|
|
942
|
+
} catch {
|
|
943
|
+
return issue();
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function parseSuitePublicProfileV2(value) {
|
|
947
|
+
const parsed = parseView(value, false);
|
|
948
|
+
if (!parsed.ok)
|
|
949
|
+
return parsed;
|
|
950
|
+
if (parsed.value.username === null || isEditor(parsed.value))
|
|
951
|
+
return issue();
|
|
952
|
+
return success({ ...parsed.value, username: parsed.value.username });
|
|
953
|
+
}
|
|
954
|
+
function parseSuiteProfileEditorV2(value) {
|
|
955
|
+
const parsed = parseView(value, true);
|
|
956
|
+
if (!parsed.ok)
|
|
957
|
+
return parsed;
|
|
958
|
+
return isEditor(parsed.value) ? success(parsed.value) : issue();
|
|
959
|
+
}
|
|
960
|
+
function isEditor(value) {
|
|
961
|
+
return Object.hasOwn(value, "publication");
|
|
962
|
+
}
|
|
963
|
+
function parseSuiteProfileUpdateV2(input) {
|
|
964
|
+
try {
|
|
965
|
+
const value = snapshot(input, UPDATE_KEYS);
|
|
966
|
+
if (value === null)
|
|
967
|
+
return issue();
|
|
968
|
+
if (value["schemaVersion"] !== 2)
|
|
969
|
+
return issue("schemaVersion");
|
|
970
|
+
const expectedRevision = value["expectedRevision"];
|
|
971
|
+
if (!nonnegativeInteger2(expectedRevision))
|
|
972
|
+
return issue("expectedRevision");
|
|
973
|
+
const publication = value["publication"];
|
|
974
|
+
if (publication !== "publish" && publication !== "private")
|
|
975
|
+
return issue("publication");
|
|
976
|
+
const avatarRef = optionalAvatar(value["avatarRef"]);
|
|
977
|
+
if (!avatarRef.ok)
|
|
978
|
+
return avatarRef;
|
|
979
|
+
const content = parsedContent(value, false);
|
|
980
|
+
if (!content.ok)
|
|
981
|
+
return content;
|
|
982
|
+
return success({
|
|
983
|
+
schemaVersion: 2,
|
|
984
|
+
expectedRevision,
|
|
985
|
+
...content.value,
|
|
986
|
+
avatarRef: avatarRef.value,
|
|
987
|
+
publication
|
|
988
|
+
});
|
|
989
|
+
} catch {
|
|
990
|
+
return issue();
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
function avatarUrl(ref, path) {
|
|
994
|
+
const parsed = parseSuiteAvatarRef(ref);
|
|
995
|
+
return parsed.ok ? success(`${getSuiteAccountsDeployment("production").accountsOrigin}${path}/${parsed.value}.webp`) : parsed;
|
|
996
|
+
}
|
|
997
|
+
function suiteProfileAvatarPublicUrl(ref) {
|
|
998
|
+
return avatarUrl(ref, "/suite/profile/avatar/v1");
|
|
999
|
+
}
|
|
1000
|
+
function suiteProfileAvatarEditorUrl(ref) {
|
|
1001
|
+
return avatarUrl(ref, "/api/profile/avatar/v1");
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/profile-form.stylex.ts
|
|
1005
|
+
var profileFormClasses = {
|
|
1006
|
+
affix: {
|
|
1007
|
+
className: "x6s0dn4 x1w9dvvm xxut2h2 x5goj4r x1y0btm7 xmkeg23 x78zum5 xeuugli xc4xxb1 x1p4mulw x1fanpfn x14xc8xn x16gxa91"
|
|
1008
|
+
}.className,
|
|
1009
|
+
affixInput: {
|
|
1010
|
+
className: "xjbqb8w xc342km x1heor9g x1iyjqo2 xs83m0k xjb2p0i x1qlqyl8 x1t35e8 x1pd3egz x37zpob x19hwnzc x1t137rt xvgi8cs x15u6esi x33uob6 x15y8ph0"
|
|
1011
|
+
}.className,
|
|
1012
|
+
affixLead: {
|
|
1013
|
+
className: "x6s0dn4 x5ee4ez x78zum5 x2lah0s x1qzg9v8 x1rcpt3j x79ra4s xuxw1ft"
|
|
1014
|
+
}.className,
|
|
1015
|
+
choice: {
|
|
1016
|
+
className: "x6s0dn4 x1ypdohk x78zum5 x5m0csh xgbkey0"
|
|
1017
|
+
}.className,
|
|
1018
|
+
button: {
|
|
1019
|
+
className: "x1fdtg7e x1u7o2vf x1jfjhf8 x18o3ruo x12koezg x1y4qj14 x182nak8 x103pssi x9ox504 x1y0btm7 xmkeg23 x18sabzy x1jleocg x1pjjote x1e53mt7 xgkqhyc x1e6avla xz46ci x1ypdohk xjb0foi xjb2p0i x1qlqyl8 x1xh6y1q x1t35e8 x1aazh3f x1xlr1w8 x10rt0pk xvmqkbn x1rcybi7 x61gc8y xd4aj15 xkyhvkk x15bjb6t x1lqcxt8 xfqoyci x4odjur x9dzeaa x52dz5p x1flg24d xo8l03z x7s97pk x9v5kkp x784prv"
|
|
1020
|
+
}.className,
|
|
1021
|
+
control: {
|
|
1022
|
+
className: "x1fdtg7e x1u7o2vf x1w9dvvm x18o3ruo x12koezg x1y4qj14 x182nak8 x103pssi xxut2h2 x1y0btm7 xmkeg23 x18sabzy x1jleocg x1pjjote x1e53mt7 xgkqhyc x5goj4r x1heor9g xjb2p0i x1qlqyl8 x1xh6y1q x1t35e8 x1aazh3f x1pd3egz x10rt0pk xvmqkbn x1rcybi7 x61gc8y xd4aj15 xkyhvkk x37zpob xeuugli xvgi8cs x79ra4s x33uob6 x15y8ph0 xh8yej3 xo8l03z x7s97pk x9v5kkp x784prv"
|
|
1023
|
+
}.className,
|
|
1024
|
+
email: {
|
|
1025
|
+
className: "x1fdtg7e x1u7o2vf x1w9dvvm x18o3ruo x12koezg x1y4qj14 x182nak8 x103pssi xxut2h2 x1y0btm7 xmkeg23 x18sabzy x1jleocg x1pjjote x1e53mt7 xgkqhyc x5goj4r xjb2p0i x1qlqyl8 x1xh6y1q x1t35e8 x1aazh3f x1pd3egz x10rt0pk xvmqkbn x1rcybi7 x61gc8y xd4aj15 xkyhvkk x37zpob xeuugli xvgi8cs x79ra4s x33uob6 x15y8ph0 xh8yej3 xo8l03z x7s97pk x9v5kkp x784prv x5ee4ez xt0e3qv xf9vgkq"
|
|
1026
|
+
}.className,
|
|
1027
|
+
error: {
|
|
1028
|
+
className: "xd5ouml x1nrrp6k xat24cr xj3b58b x1yf7rl7 xdj266r"
|
|
1029
|
+
}.className,
|
|
1030
|
+
field: {
|
|
1031
|
+
className: "xrvj5dj x73f2yu"
|
|
1032
|
+
}.className,
|
|
1033
|
+
form: {
|
|
1034
|
+
className: "xrvj5dj x8fetqu xh8yej3"
|
|
1035
|
+
}.className,
|
|
1036
|
+
label: {
|
|
1037
|
+
className: "x1nrrp6k x1xlr1w8"
|
|
1038
|
+
}.className,
|
|
1039
|
+
links: {
|
|
1040
|
+
className: "xxut2h2 x1y0btm7 x178xt8z x1qhh985 xyj58a3 xgfja2r xj3b58b x1yf7rl7 xeuugli x1q57ora"
|
|
1041
|
+
}.className,
|
|
1042
|
+
linksBody: {
|
|
1043
|
+
className: "xrvj5dj x8fetqu x1q57ora"
|
|
1044
|
+
}.className,
|
|
1045
|
+
linksHint: {
|
|
1046
|
+
className: "xo1l8bm x13zjp5t"
|
|
1047
|
+
}.className,
|
|
1048
|
+
linksSummary: {
|
|
1049
|
+
className: "x5ee4ez x1ypdohk x1nrrp6k x1xlr1w8 xes99ow x1iul6r4"
|
|
1050
|
+
}.className,
|
|
1051
|
+
mark: {
|
|
1052
|
+
className: "x1lliihq x2lah0s xdddoky xf6x3ch"
|
|
1053
|
+
}.className,
|
|
1054
|
+
note: {
|
|
1055
|
+
className: "x5ee4ez x1nrrp6k x1evy7pa xhbfen4 xj3b58b x1yf7rl7 xdj266r xj0a0fe"
|
|
1056
|
+
}.className,
|
|
1057
|
+
radio: {
|
|
1058
|
+
className: "x1c7drp3 x2lah0s xhdxxi3 xat24cr xj3b58b x1yf7rl7 xdj266r xmh2cwo xo8l03z x7s97pk x9v5kkp x784prv"
|
|
1059
|
+
}.className,
|
|
1060
|
+
textarea: {
|
|
1061
|
+
className: "x1fdtg7e x1u7o2vf x1w9dvvm x18o3ruo x12koezg x1y4qj14 x182nak8 x103pssi xxut2h2 x1y0btm7 xmkeg23 x18sabzy x1jleocg x1pjjote x1e53mt7 xgkqhyc x5goj4r x1heor9g xjb2p0i x1qlqyl8 x1xh6y1q x1t35e8 x1aazh3f x1pd3egz x10rt0pk xvmqkbn x1rcybi7 x61gc8y xd4aj15 xkyhvkk x37zpob xeuugli xvgi8cs x79ra4s x33uob6 x15y8ph0 xh8yej3 xo8l03z x7s97pk x9v5kkp x784prv x288g5"
|
|
1062
|
+
}.className,
|
|
1063
|
+
visibility: {
|
|
1064
|
+
className: "xc342km xat24cr xj3b58b x1yf7rl7 x1mjqqkp xeuugli x18d9i69 x1uhho1l x1xpa7k x1b58sdr"
|
|
1065
|
+
}.className
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
// src/public-profile-form-result.ts
|
|
1069
|
+
var LINK_KEYS2 = [
|
|
1070
|
+
"bluesky",
|
|
1071
|
+
"github",
|
|
1072
|
+
"instagram",
|
|
1073
|
+
"linkedin",
|
|
1074
|
+
"telegram",
|
|
1075
|
+
"website",
|
|
1076
|
+
"x"
|
|
1077
|
+
];
|
|
1078
|
+
function matchesRequest(profile, request) {
|
|
1079
|
+
return profile.name === request.name && profile.bio === request.bio && profile.avatarRef === request.avatarRef && profile.publication === (request.publication === "publish" ? "published" : "private") && LINK_KEYS2.every((key) => profile.links[key] === request.links[key]);
|
|
1080
|
+
}
|
|
1081
|
+
function snapshot2(value) {
|
|
1082
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
1083
|
+
return null;
|
|
1084
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1085
|
+
if (prototype !== null && prototype !== Object.prototype)
|
|
1086
|
+
return null;
|
|
1087
|
+
const keys = Reflect.ownKeys(value);
|
|
1088
|
+
if (keys.length < 1 || keys.length > 2 || !keys.includes("status"))
|
|
1089
|
+
return null;
|
|
1090
|
+
const owned = Object.create(null);
|
|
1091
|
+
for (const key of keys) {
|
|
1092
|
+
if (key !== "status" && key !== "profile")
|
|
1093
|
+
return null;
|
|
1094
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1095
|
+
if (descriptor === undefined || !descriptor.enumerable || !Object.hasOwn(descriptor, "value"))
|
|
1096
|
+
return null;
|
|
1097
|
+
const field = descriptor.value;
|
|
1098
|
+
owned[key] = field;
|
|
1099
|
+
}
|
|
1100
|
+
return owned;
|
|
1101
|
+
}
|
|
1102
|
+
function parsePublicProfileFormResult(value, currentProfile, request) {
|
|
1103
|
+
try {
|
|
1104
|
+
const current = parseSuiteProfileEditorV2(currentProfile);
|
|
1105
|
+
const update = parseSuiteProfileUpdateV2(request);
|
|
1106
|
+
if (!current.ok || !update.ok || update.value.expectedRevision !== current.value.revision)
|
|
1107
|
+
return null;
|
|
1108
|
+
const envelope = snapshot2(value);
|
|
1109
|
+
if (envelope === null)
|
|
1110
|
+
return null;
|
|
1111
|
+
const status = envelope["status"];
|
|
1112
|
+
if (status === "unauthorized" || status === "username_required" || status === "invalid_avatar") {
|
|
1113
|
+
return Object.hasOwn(envelope, "profile") ? null : Object.freeze({ status });
|
|
1114
|
+
}
|
|
1115
|
+
if (status !== "saved" && status !== "conflict")
|
|
1116
|
+
return null;
|
|
1117
|
+
if (!Object.hasOwn(envelope, "profile"))
|
|
1118
|
+
return null;
|
|
1119
|
+
const parsed = parseSuiteProfileEditorV2(envelope["profile"]);
|
|
1120
|
+
if (!parsed.ok || parsed.value.accountId !== current.value.accountId)
|
|
1121
|
+
return null;
|
|
1122
|
+
const profile = parsed.value;
|
|
1123
|
+
const expected = update.value;
|
|
1124
|
+
if (status === "conflict") {
|
|
1125
|
+
return profile.revision > expected.expectedRevision ? Object.freeze({ status, profile }) : null;
|
|
1126
|
+
}
|
|
1127
|
+
const sameRevision = profile.revision === expected.expectedRevision && matchesRequest(current.value, expected);
|
|
1128
|
+
const nextRevision = expected.expectedRevision < Number.MAX_SAFE_INTEGER && profile.revision === expected.expectedRevision + 1;
|
|
1129
|
+
if (!sameRevision && !nextRevision || !matchesRequest(profile, expected))
|
|
1130
|
+
return null;
|
|
1131
|
+
return Object.freeze({ status, profile });
|
|
1132
|
+
} catch {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// src/public-profile-links.ts
|
|
1138
|
+
var X_GLYPH = [
|
|
1139
|
+
["path", { d: "M3 21L10.5484 13.4516M21 3L13.4516 10.5484M13.4516 10.5484L8 3H3L10.5484 13.4516M13.4516 10.5484L21 21H16L10.5484 13.4516" }]
|
|
1140
|
+
];
|
|
1141
|
+
var GITHUB_GLYPH = [
|
|
1142
|
+
["path", { d: "M10 20.5675C6.57143 21.7248 3.71429 20.5675 2 17" }],
|
|
1143
|
+
["path", { d: "M10 22V18.7579C10 18.1596 10.1839 17.6396 10.4804 17.1699C10.6838 16.8476 10.5445 16.3904 10.1771 16.2894C7.13394 15.4528 5 14.1077 5 9.64606C5 8.48611 5.38005 7.39556 6.04811 6.4464C6.21437 6.21018 6.29749 6.09208 6.31748 5.9851C6.33746 5.87813 6.30272 5.73852 6.23322 5.45932C5.95038 4.32292 5.96871 3.11619 6.39322 2.02823C6.39322 2.02823 7.27042 1.74242 9.26698 2.98969C9.72282 3.27447 9.95075 3.41686 10.1515 3.44871C10.3522 3.48056 10.6206 3.41384 11.1573 3.28041C11.8913 3.09795 12.6476 3 13.5 3C14.3524 3 15.1087 3.09795 15.8427 3.28041C16.3794 3.41384 16.6478 3.48056 16.8485 3.44871C17.0493 3.41686 17.2772 3.27447 17.733 2.98969C19.7296 1.74242 20.6068 2.02823 20.6068 2.02823C21.0313 3.11619 21.0496 4.32292 20.7668 5.45932C20.6973 5.73852 20.6625 5.87813 20.6825 5.9851C20.7025 6.09207 20.7856 6.21019 20.9519 6.4464C21.6199 7.39556 22 8.48611 22 9.64606C22 14.1077 19.8661 15.4528 16.8229 16.2894C16.4555 16.3904 16.3162 16.8476 16.5196 17.1699C16.8161 17.6396 17 18.1596 17 18.7579V22" }]
|
|
1144
|
+
];
|
|
1145
|
+
var LINKEDIN_GLYPH = [
|
|
1146
|
+
["path", { d: "M7 10V17" }],
|
|
1147
|
+
["path", { d: "M11 13V17M11 13C11 11.3431 12.3431 10 14 10C15.6569 10 17 11.3431 17 13V17M11 13V10" }],
|
|
1148
|
+
["path", { d: "M7.125 6.75H7M7.25 6.75C7.25 6.88807 7.13807 7 7 7C6.86193 7 6.75 6.88807 6.75 6.75C6.75 6.61193 6.86193 6.5 7 6.5C7.13807 6.5 7.25 6.61193 7.25 6.75Z" }],
|
|
1149
|
+
["path", { d: "M3 12C3 7.75736 3 5.63604 4.31802 4.31802C5.63604 3 7.75736 3 12 3C16.2426 3 18.364 3 19.682 4.31802C21 5.63604 21 7.75736 21 12C21 16.2426 21 18.364 19.682 19.682C18.364 21 16.2426 21 12 21C7.75736 21 5.63604 21 4.31802 19.682C3 18.364 3 16.2426 3 12Z" }]
|
|
1150
|
+
];
|
|
1151
|
+
var WEBSITE_GLYPH = [
|
|
1152
|
+
["circle", { cx: 12, cy: 12, r: 10 }],
|
|
1153
|
+
["path", { d: "M8 12C8 18 12 22 12 22C12 22 16 18 16 12C16 6 12 2 12 2C12 2 8 6 8 12Z" }],
|
|
1154
|
+
["path", { d: "M21 15H3" }],
|
|
1155
|
+
["path", { d: "M21 9H3" }]
|
|
1156
|
+
];
|
|
1157
|
+
var BLUESKY_GLYPH = [
|
|
1158
|
+
["path", { d: "M12 11.4963C11.8936 11.2963 7.45492 3 3.50417 3C1.33647 3 2.00456 8 2.50443 10.5C2.70653 11.5108 3.50417 14.5 8.003 14C8.003 14 4.00404 14.5 4.00404 17C4.00404 18.5 6.50339 21 8.50287 21C10.4606 21 11.9391 16.6859 12 16.5058C12.0609 16.6859 13.5394 21 15.4971 21C17.4966 21 19.996 18.5 19.996 17C19.996 14.5 15.997 14 15.997 14C20.4958 14.5 21.2935 11.5108 21.4956 10.5C21.9954 8 22.6635 3 20.4958 3C16.5451 3 12.1064 11.2963 12 11.4963Z" }]
|
|
1159
|
+
];
|
|
1160
|
+
var INSTAGRAM_GLYPH = [
|
|
1161
|
+
["path", { d: "M3 12C3 7.75736 3 5.63604 4.31802 4.31802C5.63604 3 7.75736 3 12 3C16.2426 3 18.364 3 19.682 4.31802C21 5.63604 21 7.75736 21 12C21 16.2426 21 18.364 19.682 19.682C18.364 21 16.2426 21 12 21C7.75736 21 5.63604 21 4.31802 19.682C3 18.364 3 16.2426 3 12Z" }],
|
|
1162
|
+
["path", { d: "M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12Z" }],
|
|
1163
|
+
["path", { d: "M17.3748 6.75024H17.2498M17.4998 6.75024C17.4998 6.88832 17.3878 7.00024 17.2498 7.00024C17.1117 7.00024 16.9998 6.88832 16.9998 6.75024C16.9998 6.61217 17.1117 6.50024 17.2498 6.50024C17.3878 6.50024 17.4998 6.61217 17.4998 6.75024Z" }]
|
|
1164
|
+
];
|
|
1165
|
+
var TELEGRAM_GLYPH = [
|
|
1166
|
+
["path", { d: "M11.9854 15.4083L15.2268 19.0936C16.4277 20.4589 17.0282 21.1416 17.6567 20.9754C18.2852 20.8092 18.5008 19.9108 18.9318 18.1138L21.3229 8.1459C21.9868 5.37832 22.3187 3.99454 21.5808 3.312C20.843 2.62947 19.564 3.13725 17.0061 4.15282L5.13876 8.86449C3.09293 9.67674 2.07001 10.0829 2.00507 10.7808C1.99842 10.8522 1.99831 10.9241 2.00474 10.9955C2.06754 11.6937 3.08921 12.1033 5.13255 12.9223C6.05838 13.2934 6.5213 13.479 6.8532 13.8344C6.89052 13.8743 6.9264 13.9157 6.96078 13.9584C7.26658 14.3384 7.39709 14.8371 7.65808 15.8344L8.14653 17.701C8.4005 18.6715 8.52749 19.1568 8.86008 19.223C9.19267 19.2891 9.48225 18.8867 10.0614 18.0819L11.9854 15.4083ZM11.9854 15.4083L11.6676 15.0771C11.3059 14.7001 11.1251 14.5117 11.1251 14.2775C11.1251 14.0433 11.3059 13.8548 11.6676 13.4778L15.2406 9.75409" }]
|
|
1167
|
+
];
|
|
1168
|
+
var SUITE_PROFILE_LINK_AFFORDANCES = {
|
|
1169
|
+
bluesky: { glyph: BLUESKY_GLYPH, placeholder: "name.bsky.social", prefix: "bsky.app/profile/" },
|
|
1170
|
+
github: { glyph: GITHUB_GLYPH, placeholder: "username", prefix: "github.com/" },
|
|
1171
|
+
instagram: { glyph: INSTAGRAM_GLYPH, placeholder: "handle", prefix: "instagram.com/" },
|
|
1172
|
+
linkedin: { glyph: LINKEDIN_GLYPH, placeholder: "handle", prefix: "linkedin.com/in/" },
|
|
1173
|
+
telegram: { glyph: TELEGRAM_GLYPH, placeholder: "handle", prefix: "t.me/" },
|
|
1174
|
+
website: { glyph: WEBSITE_GLYPH, placeholder: "example.com", prefix: "https://" },
|
|
1175
|
+
x: { glyph: X_GLYPH, placeholder: "handle", prefix: "x.com/" }
|
|
1176
|
+
};
|
|
1177
|
+
var SCHEME = /^https?:\/\//iu;
|
|
1178
|
+
function expandSuiteProfileLinkInput(key, raw) {
|
|
1179
|
+
const trimmed = raw.trim();
|
|
1180
|
+
if (trimmed === "" || SCHEME.test(trimmed))
|
|
1181
|
+
return trimmed;
|
|
1182
|
+
const withoutAt = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
|
|
1183
|
+
if (key === "website")
|
|
1184
|
+
return `https://${trimmed}`;
|
|
1185
|
+
if (withoutAt.includes("/"))
|
|
1186
|
+
return `https://${withoutAt}`;
|
|
1187
|
+
switch (key) {
|
|
1188
|
+
case "bluesky": {
|
|
1189
|
+
const handle = withoutAt.includes(".") ? withoutAt : `${withoutAt}.bsky.social`;
|
|
1190
|
+
return `https://bsky.app/profile/${handle}`;
|
|
1191
|
+
}
|
|
1192
|
+
case "github":
|
|
1193
|
+
return `https://github.com/${withoutAt}`;
|
|
1194
|
+
case "instagram":
|
|
1195
|
+
return `https://www.instagram.com/${withoutAt}`;
|
|
1196
|
+
case "linkedin":
|
|
1197
|
+
return `https://www.linkedin.com/in/${withoutAt}`;
|
|
1198
|
+
case "telegram":
|
|
1199
|
+
return `https://t.me/${withoutAt}`;
|
|
1200
|
+
case "x":
|
|
1201
|
+
return `https://x.com/${withoutAt}`;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// src/public-profile-form.tsx
|
|
1206
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1207
|
+
import { createElement } from "react";
|
|
1208
|
+
var LINK_FIELDS = [
|
|
1209
|
+
["x", "X"],
|
|
1210
|
+
["github", "GitHub"],
|
|
1211
|
+
["linkedin", "LinkedIn"],
|
|
1212
|
+
["bluesky", "Bluesky"],
|
|
1213
|
+
["instagram", "Instagram"],
|
|
1214
|
+
["telegram", "Telegram"],
|
|
1215
|
+
["website", "Website"]
|
|
1216
|
+
];
|
|
1217
|
+
function expandedLinks(links) {
|
|
1218
|
+
const expanded = { ...links };
|
|
1219
|
+
for (const key of Object.keys(expanded)) {
|
|
1220
|
+
expanded[key] = expandSuiteProfileLinkInput(key, links[key]);
|
|
1221
|
+
}
|
|
1222
|
+
return expanded;
|
|
1223
|
+
}
|
|
1224
|
+
function LinkMark({ glyph }) {
|
|
1225
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
1226
|
+
"aria-hidden": "true",
|
|
1227
|
+
className: profileFormClasses.mark,
|
|
1228
|
+
fill: "none",
|
|
1229
|
+
stroke: "currentColor",
|
|
1230
|
+
strokeLinecap: "round",
|
|
1231
|
+
strokeLinejoin: "round",
|
|
1232
|
+
strokeWidth: 1.5,
|
|
1233
|
+
viewBox: "0 0 24 24",
|
|
1234
|
+
children: glyph.map(([tag, attrs], index) => tag === "circle" ? /* @__PURE__ */ jsx("circle", {
|
|
1235
|
+
...attrs
|
|
1236
|
+
}, index) : /* @__PURE__ */ jsx("path", {
|
|
1237
|
+
...attrs
|
|
1238
|
+
}, index))
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
function linkInputs(profile) {
|
|
1242
|
+
return {
|
|
1243
|
+
x: profile.links.x ?? "",
|
|
1244
|
+
github: profile.links.github ?? "",
|
|
1245
|
+
linkedin: profile.links.linkedin ?? "",
|
|
1246
|
+
website: profile.links.website ?? "",
|
|
1247
|
+
bluesky: profile.links.bluesky ?? "",
|
|
1248
|
+
instagram: profile.links.instagram ?? "",
|
|
1249
|
+
telegram: profile.links.telegram ?? ""
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
function issueMessage(issue2) {
|
|
1253
|
+
if (issue2.field === "name")
|
|
1254
|
+
return issue2.reason === "required" ? "Enter a public-facing name." : `Use ${SUITE_PROFILE_NAME_MAX_LENGTH} characters or fewer.`;
|
|
1255
|
+
if (issue2.field === "bio")
|
|
1256
|
+
return `Use ${SUITE_PROFILE_BIO_MAX_LENGTH} characters or fewer.`;
|
|
1257
|
+
if (issue2.field === "publication")
|
|
1258
|
+
return "Choose whether to keep this profile private or publish it.";
|
|
1259
|
+
if (issue2.field === "github")
|
|
1260
|
+
return "Enter a GitHub profile URL, such as https://github.com/username.";
|
|
1261
|
+
if (issue2.field === "linkedin")
|
|
1262
|
+
return "Enter a LinkedIn profile URL.";
|
|
1263
|
+
if (issue2.field === "website")
|
|
1264
|
+
return "Enter a complete HTTPS URL.";
|
|
1265
|
+
const label = LINK_FIELDS.find(([key]) => key === issue2.field)?.[1];
|
|
1266
|
+
return label === undefined ? "Check the profile and try again." : `Enter a valid ${label} handle or profile URL.`;
|
|
1267
|
+
}
|
|
1268
|
+
function SuitePublicProfileForm(props) {
|
|
1269
|
+
const parsed = parseSuiteProfileEditorV2(props.initialProfile);
|
|
1270
|
+
if (!parsed.ok) {
|
|
1271
|
+
return /* @__PURE__ */ jsx("p", {
|
|
1272
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1273
|
+
role: "alert",
|
|
1274
|
+
children: "The profile is unavailable. Reload the page to try again."
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
return /* @__PURE__ */ createElement(PublicProfileEditor, {
|
|
1278
|
+
...props,
|
|
1279
|
+
initialProfile: parsed.value,
|
|
1280
|
+
key: parsed.value.accountId
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
function PublicProfileEditor({ className, initialProfile, onSave, onSaved }) {
|
|
1284
|
+
const id = useId();
|
|
1285
|
+
const [transport] = useState(() => ({ onSave, onSaved }));
|
|
1286
|
+
const [profile, setProfile] = useState(initialProfile);
|
|
1287
|
+
const [name, setName] = useState(initialProfile.name);
|
|
1288
|
+
const [bio, setBio] = useState(initialProfile.bio);
|
|
1289
|
+
const [links, setLinks] = useState(() => linkInputs(initialProfile));
|
|
1290
|
+
const [linksOpen, setLinksOpen] = useState(() => Object.values(linkInputs(initialProfile)).some((value) => value !== ""));
|
|
1291
|
+
const [visibility, setVisibility] = useState(initialProfile.publication === "published" ? "publish" : "private");
|
|
1292
|
+
const [pending, setPending] = useState(false);
|
|
1293
|
+
const [ready, setReady] = useState(false);
|
|
1294
|
+
const [conflict, setConflict] = useState(null);
|
|
1295
|
+
const [fieldErrors, setFieldErrors] = useState({});
|
|
1296
|
+
const [formError, setFormError] = useState(null);
|
|
1297
|
+
const [notice, setNotice] = useState(null);
|
|
1298
|
+
const [unconfirmed, setUnconfirmed] = useState(false);
|
|
1299
|
+
const inFlight = useRef(null);
|
|
1300
|
+
const liveForm = useRef(null);
|
|
1301
|
+
const abandoned = useRef(false);
|
|
1302
|
+
const nameControl = useRef(null);
|
|
1303
|
+
const focusLoadedProfile = useRef(false);
|
|
1304
|
+
const pendingFocus = useRef(null);
|
|
1305
|
+
const attachForm = useCallback((node) => {
|
|
1306
|
+
liveForm.current = node;
|
|
1307
|
+
if (node === null) {
|
|
1308
|
+
if (inFlight.current !== null)
|
|
1309
|
+
abandoned.current = true;
|
|
1310
|
+
inFlight.current = null;
|
|
1311
|
+
} else if (abandoned.current) {
|
|
1312
|
+
abandoned.current = false;
|
|
1313
|
+
setPending(false);
|
|
1314
|
+
setUnconfirmed(true);
|
|
1315
|
+
setFormError("The save could not be confirmed. Your draft is preserved. Try saving again, or reload the profile to check its saved state.");
|
|
1316
|
+
}
|
|
1317
|
+
}, []);
|
|
1318
|
+
useEffect(() => {
|
|
1319
|
+
setReady(true);
|
|
1320
|
+
}, []);
|
|
1321
|
+
useEffect(() => {
|
|
1322
|
+
if (focusLoadedProfile.current && conflict === null) {
|
|
1323
|
+
focusLoadedProfile.current = false;
|
|
1324
|
+
nameControl.current?.focus();
|
|
1325
|
+
}
|
|
1326
|
+
}, [conflict]);
|
|
1327
|
+
useEffect(() => {
|
|
1328
|
+
const field = pendingFocus.current;
|
|
1329
|
+
if (field === null)
|
|
1330
|
+
return;
|
|
1331
|
+
pendingFocus.current = null;
|
|
1332
|
+
liveForm.current?.querySelector(`[id="${id}-${field}"]`)?.focus();
|
|
1333
|
+
});
|
|
1334
|
+
function clearField(field) {
|
|
1335
|
+
setFieldErrors((current) => {
|
|
1336
|
+
const next = { ...current };
|
|
1337
|
+
delete next[field];
|
|
1338
|
+
return next;
|
|
1339
|
+
});
|
|
1340
|
+
setNotice(null);
|
|
1341
|
+
}
|
|
1342
|
+
function applyProfile(next, nextVisibility) {
|
|
1343
|
+
setProfile(next);
|
|
1344
|
+
setName(next.name);
|
|
1345
|
+
setBio(next.bio);
|
|
1346
|
+
setLinks(linkInputs(next));
|
|
1347
|
+
setVisibility(nextVisibility);
|
|
1348
|
+
setUnconfirmed(false);
|
|
1349
|
+
}
|
|
1350
|
+
async function submit(event) {
|
|
1351
|
+
event.preventDefault();
|
|
1352
|
+
if (!ready || liveForm.current !== event.currentTarget || inFlight.current !== null || conflict !== null)
|
|
1353
|
+
return;
|
|
1354
|
+
setNotice(null);
|
|
1355
|
+
setFormError(null);
|
|
1356
|
+
setFieldErrors({});
|
|
1357
|
+
const parsed = parseSuiteProfileUpdateV2({
|
|
1358
|
+
schemaVersion: 2,
|
|
1359
|
+
expectedRevision: profile.revision,
|
|
1360
|
+
name,
|
|
1361
|
+
bio,
|
|
1362
|
+
links: expandedLinks(links),
|
|
1363
|
+
avatarRef: profile.avatarRef,
|
|
1364
|
+
publication: visibility
|
|
1365
|
+
});
|
|
1366
|
+
if (!parsed.ok) {
|
|
1367
|
+
setFieldErrors({ [parsed.error.field]: issueMessage(parsed.error) });
|
|
1368
|
+
if (parsed.error.field in SUITE_PROFILE_LINK_AFFORDANCES)
|
|
1369
|
+
setLinksOpen(true);
|
|
1370
|
+
pendingFocus.current = parsed.error.field;
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
if (parsed.value.publication === "publish" && profile.username === null) {
|
|
1374
|
+
setFieldErrors({ publication: "Choose a username in account settings before publishing." });
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
const dispatch = Symbol();
|
|
1378
|
+
inFlight.current = dispatch;
|
|
1379
|
+
setPending(true);
|
|
1380
|
+
let saved = null;
|
|
1381
|
+
try {
|
|
1382
|
+
const raw = await transport.onSave(parsed.value);
|
|
1383
|
+
if (liveForm.current === null || inFlight.current !== dispatch)
|
|
1384
|
+
return;
|
|
1385
|
+
const result = parsePublicProfileFormResult(raw, profile, parsed.value);
|
|
1386
|
+
if (result === null)
|
|
1387
|
+
throw new Error("Unconfirmed profile result.");
|
|
1388
|
+
if (result.status === "conflict") {
|
|
1389
|
+
setConflict(result.profile);
|
|
1390
|
+
setVisibility(null);
|
|
1391
|
+
setUnconfirmed(false);
|
|
1392
|
+
setFormError("This profile changed elsewhere. Your draft is still below. Load the latest profile to replace it, then review visibility before saving.");
|
|
1393
|
+
} else if (result.status === "saved") {
|
|
1394
|
+
applyProfile(result.profile, result.profile.publication === "published" ? "publish" : "private");
|
|
1395
|
+
setNotice(result.profile.publication === "published" ? "Public profile saved." : "Private profile saved.");
|
|
1396
|
+
saved = result.profile;
|
|
1397
|
+
} else if (result.status === "unauthorized") {
|
|
1398
|
+
setFormError("Your account session is unavailable. Sign in again before saving.");
|
|
1399
|
+
} else if (result.status === "username_required") {
|
|
1400
|
+
setFormError("Choose a username in account settings, then reload this profile before publishing.");
|
|
1401
|
+
} else {
|
|
1402
|
+
setFormError("Your profile image is unavailable. Reload the profile before saving.");
|
|
1403
|
+
}
|
|
1404
|
+
} catch {
|
|
1405
|
+
if (liveForm.current === null || inFlight.current !== dispatch)
|
|
1406
|
+
return;
|
|
1407
|
+
setUnconfirmed(true);
|
|
1408
|
+
setFormError("The save could not be confirmed. Your draft is preserved. Try saving again, or reload the profile to check its saved state.");
|
|
1409
|
+
} finally {
|
|
1410
|
+
if (liveForm.current !== null && inFlight.current === dispatch) {
|
|
1411
|
+
inFlight.current = null;
|
|
1412
|
+
setPending(false);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
if (saved !== null && liveForm.current !== null) {
|
|
1416
|
+
try {
|
|
1417
|
+
transport.onSaved?.(saved);
|
|
1418
|
+
} catch {
|
|
1419
|
+
if (liveForm.current !== null)
|
|
1420
|
+
setFormError("The profile was saved, but the page could not refresh. Reload the page.");
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const disabled = !ready || pending || conflict !== null;
|
|
1425
|
+
const currentProfile = conflict ?? profile;
|
|
1426
|
+
const action = pending ? "Saving profile" : visibility === "publish" ? profile.publication === "published" ? "Save public profile" : "Publish profile" : visibility === "private" && profile.publication === "published" ? "Make profile private" : visibility === "private" ? "Save private profile" : "Save profile";
|
|
1427
|
+
const formClass = `suite-profile-form suite-public-profile-form ${profileFormClasses.form}${className === undefined ? "" : ` ${className}`}`;
|
|
1428
|
+
const describedBy = (field) => fieldErrors[field] === undefined ? undefined : `${id}-${field}-error`;
|
|
1429
|
+
const fieldError = (field) => fieldErrors[field] === undefined ? null : /* @__PURE__ */ jsx("span", {
|
|
1430
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1431
|
+
id: `${id}-${field}-error`,
|
|
1432
|
+
role: "alert",
|
|
1433
|
+
children: fieldErrors[field]
|
|
1434
|
+
});
|
|
1435
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
1436
|
+
"aria-busy": !ready || pending,
|
|
1437
|
+
className: formClass,
|
|
1438
|
+
method: "post",
|
|
1439
|
+
onSubmit: (event) => {
|
|
1440
|
+
submit(event);
|
|
1441
|
+
},
|
|
1442
|
+
ref: attachForm,
|
|
1443
|
+
children: [
|
|
1444
|
+
/* @__PURE__ */ jsx("noscript", {
|
|
1445
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
1446
|
+
className: profileFormClasses.note,
|
|
1447
|
+
children: "Enable JavaScript to edit this profile. No changes can be saved from this form without it."
|
|
1448
|
+
})
|
|
1449
|
+
}),
|
|
1450
|
+
formError === null ? null : /* @__PURE__ */ jsx("p", {
|
|
1451
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1452
|
+
role: "alert",
|
|
1453
|
+
children: formError
|
|
1454
|
+
}),
|
|
1455
|
+
conflict === null ? null : /* @__PURE__ */ jsx("button", {
|
|
1456
|
+
className: profileFormClasses.button,
|
|
1457
|
+
onClick: () => {
|
|
1458
|
+
applyProfile(conflict, null);
|
|
1459
|
+
setConflict(null);
|
|
1460
|
+
setFieldErrors({});
|
|
1461
|
+
setFormError(null);
|
|
1462
|
+
setNotice("Latest profile loaded. Choose visibility before saving.");
|
|
1463
|
+
focusLoadedProfile.current = true;
|
|
1464
|
+
},
|
|
1465
|
+
type: "button",
|
|
1466
|
+
children: "Load latest profile"
|
|
1467
|
+
}),
|
|
1468
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1469
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1470
|
+
children: [
|
|
1471
|
+
/* @__PURE__ */ jsx("label", {
|
|
1472
|
+
className: profileFormClasses.label,
|
|
1473
|
+
htmlFor: `${id}-name`,
|
|
1474
|
+
children: "Public-facing name"
|
|
1475
|
+
}),
|
|
1476
|
+
/* @__PURE__ */ jsx("input", {
|
|
1477
|
+
"aria-describedby": describedBy("name"),
|
|
1478
|
+
"aria-invalid": fieldErrors.name === undefined ? undefined : true,
|
|
1479
|
+
autoComplete: "off",
|
|
1480
|
+
className: profileFormClasses.control,
|
|
1481
|
+
disabled,
|
|
1482
|
+
id: `${id}-name`,
|
|
1483
|
+
maxLength: SUITE_PROFILE_NAME_MAX_LENGTH,
|
|
1484
|
+
onChange: (event) => {
|
|
1485
|
+
setName(event.currentTarget.value);
|
|
1486
|
+
clearField("name");
|
|
1487
|
+
},
|
|
1488
|
+
placeholder: "Ada Lovelace",
|
|
1489
|
+
ref: nameControl,
|
|
1490
|
+
required: true,
|
|
1491
|
+
type: "text",
|
|
1492
|
+
value: name
|
|
1493
|
+
}),
|
|
1494
|
+
fieldError("name")
|
|
1495
|
+
]
|
|
1496
|
+
}),
|
|
1497
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1498
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1499
|
+
children: [
|
|
1500
|
+
/* @__PURE__ */ jsx("label", {
|
|
1501
|
+
className: profileFormClasses.label,
|
|
1502
|
+
htmlFor: `${id}-bio`,
|
|
1503
|
+
children: "Bio"
|
|
1504
|
+
}),
|
|
1505
|
+
/* @__PURE__ */ jsx("textarea", {
|
|
1506
|
+
"aria-describedby": describedBy("bio"),
|
|
1507
|
+
"aria-invalid": fieldErrors.bio === undefined ? undefined : true,
|
|
1508
|
+
className: profileFormClasses.textarea,
|
|
1509
|
+
disabled,
|
|
1510
|
+
id: `${id}-bio`,
|
|
1511
|
+
maxLength: SUITE_PROFILE_BIO_MAX_LENGTH,
|
|
1512
|
+
onChange: (event) => {
|
|
1513
|
+
setBio(event.currentTarget.value);
|
|
1514
|
+
clearField("bio");
|
|
1515
|
+
},
|
|
1516
|
+
placeholder: "A short introduction.",
|
|
1517
|
+
rows: 3,
|
|
1518
|
+
value: bio
|
|
1519
|
+
}),
|
|
1520
|
+
fieldError("bio")
|
|
1521
|
+
]
|
|
1522
|
+
}),
|
|
1523
|
+
/* @__PURE__ */ jsxs("details", {
|
|
1524
|
+
className: `suite-profile-links ${profileFormClasses.links}`,
|
|
1525
|
+
onToggle: (event) => {
|
|
1526
|
+
setLinksOpen(event.currentTarget.open);
|
|
1527
|
+
},
|
|
1528
|
+
open: linksOpen,
|
|
1529
|
+
children: [
|
|
1530
|
+
/* @__PURE__ */ jsxs("summary", {
|
|
1531
|
+
className: profileFormClasses.linksSummary,
|
|
1532
|
+
children: [
|
|
1533
|
+
"Links ",
|
|
1534
|
+
/* @__PURE__ */ jsx("span", {
|
|
1535
|
+
className: profileFormClasses.linksHint,
|
|
1536
|
+
children: "optional; a handle is enough"
|
|
1537
|
+
})
|
|
1538
|
+
]
|
|
1539
|
+
}),
|
|
1540
|
+
/* @__PURE__ */ jsx("div", {
|
|
1541
|
+
className: profileFormClasses.linksBody,
|
|
1542
|
+
children: LINK_FIELDS.map(([key, label]) => {
|
|
1543
|
+
const affordance = SUITE_PROFILE_LINK_AFFORDANCES[key];
|
|
1544
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1545
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1546
|
+
children: [
|
|
1547
|
+
/* @__PURE__ */ jsx("label", {
|
|
1548
|
+
className: profileFormClasses.label,
|
|
1549
|
+
htmlFor: `${id}-${key}`,
|
|
1550
|
+
children: label
|
|
1551
|
+
}),
|
|
1552
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1553
|
+
className: profileFormClasses.affix,
|
|
1554
|
+
children: [
|
|
1555
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1556
|
+
"aria-hidden": "true",
|
|
1557
|
+
className: profileFormClasses.affixLead,
|
|
1558
|
+
children: [
|
|
1559
|
+
/* @__PURE__ */ jsx(LinkMark, {
|
|
1560
|
+
glyph: affordance.glyph
|
|
1561
|
+
}),
|
|
1562
|
+
/* @__PURE__ */ jsx("span", {
|
|
1563
|
+
children: affordance.prefix
|
|
1564
|
+
})
|
|
1565
|
+
]
|
|
1566
|
+
}),
|
|
1567
|
+
/* @__PURE__ */ jsx("input", {
|
|
1568
|
+
"aria-describedby": describedBy(key),
|
|
1569
|
+
"aria-invalid": fieldErrors[key] === undefined ? undefined : true,
|
|
1570
|
+
autoCapitalize: "none",
|
|
1571
|
+
autoComplete: "off",
|
|
1572
|
+
className: profileFormClasses.affixInput,
|
|
1573
|
+
disabled,
|
|
1574
|
+
id: `${id}-${key}`,
|
|
1575
|
+
inputMode: "url",
|
|
1576
|
+
maxLength: SUITE_PROFILE_URL_MAX_LENGTH,
|
|
1577
|
+
onChange: (event) => {
|
|
1578
|
+
const value = event.currentTarget.value;
|
|
1579
|
+
setLinks((current) => ({ ...current, [key]: value }));
|
|
1580
|
+
clearField(key);
|
|
1581
|
+
},
|
|
1582
|
+
placeholder: affordance.placeholder,
|
|
1583
|
+
spellCheck: false,
|
|
1584
|
+
type: "text",
|
|
1585
|
+
value: links[key]
|
|
1586
|
+
})
|
|
1587
|
+
]
|
|
1588
|
+
}),
|
|
1589
|
+
fieldError(key)
|
|
1590
|
+
]
|
|
1591
|
+
}, key);
|
|
1592
|
+
})
|
|
1593
|
+
})
|
|
1594
|
+
]
|
|
1595
|
+
}),
|
|
1596
|
+
/* @__PURE__ */ jsxs("fieldset", {
|
|
1597
|
+
"aria-describedby": `${id}-visibility-note${fieldErrors.publication === undefined ? "" : ` ${id}-publication-error`}`,
|
|
1598
|
+
"aria-invalid": fieldErrors.publication === undefined ? undefined : true,
|
|
1599
|
+
className: `suite-profile-visibility ${profileFormClasses.visibility}`,
|
|
1600
|
+
disabled,
|
|
1601
|
+
id: `${id}-publication`,
|
|
1602
|
+
tabIndex: -1,
|
|
1603
|
+
children: [
|
|
1604
|
+
/* @__PURE__ */ jsx("legend", {
|
|
1605
|
+
className: profileFormClasses.label,
|
|
1606
|
+
children: "Profile visibility"
|
|
1607
|
+
}),
|
|
1608
|
+
/* @__PURE__ */ jsxs("p", {
|
|
1609
|
+
className: profileFormClasses.note,
|
|
1610
|
+
id: `${id}-visibility-note`,
|
|
1611
|
+
children: [
|
|
1612
|
+
unconfirmed ? "Publication status is unconfirmed." : currentProfile.publication === "published" ? `The saved profile for @${currentProfile.username} is public.` : "The saved profile is private.",
|
|
1613
|
+
" Your choice takes effect when you save."
|
|
1614
|
+
]
|
|
1615
|
+
}),
|
|
1616
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1617
|
+
className: profileFormClasses.choice,
|
|
1618
|
+
children: [
|
|
1619
|
+
/* @__PURE__ */ jsx("input", {
|
|
1620
|
+
checked: visibility === "private",
|
|
1621
|
+
className: profileFormClasses.radio,
|
|
1622
|
+
name: "publication",
|
|
1623
|
+
onChange: () => {
|
|
1624
|
+
setVisibility("private");
|
|
1625
|
+
clearField("publication");
|
|
1626
|
+
},
|
|
1627
|
+
required: true,
|
|
1628
|
+
type: "radio",
|
|
1629
|
+
value: "private"
|
|
1630
|
+
}),
|
|
1631
|
+
/* @__PURE__ */ jsx("span", {
|
|
1632
|
+
children: "Keep private"
|
|
1633
|
+
})
|
|
1634
|
+
]
|
|
1635
|
+
}),
|
|
1636
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1637
|
+
className: profileFormClasses.choice,
|
|
1638
|
+
children: [
|
|
1639
|
+
/* @__PURE__ */ jsx("input", {
|
|
1640
|
+
"aria-describedby": `${id}-publication-note`,
|
|
1641
|
+
checked: visibility === "publish",
|
|
1642
|
+
className: profileFormClasses.radio,
|
|
1643
|
+
disabled: profile.username === null,
|
|
1644
|
+
name: "publication",
|
|
1645
|
+
onChange: () => {
|
|
1646
|
+
setVisibility("publish");
|
|
1647
|
+
clearField("publication");
|
|
1648
|
+
},
|
|
1649
|
+
required: true,
|
|
1650
|
+
type: "radio",
|
|
1651
|
+
value: "publish"
|
|
1652
|
+
}),
|
|
1653
|
+
/* @__PURE__ */ jsx("span", {
|
|
1654
|
+
children: "Publish profile"
|
|
1655
|
+
})
|
|
1656
|
+
]
|
|
1657
|
+
}),
|
|
1658
|
+
/* @__PURE__ */ jsx("p", {
|
|
1659
|
+
className: profileFormClasses.note,
|
|
1660
|
+
id: `${id}-publication-note`,
|
|
1661
|
+
children: profile.username === null ? "Choose a username in account settings before publishing. You can still save privately." : "Anyone can read your published name, bio, social links, and profile image. Your sign-in email is not included."
|
|
1662
|
+
}),
|
|
1663
|
+
fieldError("publication")
|
|
1664
|
+
]
|
|
1665
|
+
}),
|
|
1666
|
+
/* @__PURE__ */ jsx("button", {
|
|
1667
|
+
className: profileFormClasses.button,
|
|
1668
|
+
disabled,
|
|
1669
|
+
type: "submit",
|
|
1670
|
+
children: action
|
|
1671
|
+
}),
|
|
1672
|
+
/* @__PURE__ */ jsx("p", {
|
|
1673
|
+
"aria-live": "polite",
|
|
1674
|
+
className: profileFormClasses.note,
|
|
1675
|
+
role: "status",
|
|
1676
|
+
children: notice
|
|
1677
|
+
})
|
|
1678
|
+
]
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
// src/profile-form.tsx
|
|
1682
|
+
import {
|
|
1683
|
+
useId as useId2,
|
|
1684
|
+
useState as useState2
|
|
1685
|
+
} from "react";
|
|
1686
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1687
|
+
function withoutFieldError(errors, field) {
|
|
1688
|
+
const next = { ...errors };
|
|
1689
|
+
delete next[field];
|
|
1690
|
+
return next;
|
|
1691
|
+
}
|
|
1692
|
+
var LINK_FIELDS2 = [
|
|
1693
|
+
["x", "X"],
|
|
1694
|
+
["linkedin", "LinkedIn"],
|
|
1695
|
+
["bluesky", "Bluesky"],
|
|
1696
|
+
["instagram", "Instagram"],
|
|
1697
|
+
["telegram", "Telegram"],
|
|
1698
|
+
["website", "Personal website"]
|
|
1699
|
+
];
|
|
1700
|
+
function linkInputs2(profile) {
|
|
1701
|
+
return {
|
|
1702
|
+
bluesky: profile.links.bluesky ?? "",
|
|
1703
|
+
instagram: profile.links.instagram ?? "",
|
|
1704
|
+
linkedin: profile.links.linkedin ?? "",
|
|
1705
|
+
telegram: profile.links.telegram ?? "",
|
|
1706
|
+
website: profile.links.website ?? "",
|
|
1707
|
+
x: profile.links.x ?? ""
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
function issueMessage2(issue2) {
|
|
1711
|
+
if (issue2.field === "name") {
|
|
1712
|
+
return issue2.reason === "required" ? "Enter a name." : `Use ${SUITE_PROFILE_NAME_MAX_LENGTH} characters or fewer.`;
|
|
1713
|
+
}
|
|
1714
|
+
if (issue2.field === "bio") {
|
|
1715
|
+
return `Use ${SUITE_PROFILE_BIO_MAX_LENGTH} characters or fewer.`;
|
|
1716
|
+
}
|
|
1717
|
+
if (issue2.field === "linkedin")
|
|
1718
|
+
return "Enter a LinkedIn profile URL.";
|
|
1719
|
+
if (issue2.field === "website")
|
|
1720
|
+
return "Enter a complete HTTPS URL.";
|
|
1721
|
+
if (issue2.field === "x" || issue2.field === "bluesky" || issue2.field === "instagram" || issue2.field === "telegram") {
|
|
1722
|
+
const label = LINK_FIELDS2.find(([key]) => key === issue2.field)?.[1] ?? "profile";
|
|
1723
|
+
return `Enter a valid ${label} handle or profile URL.`;
|
|
1724
|
+
}
|
|
1725
|
+
return "Check the profile and try again.";
|
|
1726
|
+
}
|
|
1727
|
+
function applyProfile(profile, setters) {
|
|
1728
|
+
setters.setBio(profile.bio);
|
|
1729
|
+
setters.setEmail(profile.email);
|
|
1730
|
+
setters.setLinks(linkInputs2(profile));
|
|
1731
|
+
setters.setName(profile.name);
|
|
1732
|
+
setters.setRevision(profile.revision);
|
|
1733
|
+
}
|
|
1734
|
+
function SuiteProfileForm({
|
|
1735
|
+
className,
|
|
1736
|
+
initialProfile,
|
|
1737
|
+
onSave,
|
|
1738
|
+
onSaved,
|
|
1739
|
+
submitLabel
|
|
1740
|
+
}) {
|
|
1741
|
+
const id = useId2();
|
|
1742
|
+
const [name, setName] = useState2(initialProfile.name);
|
|
1743
|
+
const [email, setEmail] = useState2(initialProfile.email);
|
|
1744
|
+
const [bio, setBio] = useState2(initialProfile.bio);
|
|
1745
|
+
const [links, setLinks] = useState2(() => linkInputs2(initialProfile));
|
|
1746
|
+
const [revision, setRevision] = useState2(initialProfile.revision);
|
|
1747
|
+
const [pending, setPending] = useState2(false);
|
|
1748
|
+
const [fieldErrors, setFieldErrors] = useState2({});
|
|
1749
|
+
const [formError, setFormError] = useState2(null);
|
|
1750
|
+
async function submit(event) {
|
|
1751
|
+
event.preventDefault();
|
|
1752
|
+
if (pending)
|
|
1753
|
+
return;
|
|
1754
|
+
setFieldErrors({});
|
|
1755
|
+
setFormError(null);
|
|
1756
|
+
const parsed = parseSuiteProfileUpdateRequest({
|
|
1757
|
+
bio,
|
|
1758
|
+
expectedRevision: revision,
|
|
1759
|
+
links,
|
|
1760
|
+
name
|
|
1761
|
+
});
|
|
1762
|
+
if (!parsed.ok) {
|
|
1763
|
+
setFieldErrors({
|
|
1764
|
+
[parsed.error.field]: issueMessage2(parsed.error)
|
|
1765
|
+
});
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
setPending(true);
|
|
1769
|
+
try {
|
|
1770
|
+
const result = await onSave(parsed.value);
|
|
1771
|
+
applyProfile(result.profile, {
|
|
1772
|
+
setBio,
|
|
1773
|
+
setEmail,
|
|
1774
|
+
setLinks,
|
|
1775
|
+
setName,
|
|
1776
|
+
setRevision
|
|
1777
|
+
});
|
|
1778
|
+
if (result.status === "conflict") {
|
|
1779
|
+
setFormError("The profile changed elsewhere. Review it and save again.");
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
onSaved?.(result.profile);
|
|
1783
|
+
} catch {
|
|
1784
|
+
setFormError("The profile could not be saved. Try again.");
|
|
1785
|
+
} finally {
|
|
1786
|
+
setPending(false);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
const formClassName = className === undefined ? `suite-profile-form ${profileFormClasses.form}` : `suite-profile-form ${profileFormClasses.form} ${className}`;
|
|
1790
|
+
return /* @__PURE__ */ jsxs2("form", {
|
|
1791
|
+
"aria-busy": pending,
|
|
1792
|
+
className: formClassName,
|
|
1793
|
+
onSubmit: (event) => {
|
|
1794
|
+
submit(event);
|
|
1795
|
+
},
|
|
1796
|
+
children: [
|
|
1797
|
+
/* @__PURE__ */ jsxs2("label", {
|
|
1798
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1799
|
+
htmlFor: `${id}-name`,
|
|
1800
|
+
children: [
|
|
1801
|
+
/* @__PURE__ */ jsx2("span", {
|
|
1802
|
+
className: profileFormClasses.label,
|
|
1803
|
+
children: "Name"
|
|
1804
|
+
}),
|
|
1805
|
+
/* @__PURE__ */ jsx2("input", {
|
|
1806
|
+
className: profileFormClasses.control,
|
|
1807
|
+
"aria-describedby": fieldErrors.name === undefined ? undefined : `${id}-name-error`,
|
|
1808
|
+
"aria-invalid": fieldErrors.name === undefined ? undefined : "true",
|
|
1809
|
+
autoComplete: "name",
|
|
1810
|
+
disabled: pending,
|
|
1811
|
+
id: `${id}-name`,
|
|
1812
|
+
maxLength: SUITE_PROFILE_NAME_MAX_LENGTH,
|
|
1813
|
+
onChange: (event) => {
|
|
1814
|
+
setName(event.currentTarget.value);
|
|
1815
|
+
setFieldErrors((errors) => withoutFieldError(errors, "name"));
|
|
1816
|
+
},
|
|
1817
|
+
required: true,
|
|
1818
|
+
type: "text",
|
|
1819
|
+
value: name
|
|
1820
|
+
}),
|
|
1821
|
+
fieldErrors.name === undefined ? null : /* @__PURE__ */ jsx2("span", {
|
|
1822
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1823
|
+
id: `${id}-name-error`,
|
|
1824
|
+
role: "alert",
|
|
1825
|
+
children: fieldErrors.name
|
|
1826
|
+
})
|
|
1827
|
+
]
|
|
1828
|
+
}),
|
|
1829
|
+
/* @__PURE__ */ jsxs2("label", {
|
|
1830
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1831
|
+
htmlFor: `${id}-email`,
|
|
1832
|
+
children: [
|
|
1833
|
+
/* @__PURE__ */ jsx2("span", {
|
|
1834
|
+
className: profileFormClasses.label,
|
|
1835
|
+
children: "Email"
|
|
1836
|
+
}),
|
|
1837
|
+
/* @__PURE__ */ jsx2("input", {
|
|
1838
|
+
className: profileFormClasses.email,
|
|
1839
|
+
"aria-readonly": "true",
|
|
1840
|
+
autoComplete: "email",
|
|
1841
|
+
id: `${id}-email`,
|
|
1842
|
+
readOnly: true,
|
|
1843
|
+
type: "email",
|
|
1844
|
+
value: email
|
|
1845
|
+
})
|
|
1846
|
+
]
|
|
1847
|
+
}),
|
|
1848
|
+
/* @__PURE__ */ jsxs2("label", {
|
|
1849
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1850
|
+
htmlFor: `${id}-bio`,
|
|
1851
|
+
children: [
|
|
1852
|
+
/* @__PURE__ */ jsx2("span", {
|
|
1853
|
+
className: profileFormClasses.label,
|
|
1854
|
+
children: "Bio"
|
|
1855
|
+
}),
|
|
1856
|
+
/* @__PURE__ */ jsx2("textarea", {
|
|
1857
|
+
className: profileFormClasses.textarea,
|
|
1858
|
+
"aria-describedby": fieldErrors.bio === undefined ? undefined : `${id}-bio-error`,
|
|
1859
|
+
"aria-invalid": fieldErrors.bio === undefined ? undefined : "true",
|
|
1860
|
+
disabled: pending,
|
|
1861
|
+
id: `${id}-bio`,
|
|
1862
|
+
maxLength: SUITE_PROFILE_BIO_MAX_LENGTH,
|
|
1863
|
+
onChange: (event) => {
|
|
1864
|
+
setBio(event.currentTarget.value);
|
|
1865
|
+
setFieldErrors((errors) => withoutFieldError(errors, "bio"));
|
|
1866
|
+
},
|
|
1867
|
+
placeholder: "Introduce yourself",
|
|
1868
|
+
rows: 5,
|
|
1869
|
+
value: bio
|
|
1870
|
+
}),
|
|
1871
|
+
fieldErrors.bio === undefined ? null : /* @__PURE__ */ jsx2("span", {
|
|
1872
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1873
|
+
id: `${id}-bio-error`,
|
|
1874
|
+
role: "alert",
|
|
1875
|
+
children: fieldErrors.bio
|
|
1876
|
+
})
|
|
1877
|
+
]
|
|
1878
|
+
}),
|
|
1879
|
+
LINK_FIELDS2.map(([key, label]) => {
|
|
1880
|
+
const error = fieldErrors[key];
|
|
1881
|
+
return /* @__PURE__ */ jsxs2("label", {
|
|
1882
|
+
className: `suite-profile-field ${profileFormClasses.field}`,
|
|
1883
|
+
htmlFor: `${id}-${key}`,
|
|
1884
|
+
children: [
|
|
1885
|
+
/* @__PURE__ */ jsx2("span", {
|
|
1886
|
+
className: profileFormClasses.label,
|
|
1887
|
+
children: label
|
|
1888
|
+
}),
|
|
1889
|
+
/* @__PURE__ */ jsx2("input", {
|
|
1890
|
+
className: profileFormClasses.control,
|
|
1891
|
+
"aria-describedby": error === undefined ? undefined : `${id}-${key}-error`,
|
|
1892
|
+
"aria-invalid": error === undefined ? undefined : "true",
|
|
1893
|
+
autoCapitalize: "none",
|
|
1894
|
+
autoComplete: "url",
|
|
1895
|
+
disabled: pending,
|
|
1896
|
+
id: `${id}-${key}`,
|
|
1897
|
+
inputMode: "url",
|
|
1898
|
+
maxLength: 2048,
|
|
1899
|
+
onChange: (event) => {
|
|
1900
|
+
const value = event.currentTarget.value;
|
|
1901
|
+
setLinks((current) => ({ ...current, [key]: value }));
|
|
1902
|
+
setFieldErrors((errors) => withoutFieldError(errors, key));
|
|
1903
|
+
},
|
|
1904
|
+
spellCheck: false,
|
|
1905
|
+
type: "text",
|
|
1906
|
+
value: links[key]
|
|
1907
|
+
}),
|
|
1908
|
+
error === undefined ? null : /* @__PURE__ */ jsx2("span", {
|
|
1909
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1910
|
+
id: `${id}-${key}-error`,
|
|
1911
|
+
role: "alert",
|
|
1912
|
+
children: error
|
|
1913
|
+
})
|
|
1914
|
+
]
|
|
1915
|
+
}, key);
|
|
1916
|
+
}),
|
|
1917
|
+
formError === null ? null : /* @__PURE__ */ jsx2("p", {
|
|
1918
|
+
className: `suite-profile-error ${profileFormClasses.error}`,
|
|
1919
|
+
role: "alert",
|
|
1920
|
+
children: formError
|
|
1921
|
+
}),
|
|
1922
|
+
/* @__PURE__ */ jsx2("button", {
|
|
1923
|
+
className: profileFormClasses.button,
|
|
1924
|
+
disabled: pending,
|
|
1925
|
+
type: "submit",
|
|
1926
|
+
children: submitLabel
|
|
1927
|
+
})
|
|
1928
|
+
]
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
export {
|
|
1932
|
+
SuitePublicProfileForm,
|
|
1933
|
+
SuiteProfileForm
|
|
1934
|
+
};
|