@kavtuai/guildgate 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/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +399 -0
- package/README.tr.md +397 -0
- package/SECURITY.md +36 -0
- package/TEST_REPORT.md +50 -0
- package/bin/guildgate-doctor.mjs +91 -0
- package/bin/guildgate-writing-check.mjs +73 -0
- package/dist/action.d.ts +65 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +4 -0
- package/dist/action.js.map +1 -0
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +53 -0
- package/dist/cache.js.map +1 -0
- package/dist/crypto.d.ts +15 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +87 -0
- package/dist/crypto.js.map +1 -0
- package/dist/discord/authorizer.d.ts +41 -0
- package/dist/discord/authorizer.d.ts.map +1 -0
- package/dist/discord/authorizer.js +101 -0
- package/dist/discord/authorizer.js.map +1 -0
- package/dist/discord/oauth.d.ts +64 -0
- package/dist/discord/oauth.d.ts.map +1 -0
- package/dist/discord/oauth.js +226 -0
- package/dist/discord/oauth.js.map +1 -0
- package/dist/discord/permissions.d.ts +85 -0
- package/dist/discord/permissions.d.ts.map +1 -0
- package/dist/discord/permissions.js +99 -0
- package/dist/discord/permissions.js.map +1 -0
- package/dist/discord.d.ts +4 -0
- package/dist/discord.d.ts.map +1 -0
- package/dist/discord.js +4 -0
- package/dist/discord.js.map +1 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +57 -0
- package/dist/errors.js.map +1 -0
- package/dist/express.d.ts +3 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +3 -0
- package/dist/express.js.map +1 -0
- package/dist/fastify.d.ts +3 -0
- package/dist/fastify.d.ts.map +1 -0
- package/dist/fastify.js +3 -0
- package/dist/fastify.js.map +1 -0
- package/dist/http/express.d.ts +24 -0
- package/dist/http/express.d.ts.map +1 -0
- package/dist/http/express.js +60 -0
- package/dist/http/express.js.map +1 -0
- package/dist/http/fastify.d.ts +25 -0
- package/dist/http/fastify.d.ts.map +1 -0
- package/dist/http/fastify.js +57 -0
- package/dist/http/fastify.js.map +1 -0
- package/dist/i18n.d.ts +5 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +66 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +83 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +395 -0
- package/dist/kernel.js.map +1 -0
- package/dist/memory-store.d.ts +13 -0
- package/dist/memory-store.d.ts.map +1 -0
- package/dist/memory-store.js +261 -0
- package/dist/memory-store.js.map +1 -0
- package/dist/realtime/hub.d.ts +42 -0
- package/dist/realtime/hub.d.ts.map +1 -0
- package/dist/realtime/hub.js +152 -0
- package/dist/realtime/hub.js.map +1 -0
- package/dist/realtime/outbox.d.ts +12 -0
- package/dist/realtime/outbox.d.ts.map +1 -0
- package/dist/realtime/outbox.js +18 -0
- package/dist/realtime/outbox.js.map +1 -0
- package/dist/realtime.d.ts +3 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +3 -0
- package/dist/realtime.js.map +1 -0
- package/dist/redis.d.ts +49 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +233 -0
- package/dist/redis.js.map +1 -0
- package/dist/resilience.d.ts +23 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +85 -0
- package/dist/resilience.js.map +1 -0
- package/dist/security.d.ts +21 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +94 -0
- package/dist/security.js.map +1 -0
- package/dist/session.d.ts +32 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +97 -0
- package/dist/session.js.map +1 -0
- package/dist/stores.d.ts +73 -0
- package/dist/stores.d.ts.map +1 -0
- package/dist/stores.js +4 -0
- package/dist/stores.js.map +1 -0
- package/dist/testing.d.ts +32 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +60 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +183 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/docs/WRITING_STYLE.md +118 -0
- package/docs/architecture.md +83 -0
- package/docs/custom-stores.md +111 -0
- package/docs/owner-management.md +44 -0
- package/docs/research-notes.md +61 -0
- package/docs/setup.md +104 -0
- package/docs/threat-model.md +79 -0
- package/docs/tr/arastirma-notlari.md +41 -0
- package/docs/tr/guvenlik-bildirimi.md +7 -0
- package/docs/tr/kurulum.md +63 -0
- package/docs/tr/mimari.md +59 -0
- package/docs/tr/sahip-yonetimi.md +7 -0
- package/docs/tr/tehdit-modeli.md +39 -0
- package/docs/tr/veritabani-suruculeri.md +23 -0
- package/docs/tr/yazim-kilavuzu.md +116 -0
- package/docs/uml/action-sequence.mmd +39 -0
- package/docs/uml/components.mmd +84 -0
- package/docs/uml/deployment.mmd +19 -0
- package/docs/uml/oauth-sequence.mmd +30 -0
- package/docs/uml/realtime-sequence.mmd +28 -0
- package/package.json +90 -0
package/dist/action.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { CacheManager } from "./cache.js";
|
|
2
|
+
import type { GuildGateStores } from "./stores.js";
|
|
3
|
+
import type { RealtimeEvent, RequestEnvelope, ResourceRef, SessionRecord, SupportedLocale } from "./types.js";
|
|
4
|
+
export interface AuthorizationDecision {
|
|
5
|
+
allowed: boolean;
|
|
6
|
+
code?: string;
|
|
7
|
+
details?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface ActionContext {
|
|
10
|
+
requestId: string;
|
|
11
|
+
request: RequestEnvelope;
|
|
12
|
+
locale: SupportedLocale;
|
|
13
|
+
session: SessionRecord | null;
|
|
14
|
+
userId?: string;
|
|
15
|
+
isOwner: boolean;
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
stores: GuildGateStores;
|
|
18
|
+
cache: CacheManager;
|
|
19
|
+
now: Date;
|
|
20
|
+
}
|
|
21
|
+
export interface ActionRateLimit<I> {
|
|
22
|
+
limit: number;
|
|
23
|
+
windowMs: number;
|
|
24
|
+
cost?: number;
|
|
25
|
+
key?: (context: Omit<ActionContext, "signal">, input: I) => string;
|
|
26
|
+
}
|
|
27
|
+
export interface ActionIdempotency<I> {
|
|
28
|
+
required?: boolean;
|
|
29
|
+
ttlMs: number;
|
|
30
|
+
scope?: (context: Omit<ActionContext, "signal">, input: I) => string;
|
|
31
|
+
}
|
|
32
|
+
export interface ActionConcurrency<I> {
|
|
33
|
+
key: (context: Omit<ActionContext, "signal">, input: I) => string;
|
|
34
|
+
ttlMs?: number;
|
|
35
|
+
waitMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface ActionDefinition<I, O> {
|
|
38
|
+
name: string;
|
|
39
|
+
authentication?: "required" | "optional" | "none";
|
|
40
|
+
csrf?: "required" | "disabled";
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
parse?: (value: unknown) => I;
|
|
43
|
+
resource?: (input: I) => ResourceRef | undefined;
|
|
44
|
+
rateLimit?: ActionRateLimit<I>;
|
|
45
|
+
idempotency?: ActionIdempotency<I>;
|
|
46
|
+
concurrency?: ActionConcurrency<I>;
|
|
47
|
+
authorize?: (context: Omit<ActionContext, "signal">, input: I) => Promise<AuthorizationDecision | void> | AuthorizationDecision | void;
|
|
48
|
+
execute: (context: ActionContext, input: I) => Promise<O>;
|
|
49
|
+
audit?: {
|
|
50
|
+
enabled?: boolean;
|
|
51
|
+
changes?: (result: O, input: I) => unknown;
|
|
52
|
+
};
|
|
53
|
+
cache?: {
|
|
54
|
+
invalidateTags?: (result: O, input: I) => string[];
|
|
55
|
+
};
|
|
56
|
+
realtime?: {
|
|
57
|
+
delivery?: "immediate" | "outbox";
|
|
58
|
+
events: (result: O, input: I) => Array<Omit<RealtimeEvent, "version" | "id" | "timestamp">>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface DefinedAction<I, O> {
|
|
62
|
+
readonly definition: ActionDefinition<I, O>;
|
|
63
|
+
}
|
|
64
|
+
export declare function defineAction<I, O>(definition: ActionDefinition<I, O>): DefinedAction<I, O>;
|
|
65
|
+
//# sourceMappingURL=action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"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"}
|
package/dist/action.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AA6EA,MAAM,UAAU,YAAY,CAAO,UAAkC;IACnE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CacheStore, LockStore } from "./stores.js";
|
|
2
|
+
export declare class CacheManager {
|
|
3
|
+
private readonly store;
|
|
4
|
+
private readonly locks;
|
|
5
|
+
constructor(store: CacheStore, locks: LockStore);
|
|
6
|
+
get<T>(key: string): Promise<T | null>;
|
|
7
|
+
set<T>(key: string, value: T, input: {
|
|
8
|
+
ttlMs: number;
|
|
9
|
+
staleMs?: number;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
remember<T>(input: {
|
|
13
|
+
key: string;
|
|
14
|
+
ttlMs: number;
|
|
15
|
+
staleMs?: number;
|
|
16
|
+
tags?: string[];
|
|
17
|
+
lockTtlMs?: number;
|
|
18
|
+
waitMs?: number;
|
|
19
|
+
load: () => Promise<T>;
|
|
20
|
+
}): Promise<T>;
|
|
21
|
+
invalidateTags(tags: string[]): Promise<number>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGzD,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,SAAS;IAG7B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAMtC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzG,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE;QACvB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;KACxB,GAAG,OAAO,CAAC,CAAC,CAAC;IAwBd,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAGhD"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { randomToken } from "./crypto.js";
|
|
2
|
+
export class CacheManager {
|
|
3
|
+
store;
|
|
4
|
+
locks;
|
|
5
|
+
constructor(store, locks) {
|
|
6
|
+
this.store = store;
|
|
7
|
+
this.locks = locks;
|
|
8
|
+
}
|
|
9
|
+
async get(key) {
|
|
10
|
+
const entry = await this.store.get(key);
|
|
11
|
+
if (!entry || entry.expiresAtMs <= Date.now())
|
|
12
|
+
return null;
|
|
13
|
+
return entry.value;
|
|
14
|
+
}
|
|
15
|
+
async set(key, value, input) {
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
await this.store.set(key, {
|
|
18
|
+
value,
|
|
19
|
+
expiresAtMs: now + input.ttlMs,
|
|
20
|
+
staleUntilMs: input.staleMs ? now + input.ttlMs + input.staleMs : undefined,
|
|
21
|
+
tags: input.tags ?? [],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async remember(input) {
|
|
25
|
+
const entry = await this.store.get(input.key);
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
if (entry && entry.expiresAtMs > now)
|
|
28
|
+
return entry.value;
|
|
29
|
+
const token = randomToken(16);
|
|
30
|
+
const lockKey = `cache-load:${input.key}`;
|
|
31
|
+
const acquired = await this.locks.acquire(lockKey, token, input.lockTtlMs ?? 5_000, input.waitMs ?? 1_000);
|
|
32
|
+
if (!acquired) {
|
|
33
|
+
if (entry && (entry.staleUntilMs ?? 0) > now)
|
|
34
|
+
return entry.value;
|
|
35
|
+
return input.load();
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const secondRead = await this.store.get(input.key);
|
|
39
|
+
if (secondRead && secondRead.expiresAtMs > Date.now())
|
|
40
|
+
return secondRead.value;
|
|
41
|
+
const value = await input.load();
|
|
42
|
+
await this.set(input.key, value, input);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
await this.locks.release(lockKey, token);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
invalidateTags(tags) {
|
|
50
|
+
return this.store.deleteByTags(tags);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,OAAO,YAAY;IAEJ;IACA;IAFnB,YACmB,KAAiB,EACjB,KAAgB;QADhB,UAAK,GAAL,KAAK,CAAY;QACjB,UAAK,GAAL,KAAK,CAAW;IAChC,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAI,GAAW;QACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAI,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,IAAI,CAAC;QAC3D,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,KAA2D;QAC7F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YACxB,KAAK;YACL,WAAW,EAAE,GAAG,GAAG,KAAK,CAAC,KAAK;YAC9B,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;YAC3E,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;SACvB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAI,KAQjB;QACC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAI,KAAK,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,KAAK,IAAI,KAAK,CAAC,WAAW,GAAG,GAAG;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC;QAEzD,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,cAAc,KAAK,CAAC,GAAG,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;QAC3G,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,GAAG;gBAAE,OAAO,KAAK,CAAC,KAAK,CAAC;YACjE,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAI,KAAK,CAAC,GAAG,CAAC,CAAC;YACtD,IAAI,UAAU,IAAI,UAAU,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE;gBAAE,OAAO,UAAU,CAAC,KAAK,CAAC;YAC/E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACxC,OAAO,KAAK,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,cAAc,CAAC,IAAc;QAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;CACF"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function randomToken(bytes?: number): string;
|
|
2
|
+
export declare function sha256(value: string): string;
|
|
3
|
+
export declare function hmacSha256(secret: string | Uint8Array, value: string): string;
|
|
4
|
+
export declare function safeEqualText(left: string, right: string): boolean;
|
|
5
|
+
export declare function stableStringify(value: unknown): string;
|
|
6
|
+
export interface TokenCipher {
|
|
7
|
+
encrypt(plaintext: string): string;
|
|
8
|
+
decrypt(payload: string): string;
|
|
9
|
+
}
|
|
10
|
+
export declare function createTokenCipher(input: {
|
|
11
|
+
keys: Record<string, string | Uint8Array>;
|
|
12
|
+
activeKeyId: string;
|
|
13
|
+
}): TokenCipher;
|
|
14
|
+
export declare function redactValue(value: unknown, sensitiveKeys: Set<string>): unknown;
|
|
15
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AASA,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAE9C;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI5C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAIlE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAatD;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAmCxH;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAQ/E"}
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
|
|
2
|
+
export function randomToken(bytes = 32) {
|
|
3
|
+
return randomBytes(bytes).toString("base64url");
|
|
4
|
+
}
|
|
5
|
+
export function sha256(value) {
|
|
6
|
+
const hash = createHash("sha256");
|
|
7
|
+
hash.update(value);
|
|
8
|
+
return hash.digest("hex");
|
|
9
|
+
}
|
|
10
|
+
export function hmacSha256(secret, value) {
|
|
11
|
+
const hmac = createHmac("sha256", secret);
|
|
12
|
+
hmac.update(value);
|
|
13
|
+
return hmac.digest("base64url");
|
|
14
|
+
}
|
|
15
|
+
export function safeEqualText(left, right) {
|
|
16
|
+
const a = Buffer.from(left);
|
|
17
|
+
const b = Buffer.from(right);
|
|
18
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
19
|
+
}
|
|
20
|
+
export function stableStringify(value) {
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return '{"$undefined":true}';
|
|
23
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
24
|
+
return JSON.stringify(value);
|
|
25
|
+
if (typeof value === "number") {
|
|
26
|
+
return Number.isFinite(value) ? JSON.stringify(value) : `{"$number":${JSON.stringify(String(value))}}`;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === "bigint")
|
|
29
|
+
return `{"$bigint":${JSON.stringify(value.toString())}}`;
|
|
30
|
+
if (value instanceof Date)
|
|
31
|
+
return `{"$date":${JSON.stringify(value.toISOString())}}`;
|
|
32
|
+
if (Array.isArray(value))
|
|
33
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
34
|
+
if (typeof value !== "object")
|
|
35
|
+
throw new TypeError(`Unsupported stable value type: ${typeof value}`);
|
|
36
|
+
const source = value;
|
|
37
|
+
const keys = Object.keys(source).sort();
|
|
38
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(source[key])}`).join(",")}}`;
|
|
39
|
+
}
|
|
40
|
+
export function createTokenCipher(input) {
|
|
41
|
+
const active = input.keys[input.activeKeyId];
|
|
42
|
+
if (!active)
|
|
43
|
+
throw new Error(`Unknown active key id: ${input.activeKeyId}`);
|
|
44
|
+
const normalizeKey = (value) => {
|
|
45
|
+
const raw = typeof value === "string" ? Buffer.from(value, "base64") : Buffer.from(value);
|
|
46
|
+
if (raw.length !== 32)
|
|
47
|
+
throw new Error("Encryption keys must decode to 32 bytes");
|
|
48
|
+
return raw;
|
|
49
|
+
};
|
|
50
|
+
const keyring = new Map(Object.entries(input.keys).map(([id, key]) => [id, normalizeKey(key)]));
|
|
51
|
+
return {
|
|
52
|
+
encrypt(plaintext) {
|
|
53
|
+
const iv = randomBytes(12);
|
|
54
|
+
const cipher = createCipheriv("aes-256-gcm", normalizeKey(active), iv);
|
|
55
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
56
|
+
const tag = cipher.getAuthTag();
|
|
57
|
+
return ["v1", input.activeKeyId, iv.toString("base64url"), tag.toString("base64url"), ciphertext.toString("base64url")].join(".");
|
|
58
|
+
},
|
|
59
|
+
decrypt(payload) {
|
|
60
|
+
const [version, keyId, ivText, tagText, ciphertextText] = payload.split(".");
|
|
61
|
+
if (version !== "v1" || !keyId || !ivText || !tagText || !ciphertextText) {
|
|
62
|
+
throw new Error("Invalid encrypted token payload");
|
|
63
|
+
}
|
|
64
|
+
const key = keyring.get(keyId);
|
|
65
|
+
if (!key)
|
|
66
|
+
throw new Error(`Unknown encryption key id: ${keyId}`);
|
|
67
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(ivText, "base64url"));
|
|
68
|
+
decipher.setAuthTag(Buffer.from(tagText, "base64url"));
|
|
69
|
+
return Buffer.concat([
|
|
70
|
+
decipher.update(Buffer.from(ciphertextText, "base64url")),
|
|
71
|
+
decipher.final(),
|
|
72
|
+
]).toString("utf8");
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function redactValue(value, sensitiveKeys) {
|
|
77
|
+
if (Array.isArray(value))
|
|
78
|
+
return value.map((item) => redactValue(item, sensitiveKeys));
|
|
79
|
+
if (!value || typeof value !== "object")
|
|
80
|
+
return value;
|
|
81
|
+
const output = {};
|
|
82
|
+
for (const [key, item] of Object.entries(value)) {
|
|
83
|
+
output[key] = sensitiveKeys.has(key.toLowerCase()) ? "[REDACTED]" : redactValue(item, sensitiveKeys);
|
|
84
|
+
}
|
|
85
|
+
return output;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CacheManager } from "../cache.js";
|
|
2
|
+
import type { AuthorizationDecision, ActionContext } from "../action.js";
|
|
3
|
+
import type { DiscordOAuthClient } from "./oauth.js";
|
|
4
|
+
import { type DiscordPermissionName, type DiscordRoleLike } from "./permissions.js";
|
|
5
|
+
export interface DiscordGuildAccess {
|
|
6
|
+
guildId: string;
|
|
7
|
+
user: {
|
|
8
|
+
owner: boolean;
|
|
9
|
+
permissions: string;
|
|
10
|
+
};
|
|
11
|
+
bot: {
|
|
12
|
+
present: boolean;
|
|
13
|
+
permissions: string;
|
|
14
|
+
roleIds: string[];
|
|
15
|
+
roles: DiscordRoleLike[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface DiscordGuildAuthorizer {
|
|
19
|
+
inspect(input: {
|
|
20
|
+
userId: string;
|
|
21
|
+
guildId: string;
|
|
22
|
+
consistency?: "cached" | "live";
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}): Promise<DiscordGuildAccess>;
|
|
25
|
+
require<I>(input: {
|
|
26
|
+
guildId: (value: I) => string;
|
|
27
|
+
userPermissions?: DiscordPermissionName[];
|
|
28
|
+
botPermissions?: DiscordPermissionName[];
|
|
29
|
+
consistency?: "cached" | "live";
|
|
30
|
+
}): (context: Omit<ActionContext, "signal">, value: I) => Promise<AuthorizationDecision>;
|
|
31
|
+
}
|
|
32
|
+
export declare function createDiscordGuildAuthorizer(input: {
|
|
33
|
+
oauth: DiscordOAuthClient;
|
|
34
|
+
botToken: string;
|
|
35
|
+
botUserId: string;
|
|
36
|
+
cache: CacheManager;
|
|
37
|
+
apiBaseUrl?: string;
|
|
38
|
+
fetch?: typeof globalThis.fetch;
|
|
39
|
+
cachedTtlMs?: number;
|
|
40
|
+
}): DiscordGuildAuthorizer;
|
|
41
|
+
//# sourceMappingURL=authorizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizer.d.ts","sourceRoot":"","sources":["../../src/discord/authorizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAuB,MAAM,YAAY,CAAC;AAC1E,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAY1B,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,EAAE,OAAO,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,EAAE;QACH,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,EAAE,eAAe,EAAE,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACxI,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE;QAChB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC;QAC9B,eAAe,CAAC,EAAE,qBAAqB,EAAE,CAAC;QAC1C,cAAc,CAAC,EAAE,qBAAqB,EAAE,CAAC;QACzC,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;KACjC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC1F;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,sBAAsB,CAwFzB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { errors } from "../errors.js";
|
|
2
|
+
import { computeBaseGuildPermissions, missingPermissions, } from "./permissions.js";
|
|
3
|
+
export function createDiscordGuildAuthorizer(input) {
|
|
4
|
+
const fetcher = input.fetch ?? globalThis.fetch;
|
|
5
|
+
const apiBase = input.apiBaseUrl ?? "https://discord.com/api/v10";
|
|
6
|
+
const inspect = async (request) => {
|
|
7
|
+
const load = () => loadAccess(request.userId, request.guildId, request.signal);
|
|
8
|
+
if (request.consistency === "live")
|
|
9
|
+
return load();
|
|
10
|
+
return input.cache.remember({
|
|
11
|
+
key: `discord-access:${request.userId}:${request.guildId}`,
|
|
12
|
+
ttlMs: input.cachedTtlMs ?? 20_000,
|
|
13
|
+
tags: [`discord-access:${request.userId}`, `guild:${request.guildId}`],
|
|
14
|
+
load,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
inspect,
|
|
19
|
+
require(requirement) {
|
|
20
|
+
return async (context, value) => {
|
|
21
|
+
if (!context.userId)
|
|
22
|
+
return { allowed: false, code: "AUTHENTICATION_REQUIRED" };
|
|
23
|
+
const guildId = requirement.guildId(value);
|
|
24
|
+
const access = await inspect({
|
|
25
|
+
userId: context.userId,
|
|
26
|
+
guildId,
|
|
27
|
+
consistency: requirement.consistency ?? "live",
|
|
28
|
+
});
|
|
29
|
+
const missingUser = missingPermissions(access.user.permissions, requirement.userPermissions ?? ["MANAGE_GUILD"], {
|
|
30
|
+
owner: access.user.owner,
|
|
31
|
+
});
|
|
32
|
+
if (missingUser.length)
|
|
33
|
+
return { allowed: false, code: "USER_PERMISSION_MISSING", details: { permissions: missingUser } };
|
|
34
|
+
if (!access.bot.present)
|
|
35
|
+
return { allowed: false, code: "BOT_NOT_IN_GUILD" };
|
|
36
|
+
const missingBot = missingPermissions(access.bot.permissions, requirement.botPermissions ?? []);
|
|
37
|
+
if (missingBot.length)
|
|
38
|
+
return { allowed: false, code: "BOT_PERMISSION_MISSING", details: { permissions: missingBot } };
|
|
39
|
+
return { allowed: true };
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
async function loadAccess(userId, guildId, signal) {
|
|
44
|
+
const accessToken = await input.oauth.getAccessToken(userId);
|
|
45
|
+
const userGuilds = await discordGet("/users/@me/guilds", `Bearer ${accessToken}`, signal);
|
|
46
|
+
const userGuild = userGuilds.find((guild) => guild.id === guildId);
|
|
47
|
+
if (!userGuild)
|
|
48
|
+
throw errors.guildAccessDenied();
|
|
49
|
+
const [guild, member, roles] = await Promise.all([
|
|
50
|
+
discordGet(`/guilds/${guildId}`, `Bot ${input.botToken}`, signal),
|
|
51
|
+
discordGet(`/guilds/${guildId}/members/${input.botUserId}`, `Bot ${input.botToken}`, signal)
|
|
52
|
+
.catch((error) => {
|
|
53
|
+
if (error instanceof ResponseError && error.status === 404)
|
|
54
|
+
return null;
|
|
55
|
+
throw error;
|
|
56
|
+
}),
|
|
57
|
+
discordGet(`/guilds/${guildId}/roles`, `Bot ${input.botToken}`, signal),
|
|
58
|
+
]);
|
|
59
|
+
if (!member) {
|
|
60
|
+
return {
|
|
61
|
+
guildId,
|
|
62
|
+
user: { owner: userGuild.owner, permissions: userGuild.permissions },
|
|
63
|
+
bot: { present: false, permissions: "0", roleIds: [], roles },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const permissions = computeBaseGuildPermissions({
|
|
67
|
+
guildId,
|
|
68
|
+
ownerId: guild.owner_id,
|
|
69
|
+
memberId: input.botUserId,
|
|
70
|
+
roleIds: member.roles,
|
|
71
|
+
roles,
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
guildId,
|
|
75
|
+
user: { owner: userGuild.owner, permissions: userGuild.permissions },
|
|
76
|
+
bot: { present: true, permissions: permissions.toString(), roleIds: member.roles, roles },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function discordGet(path, authorization, signal) {
|
|
80
|
+
const response = await fetcher(`${apiBase}${path}`, { headers: { authorization }, signal });
|
|
81
|
+
if (response.status === 429) {
|
|
82
|
+
const body = await response.clone().json().catch(() => ({}));
|
|
83
|
+
const headerSeconds = Number(response.headers.get("retry-after"));
|
|
84
|
+
const retryAfterMs = Number.isFinite(headerSeconds)
|
|
85
|
+
? Math.ceil(headerSeconds * 1000)
|
|
86
|
+
: Math.ceil((body.retry_after ?? 1) * 1000);
|
|
87
|
+
throw errors.rateLimited(retryAfterMs);
|
|
88
|
+
}
|
|
89
|
+
if (!response.ok)
|
|
90
|
+
throw new ResponseError(response.status, await response.text().catch(() => ""));
|
|
91
|
+
return response.json();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
class ResponseError extends Error {
|
|
95
|
+
status;
|
|
96
|
+
constructor(status, body) {
|
|
97
|
+
super(`Discord request failed with ${status}: ${body}`);
|
|
98
|
+
this.status = status;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=authorizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizer.js","sourceRoot":"","sources":["../../src/discord/authorizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type TokenCipher } from "../crypto.js";
|
|
2
|
+
import type { GuildGateKernel } from "../kernel.js";
|
|
3
|
+
import type { OAuthCredentialStore, OAuthStateStore } from "../stores.js";
|
|
4
|
+
import type { SupportedLocale } from "../types.js";
|
|
5
|
+
export interface DiscordOAuthUser {
|
|
6
|
+
id: string;
|
|
7
|
+
username: string;
|
|
8
|
+
global_name?: string | null;
|
|
9
|
+
avatar?: string | null;
|
|
10
|
+
locale?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DiscordPartialGuild {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
icon?: string | null;
|
|
16
|
+
owner: boolean;
|
|
17
|
+
permissions: string;
|
|
18
|
+
features?: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface DiscordOAuthConfig {
|
|
21
|
+
clientId: string;
|
|
22
|
+
clientSecret: string;
|
|
23
|
+
redirectUri: string;
|
|
24
|
+
scopes?: string[];
|
|
25
|
+
apiBaseUrl?: string;
|
|
26
|
+
authorizationBaseUrl?: string;
|
|
27
|
+
stateTtlMs?: number;
|
|
28
|
+
requestTimeoutMs?: number;
|
|
29
|
+
fetch?: typeof globalThis.fetch;
|
|
30
|
+
stateCookieName?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface DiscordOAuthClient {
|
|
33
|
+
beginLogin(input?: {
|
|
34
|
+
returnTo?: string;
|
|
35
|
+
locale?: SupportedLocale;
|
|
36
|
+
prompt?: "consent" | "none";
|
|
37
|
+
}): Promise<{
|
|
38
|
+
authorizationUrl: string;
|
|
39
|
+
stateCookie: string;
|
|
40
|
+
}>;
|
|
41
|
+
completeLogin(input: {
|
|
42
|
+
code: string;
|
|
43
|
+
state: string;
|
|
44
|
+
stateCookie: string | undefined;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
user: DiscordOAuthUser;
|
|
47
|
+
guilds: DiscordPartialGuild[];
|
|
48
|
+
returnTo: string;
|
|
49
|
+
sessionToken: string;
|
|
50
|
+
csrfToken: string;
|
|
51
|
+
setCookie: string;
|
|
52
|
+
clearStateCookie: string;
|
|
53
|
+
}>;
|
|
54
|
+
getAccessToken(userId: string): Promise<string>;
|
|
55
|
+
revoke(userId: string): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export declare function createDiscordOAuth(input: {
|
|
58
|
+
config: DiscordOAuthConfig;
|
|
59
|
+
kernel: GuildGateKernel;
|
|
60
|
+
stateStore?: OAuthStateStore;
|
|
61
|
+
credentialStore?: OAuthCredentialStore;
|
|
62
|
+
cipher: TokenCipher;
|
|
63
|
+
}): DiscordOAuthClient;
|
|
64
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/discord/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAUD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACxG,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,aAAa,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC;QAC9F,IAAI,EAAE,gBAAgB,CAAC;QACvB,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAC9B,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC,CAAC;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,MAAM,EAAE,WAAW,CAAC;CACrB,GAAG,kBAAkB,CAoLrB"}
|