@ascorbic/pds 0.0.1 → 0.1.0
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/README.md +1 -6
- package/dist/cli.js +135 -137
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2809 -569
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -8,12 +8,7 @@ A single-user [AT Protocol](https://atproto.com) Personal Data Server (PDS) that
|
|
|
8
8
|
|
|
9
9
|
## What is this?
|
|
10
10
|
|
|
11
|
-
A PDS is where your Bluesky data lives
|
|
12
|
-
|
|
13
|
-
- **Your own identity** - Use your own domain as your Bluesky handle
|
|
14
|
-
- **Data ownership** - Your content lives in your Cloudflare account
|
|
15
|
-
- **Federation** - Works with the Bluesky network via relay sync
|
|
16
|
-
- **Low cost** - Runs on Cloudflare's generous free tier
|
|
11
|
+
A PDS is where your Bluesky data lives – your posts, follows, profile, and media. This package lets you run your own PDS on Cloudflare Workers, giving you control over your data and identity.
|
|
17
12
|
|
|
18
13
|
## Quick Start
|
|
19
14
|
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
|
-
import { randomBytes } from "node:crypto";
|
|
4
3
|
import * as p from "@clack/prompts";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
import { Secp256k1Keypair } from "@atproto/crypto";
|
|
6
|
+
import bcrypt from "bcryptjs";
|
|
5
7
|
import { spawn } from "node:child_process";
|
|
6
8
|
import { experimental_patchConfig, experimental_readRawConfig } from "wrangler";
|
|
7
9
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
10
|
import { resolve } from "node:path";
|
|
9
|
-
import bcrypt from "bcryptjs";
|
|
10
|
-
import { Secp256k1Keypair } from "@atproto/crypto";
|
|
11
11
|
|
|
12
12
|
//#region src/cli/utils/wrangler.ts
|
|
13
13
|
/**
|
|
@@ -131,6 +131,80 @@ function setDevVar(key, value, dir = process.cwd()) {
|
|
|
131
131
|
writeDevVars(vars, dir);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/cli/utils/secrets.ts
|
|
136
|
+
/**
|
|
137
|
+
* Secret generation and management utilities for PDS CLI
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* Generate a new secp256k1 signing keypair
|
|
141
|
+
*/
|
|
142
|
+
async function generateSigningKeypair() {
|
|
143
|
+
const keypair = await Secp256k1Keypair.create({ exportable: true });
|
|
144
|
+
return {
|
|
145
|
+
privateKey: Buffer.from(await keypair.export()).toString("hex"),
|
|
146
|
+
publicKey: keypair.did().replace("did:key:", "")
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Derive public key from an existing private key
|
|
151
|
+
*/
|
|
152
|
+
async function derivePublicKey(privateKeyHex) {
|
|
153
|
+
return (await Secp256k1Keypair.import(privateKeyHex)).did().replace("did:key:", "");
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate a random auth token (base64url, 32 bytes)
|
|
157
|
+
*/
|
|
158
|
+
function generateAuthToken() {
|
|
159
|
+
return randomBytes(32).toString("base64url");
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Generate a random JWT secret (base64, 32 bytes)
|
|
163
|
+
*/
|
|
164
|
+
function generateJwtSecret() {
|
|
165
|
+
return randomBytes(32).toString("base64");
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Hash a password using bcrypt
|
|
169
|
+
*/
|
|
170
|
+
async function hashPassword(password) {
|
|
171
|
+
return bcrypt.hash(password, 10);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Prompt for password with confirmation
|
|
175
|
+
*/
|
|
176
|
+
async function promptPassword() {
|
|
177
|
+
const password = await p.password({ message: "Enter password:" });
|
|
178
|
+
if (p.isCancel(password)) {
|
|
179
|
+
p.cancel("Cancelled");
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
182
|
+
const confirm = await p.password({ message: "Confirm password:" });
|
|
183
|
+
if (p.isCancel(confirm)) {
|
|
184
|
+
p.cancel("Cancelled");
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
if (password !== confirm) {
|
|
188
|
+
p.log.error("Passwords do not match");
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
return password;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Set a secret value, either locally (.dev.vars) or via wrangler
|
|
195
|
+
*/
|
|
196
|
+
async function setSecretValue(name, value, local) {
|
|
197
|
+
if (local) setDevVar(name, value);
|
|
198
|
+
else await setSecret(name, value);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Set a public var in wrangler.jsonc
|
|
202
|
+
*/
|
|
203
|
+
function setPublicVar(name, value, local) {
|
|
204
|
+
if (local) setDevVar(name, value);
|
|
205
|
+
else setVar(name, value);
|
|
206
|
+
}
|
|
207
|
+
|
|
134
208
|
//#endregion
|
|
135
209
|
//#region src/cli/commands/secret/jwt.ts
|
|
136
210
|
/**
|
|
@@ -148,22 +222,13 @@ const jwtCommand = defineCommand({
|
|
|
148
222
|
} },
|
|
149
223
|
async run({ args }) {
|
|
150
224
|
p.intro("Generate JWT Secret");
|
|
151
|
-
const secret =
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
p.outro("JWT_SECRET written to .dev.vars");
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
try {
|
|
159
|
-
await setSecret("JWT_SECRET", secret);
|
|
160
|
-
spinner.stop("JWT_SECRET set successfully");
|
|
161
|
-
p.outro("Done!");
|
|
162
|
-
} catch (error) {
|
|
163
|
-
spinner.stop("Failed to set JWT_SECRET");
|
|
164
|
-
p.log.error(String(error));
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
225
|
+
const secret = generateJwtSecret();
|
|
226
|
+
try {
|
|
227
|
+
await setSecretValue("JWT_SECRET", secret, args.local);
|
|
228
|
+
p.outro(args.local ? "JWT_SECRET written to .dev.vars" : "Done!");
|
|
229
|
+
} catch (error) {
|
|
230
|
+
p.log.error(String(error));
|
|
231
|
+
process.exit(1);
|
|
167
232
|
}
|
|
168
233
|
}
|
|
169
234
|
});
|
|
@@ -185,38 +250,17 @@ const passwordCommand = defineCommand({
|
|
|
185
250
|
} },
|
|
186
251
|
async run({ args }) {
|
|
187
252
|
p.intro("Set Account Password");
|
|
188
|
-
const password = await
|
|
189
|
-
if (p.isCancel(password)) {
|
|
190
|
-
p.cancel("Cancelled");
|
|
191
|
-
process.exit(0);
|
|
192
|
-
}
|
|
193
|
-
const confirm = await p.password({ message: "Confirm password:" });
|
|
194
|
-
if (p.isCancel(confirm)) {
|
|
195
|
-
p.cancel("Cancelled");
|
|
196
|
-
process.exit(0);
|
|
197
|
-
}
|
|
198
|
-
if (password !== confirm) {
|
|
199
|
-
p.log.error("Passwords do not match");
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}
|
|
253
|
+
const password = await promptPassword();
|
|
202
254
|
const spinner = p.spinner();
|
|
203
255
|
spinner.start("Hashing password...");
|
|
204
|
-
const passwordHash = await
|
|
256
|
+
const passwordHash = await hashPassword(password);
|
|
205
257
|
spinner.stop("Password hashed");
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
p.outro("PASSWORD_HASH written to .dev.vars");
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
await setSecret("PASSWORD_HASH", passwordHash);
|
|
213
|
-
spinner.stop("PASSWORD_HASH set successfully");
|
|
214
|
-
p.outro("Done!");
|
|
215
|
-
} catch (error) {
|
|
216
|
-
spinner.stop("Failed to set PASSWORD_HASH");
|
|
217
|
-
p.log.error(String(error));
|
|
218
|
-
process.exit(1);
|
|
219
|
-
}
|
|
258
|
+
try {
|
|
259
|
+
await setSecretValue("PASSWORD_HASH", passwordHash, args.local);
|
|
260
|
+
p.outro(args.local ? "PASSWORD_HASH written to .dev.vars" : "Done!");
|
|
261
|
+
} catch (error) {
|
|
262
|
+
p.log.error(String(error));
|
|
263
|
+
process.exit(1);
|
|
220
264
|
}
|
|
221
265
|
}
|
|
222
266
|
});
|
|
@@ -240,31 +284,17 @@ const keyCommand = defineCommand({
|
|
|
240
284
|
p.intro("Generate Signing Keypair");
|
|
241
285
|
const spinner = p.spinner();
|
|
242
286
|
spinner.start("Generating secp256k1 keypair...");
|
|
243
|
-
const
|
|
244
|
-
const privateKeyJwk = await keypair.export();
|
|
245
|
-
const publicKeyMultibase = keypair.did().replace("did:key:", "");
|
|
287
|
+
const { privateKey, publicKey } = await generateSigningKeypair();
|
|
246
288
|
spinner.stop("Keypair generated");
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
p.outro("SIGNING_KEY and SIGNING_KEY_PUBLIC written to .dev.vars");
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
await setSecret("SIGNING_KEY", privateKeyJson);
|
|
256
|
-
spinner.stop("SIGNING_KEY set");
|
|
257
|
-
spinner.start("Setting SIGNING_KEY_PUBLIC in wrangler.jsonc...");
|
|
258
|
-
setVar("SIGNING_KEY_PUBLIC", publicKeyMultibase);
|
|
259
|
-
spinner.stop("SIGNING_KEY_PUBLIC set");
|
|
260
|
-
p.outro("Done!");
|
|
261
|
-
} catch (error) {
|
|
262
|
-
spinner.stop("Failed");
|
|
263
|
-
p.log.error(String(error));
|
|
264
|
-
process.exit(1);
|
|
265
|
-
}
|
|
289
|
+
try {
|
|
290
|
+
await setSecretValue("SIGNING_KEY", privateKey, args.local);
|
|
291
|
+
setPublicVar("SIGNING_KEY_PUBLIC", publicKey, args.local);
|
|
292
|
+
p.log.info("Public key (for DID document): " + publicKey);
|
|
293
|
+
p.outro(args.local ? "SIGNING_KEY and SIGNING_KEY_PUBLIC written to .dev.vars" : "Done!");
|
|
294
|
+
} catch (error) {
|
|
295
|
+
p.log.error(String(error));
|
|
296
|
+
process.exit(1);
|
|
266
297
|
}
|
|
267
|
-
p.log.info("Public key (for DID document): " + publicKeyMultibase);
|
|
268
298
|
}
|
|
269
299
|
});
|
|
270
300
|
|
|
@@ -295,10 +325,20 @@ const secretCommand = defineCommand({
|
|
|
295
325
|
*/
|
|
296
326
|
function runWranglerTypes() {
|
|
297
327
|
return new Promise((resolve$1, reject) => {
|
|
298
|
-
const child = spawn("wrangler", ["types"], { stdio: "
|
|
328
|
+
const child = spawn("wrangler", ["types"], { stdio: "pipe" });
|
|
329
|
+
let output = "";
|
|
330
|
+
child.stdout?.on("data", (data) => {
|
|
331
|
+
output += data.toString();
|
|
332
|
+
});
|
|
333
|
+
child.stderr?.on("data", (data) => {
|
|
334
|
+
output += data.toString();
|
|
335
|
+
});
|
|
299
336
|
child.on("close", (code) => {
|
|
300
337
|
if (code === 0) resolve$1();
|
|
301
|
-
else
|
|
338
|
+
else {
|
|
339
|
+
if (output) console.error(output);
|
|
340
|
+
reject(/* @__PURE__ */ new Error(`wrangler types failed with code ${code}`));
|
|
341
|
+
}
|
|
302
342
|
});
|
|
303
343
|
child.on("error", reject);
|
|
304
344
|
});
|
|
@@ -366,72 +406,45 @@ const initCommand = defineCommand({
|
|
|
366
406
|
if (isProduction) {
|
|
367
407
|
authToken = await getOrGenerateSecret("AUTH_TOKEN", devVars, async () => {
|
|
368
408
|
spinner.start("Generating auth token...");
|
|
369
|
-
const token =
|
|
409
|
+
const token = generateAuthToken();
|
|
370
410
|
spinner.stop("Auth token generated");
|
|
371
411
|
return token;
|
|
372
412
|
});
|
|
373
413
|
signingKey = await getOrGenerateSecret("SIGNING_KEY", devVars, async () => {
|
|
374
414
|
spinner.start("Generating signing keypair...");
|
|
375
|
-
const
|
|
376
|
-
const key = JSON.stringify(await keypair.export());
|
|
415
|
+
const { privateKey } = await generateSigningKeypair();
|
|
377
416
|
spinner.stop("Signing keypair generated");
|
|
378
|
-
return
|
|
417
|
+
return privateKey;
|
|
379
418
|
});
|
|
380
|
-
signingKeyPublic =
|
|
419
|
+
signingKeyPublic = await derivePublicKey(signingKey);
|
|
381
420
|
jwtSecret = await getOrGenerateSecret("JWT_SECRET", devVars, async () => {
|
|
382
421
|
spinner.start("Generating JWT secret...");
|
|
383
|
-
const secret =
|
|
422
|
+
const secret = generateJwtSecret();
|
|
384
423
|
spinner.stop("JWT secret generated");
|
|
385
424
|
return secret;
|
|
386
425
|
});
|
|
387
426
|
passwordHash = await getOrGenerateSecret("PASSWORD_HASH", devVars, async () => {
|
|
388
|
-
const password = await
|
|
389
|
-
if (p.isCancel(password)) {
|
|
390
|
-
p.cancel("Cancelled");
|
|
391
|
-
process.exit(0);
|
|
392
|
-
}
|
|
393
|
-
const confirm = await p.password({ message: "Confirm password:" });
|
|
394
|
-
if (p.isCancel(confirm)) {
|
|
395
|
-
p.cancel("Cancelled");
|
|
396
|
-
process.exit(0);
|
|
397
|
-
}
|
|
398
|
-
if (password !== confirm) {
|
|
399
|
-
p.log.error("Passwords do not match");
|
|
400
|
-
process.exit(1);
|
|
401
|
-
}
|
|
427
|
+
const password = await promptPassword();
|
|
402
428
|
spinner.start("Hashing password...");
|
|
403
|
-
const hash = await
|
|
429
|
+
const hash = await hashPassword(password);
|
|
404
430
|
spinner.stop("Password hashed");
|
|
405
431
|
return hash;
|
|
406
432
|
});
|
|
407
433
|
} else {
|
|
408
|
-
const password = await
|
|
409
|
-
if (p.isCancel(password)) {
|
|
410
|
-
p.cancel("Cancelled");
|
|
411
|
-
process.exit(0);
|
|
412
|
-
}
|
|
413
|
-
const confirm = await p.password({ message: "Confirm password:" });
|
|
414
|
-
if (p.isCancel(confirm)) {
|
|
415
|
-
p.cancel("Cancelled");
|
|
416
|
-
process.exit(0);
|
|
417
|
-
}
|
|
418
|
-
if (password !== confirm) {
|
|
419
|
-
p.log.error("Passwords do not match");
|
|
420
|
-
process.exit(1);
|
|
421
|
-
}
|
|
434
|
+
const password = await promptPassword();
|
|
422
435
|
spinner.start("Hashing password...");
|
|
423
|
-
passwordHash = await
|
|
436
|
+
passwordHash = await hashPassword(password);
|
|
424
437
|
spinner.stop("Password hashed");
|
|
425
438
|
spinner.start("Generating JWT secret...");
|
|
426
|
-
jwtSecret =
|
|
439
|
+
jwtSecret = generateJwtSecret();
|
|
427
440
|
spinner.stop("JWT secret generated");
|
|
428
441
|
spinner.start("Generating auth token...");
|
|
429
|
-
authToken =
|
|
442
|
+
authToken = generateAuthToken();
|
|
430
443
|
spinner.stop("Auth token generated");
|
|
431
444
|
spinner.start("Generating signing keypair...");
|
|
432
|
-
const keypair = await
|
|
433
|
-
signingKey =
|
|
434
|
-
signingKeyPublic = keypair.
|
|
445
|
+
const keypair = await generateSigningKeypair();
|
|
446
|
+
signingKey = keypair.privateKey;
|
|
447
|
+
signingKeyPublic = keypair.publicKey;
|
|
435
448
|
spinner.stop("Signing keypair generated");
|
|
436
449
|
}
|
|
437
450
|
spinner.start("Updating wrangler.jsonc...");
|
|
@@ -442,29 +455,14 @@ const initCommand = defineCommand({
|
|
|
442
455
|
SIGNING_KEY_PUBLIC: signingKeyPublic
|
|
443
456
|
});
|
|
444
457
|
spinner.stop("wrangler.jsonc updated");
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
await setSecret("JWT_SECRET", jwtSecret);
|
|
454
|
-
spinner.stop("JWT_SECRET set");
|
|
455
|
-
spinner.start("Setting PASSWORD_HASH...");
|
|
456
|
-
await setSecret("PASSWORD_HASH", passwordHash);
|
|
457
|
-
spinner.stop("PASSWORD_HASH set");
|
|
458
|
-
} else {
|
|
459
|
-
spinner.start("Writing secrets to .dev.vars...");
|
|
460
|
-
writeDevVars({
|
|
461
|
-
AUTH_TOKEN: authToken,
|
|
462
|
-
SIGNING_KEY: signingKey,
|
|
463
|
-
JWT_SECRET: jwtSecret,
|
|
464
|
-
PASSWORD_HASH: passwordHash
|
|
465
|
-
});
|
|
466
|
-
spinner.stop("Secrets written to .dev.vars");
|
|
467
|
-
}
|
|
458
|
+
const local = !isProduction;
|
|
459
|
+
if (isProduction) spinner.start("Deploying secrets to Cloudflare...");
|
|
460
|
+
else spinner.start("Writing secrets to .dev.vars...");
|
|
461
|
+
await setSecretValue("AUTH_TOKEN", authToken, local);
|
|
462
|
+
await setSecretValue("SIGNING_KEY", signingKey, local);
|
|
463
|
+
await setSecretValue("JWT_SECRET", jwtSecret, local);
|
|
464
|
+
await setSecretValue("PASSWORD_HASH", passwordHash, local);
|
|
465
|
+
spinner.stop(isProduction ? "Secrets deployed" : "Secrets written to .dev.vars");
|
|
468
466
|
spinner.start("Generating TypeScript types...");
|
|
469
467
|
try {
|
|
470
468
|
await runWranglerTypes();
|
package/dist/index.d.ts
CHANGED
|
@@ -79,6 +79,14 @@ declare class SqliteRepoStorage extends ReadableBlockstore implements RepoStorag
|
|
|
79
79
|
* Count the number of blocks stored.
|
|
80
80
|
*/
|
|
81
81
|
countBlocks(): Promise<number>;
|
|
82
|
+
/**
|
|
83
|
+
* Get user preferences.
|
|
84
|
+
*/
|
|
85
|
+
getPreferences(): Promise<unknown[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Update user preferences.
|
|
88
|
+
*/
|
|
89
|
+
putPreferences(preferences: unknown[]): Promise<void>;
|
|
82
90
|
}
|
|
83
91
|
//#endregion
|
|
84
92
|
//#region src/blobs.d.ts
|
|
@@ -310,6 +318,16 @@ declare class AccountDurableObject extends DurableObject<PDSEnv> {
|
|
|
310
318
|
* WebSocket error handler (hibernation API).
|
|
311
319
|
*/
|
|
312
320
|
webSocketError(_ws: WebSocket, error: Error): void;
|
|
321
|
+
/**
|
|
322
|
+
* RPC method: Get user preferences
|
|
323
|
+
*/
|
|
324
|
+
rpcGetPreferences(): Promise<{
|
|
325
|
+
preferences: unknown[];
|
|
326
|
+
}>;
|
|
327
|
+
/**
|
|
328
|
+
* RPC method: Put user preferences
|
|
329
|
+
*/
|
|
330
|
+
rpcPutPreferences(preferences: unknown[]): Promise<void>;
|
|
313
331
|
/**
|
|
314
332
|
* Emit an identity event to notify downstream services to refresh identity cache.
|
|
315
333
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/storage.ts","../src/blobs.ts","../src/types.ts","../src/account-do.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AAUA;AAI0B,cAJb,iBAAA,SACJ,kBAAA,YACG,WAEc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/storage.ts","../src/blobs.ts","../src/types.ts","../src/account-do.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AAUA;AAI0B,cAJb,iBAAA,SACJ,kBAAA,YACG,WAEc,CAAA;EAsDA,QAAA,GAAA;EAAR,WAAA,CAAA,GAAA,EAtDQ,UAsDR;EAaD;;;EA4BI,UAAA,CAAA,CAAA,EAAA,IAAA;EAAc;;;EAcb,OAAA,CAAA,CAAA,EAvDJ,OAuDI,CAvDI,GAuDJ,GAAA,IAAA,CAAA;EAUC;;;EAAQ,MAAA,CAAA,CAAA,EApDd,OAoDc,CAAA,MAAA,GAAA,IAAA,CAAA;EAmBV;;;EAYE,MAAA,CAAA,CAAA,EAzEN,OAyEM,CAAA,MAAA,CAAA;EAAwB;;;EA+BpB,OAAA,CAAA,CAAA,EA9FT,OA8FS,CAAA,MAAA,CAAA;EAAa;;;EAuDlB,QAAA,CAAA,GAAA,EA7ID,GA6IC,CAAA,EA7IK,OA6IL,CA7Ia,UA6Ib,GAAA,IAAA,CAAA;EAUG;;;EAxPb,GAAA,CAAA,GAAA,EA+GI,GA/GJ,CAAA,EA+GU,OA/GV,CAAA,OAAA,CAAA;EAAW;;;kBAyHA,QAAQ;IClId,MAAO,EDkIyB,QClIzB;aDkI4C;;;AE9HpE;;EAkBU,QAAA,CAAA,GAAA,EF+HW,GE/HX,EAAA,KAAA,EF+HuB,UE/HvB,EAAA,GAAA,EAAA,MAAA,CAAA,EF+HiD,OE/HjD,CAAA,IAAA,CAAA;EAED;;;kBFyIc,wBAAwB;;AGpI/C;;EASkB,UAAA,CAAA,GAAA,EH+IK,GG/IL,EAAA,GAAA,EAAA,MAAA,CAAA,EH+IwB,OG/IxB,CAAA,IAAA,CAAA;EAAyB;;;EA0EjB,WAAA,CAAA,MAAA,EHgFC,UGhFD,CAAA,EHgFc,OGhFd,CAAA,IAAA,CAAA;EAAR;;;EAgBG,WAAA,CAAA,CAAA,EHmGC,OGnGD,CAAA,MAAA,CAAA;EAAO;;;EAoCxB,OAAA,CAAA,CAAA,EHyEc,OGzEd,CAAA,IAAA,CAAA;EAuCO;;;EAuHP,WAAA,CAAA,CAAA,EH3EkB,OG2ElB,CAAA,MAAA,CAAA;EA8DA;;;EA2FA,cAAA,CAAA,CAAA,EH1NqB,OG0NrB,CAAA,OAAA,EAAA,CAAA;EAgKuB;;;EA8CI,cAAA,CAAA,WAAA,EAAA,OAAA,EAAA,CAAA,EHtZgB,OGsZhB,CAAA,IAAA,CAAA;;;;UFzqBd,OAAA;;;;;;;ADOjB;;;;;;;UEHiB,MAAA;;EFGJ,GAAA,EAAA,MAAA;EAIa;EAsDA,MAAA,EAAA,MAAA;EAAR;EAaD,YAAA,EAAA,MAAA;EAUA;EAUC,UAAA,EAAA,MAAA;EAQG;EAAc,WAAA,EAAA,MAAA;EAAR;EAcX,kBAAA,EAAA,MAAA;EAAM;EAUC,UAAA,EAAA,MAAA;EAA0B;EAAmB,aAAA,EAAA,MAAA;EAArC;EAmBV,OAAA,EE/HX,sBF+HW,CE/HY,oBF+HZ,CAAA;EAAY;EAA0B,KAAA,CAAA,EE7HlD,QF6HkD;;;;;;AA9I3D;;;;;;;AAmGqB,cG7ER,oBAAA,SAA6B,aH6ErB,CG7EmC,MH6EnC,CAAA,CAAA;EAAc,QAAA,OAAA;EAAR,QAAA,IAAA;EAcX,QAAA,OAAA;EAAM,QAAA,SAAA;EAUC,QAAA,SAAA;EAA0B,QAAA,kBAAA;EAAmB,QAAA,eAAA;EAArC,WAAA,CAAA,GAAA,EG5Fb,kBH4Fa,EAAA,GAAA,EG5FY,MH4FZ;EAmBV;;;EAYE,QAAA,wBAAA;EAAwB;;;EA+BpB,QAAA,qBAAA;EAAa;;;EAuDlB,UAAA,CAAA,CAAA,EG/ID,OH+IC,CG/IO,iBH+IP,CAAA;EAUG;;;EAxPb,OAAA,CAAA,CAAA,EGuGM,OHvGN,CGuGc,IHvGd,CAAA;EAAW;;;gBG+GF,QAAQ;EFxHZ;;;gBEgII,OAAO;ED5HX;;;EAoBR,eAAA,CAAA,CAAA,EC+GiB,OD/GjB,CAAA;IAAQ,GAAA,EAAA,MAAA;;;;ECKJ;;;EAS8B,YAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,CAAA,EA8HvC,OA9HuC,CAAA;IAkEd,GAAA,EAAA,MAAA;IAAR,MAAA,EA8DX,GAAA,CAAI,YA9DO,CAAA,GAAA,CAAA;EAQK,CAAA,GAAA,IAAA,CAAA;EAAR;;;EAgBG,cAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAAA;IAAO,KAAA,EAAA,MAAA;IAOF,MAAA,CAAA,EAAA,MAAA;IA+BZ,OAAA,CAAA,EAAA,OAAA;EAFV,CAAA,CAAA,EAsCA,OAtCA,CAAA;IAuCO,OAAA,EAAA,KAAA,CAAA;MADP,GAAA,EAAA,MAAA;MA2CA,GAAA,EAAA,MAAA;MA6EA,KAAA,EAAA,OAAA;IA8DA,CAAA,CAAA;IAqFM,MAAA,CAAA,EAAA,MAAA;EAQC,CAAA,CAAA;EAFP;;;EAgLoB,eAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,GAAA,SAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAtZpB,OAsZoB,CAAA;IA8BO,GAAA,EAAA,MAAA;IAAa,GAAA,EAAA,MAAA;IAgDhB,MAAA,EAAA;MAAuC,GAAA,EAAA,MAAA;MAAR,GAAA,EAAA,MAAA;IAmBhB,CAAA;EAAR,CAAA,CAAA;EAwGG;;;EAoC/B,eAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,CAAA,EAtjBH,OAsjBG,CAAA;IACc,MAAA,EAAA;MASd,GAAA,EAAA,MAAA;MAWuB,GAAA,EAAA,MAAA;IAAkB,CAAA;EAOpB,CAAA,GAAA,IAAA,CAAA;EASsB;;;EA2DD,YAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAxlB7C,OAwlB6C,CAAA;IAAR,GAAA,EAAA,MAAA;IA37BC,GAAA,EAAA,MAAA;IAAa,MAAA,EAAA;;;;ICkCjD,gBAAsC,EAAA,MAAA;EAAX,CAAA,CAAA;EAAM;;;yBDsZ7B;;;;;OAMN;;;;;aAEO;;;;;;;;;;sBA8JgB;;;;;;;;mBAgBH,QAAQ;;;;;;0BA8BD,aAAa;;;;;;;;uBAgDhB,+BAA+B,QAAQ;;;;8BAmBhC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;iCAwGL,UAAU,QAAQ;;;;wBAoCjD,8BACc;;;;sBASd;;;;sBAWuB,kBAAkB;;;;uBAOpB;;;;;;6CASsB;;;;wCAQL;;;;;;;iBAmDd,UAAU,QAAQ;;;;cCz5B3C,KAAG;YAAwB;GAAM,WAAA,CAAA,WAAA"}
|