@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.
Files changed (133) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +21 -0
  3. package/README.md +399 -0
  4. package/README.tr.md +397 -0
  5. package/SECURITY.md +36 -0
  6. package/TEST_REPORT.md +50 -0
  7. package/bin/guildgate-doctor.mjs +91 -0
  8. package/bin/guildgate-writing-check.mjs +73 -0
  9. package/dist/action.d.ts +65 -0
  10. package/dist/action.d.ts.map +1 -0
  11. package/dist/action.js +4 -0
  12. package/dist/action.js.map +1 -0
  13. package/dist/cache.d.ts +23 -0
  14. package/dist/cache.d.ts.map +1 -0
  15. package/dist/cache.js +53 -0
  16. package/dist/cache.js.map +1 -0
  17. package/dist/crypto.d.ts +15 -0
  18. package/dist/crypto.d.ts.map +1 -0
  19. package/dist/crypto.js +87 -0
  20. package/dist/crypto.js.map +1 -0
  21. package/dist/discord/authorizer.d.ts +41 -0
  22. package/dist/discord/authorizer.d.ts.map +1 -0
  23. package/dist/discord/authorizer.js +101 -0
  24. package/dist/discord/authorizer.js.map +1 -0
  25. package/dist/discord/oauth.d.ts +64 -0
  26. package/dist/discord/oauth.d.ts.map +1 -0
  27. package/dist/discord/oauth.js +226 -0
  28. package/dist/discord/oauth.js.map +1 -0
  29. package/dist/discord/permissions.d.ts +85 -0
  30. package/dist/discord/permissions.d.ts.map +1 -0
  31. package/dist/discord/permissions.js +99 -0
  32. package/dist/discord/permissions.js.map +1 -0
  33. package/dist/discord.d.ts +4 -0
  34. package/dist/discord.d.ts.map +1 -0
  35. package/dist/discord.js +4 -0
  36. package/dist/discord.js.map +1 -0
  37. package/dist/errors.d.ts +43 -0
  38. package/dist/errors.d.ts.map +1 -0
  39. package/dist/errors.js +57 -0
  40. package/dist/errors.js.map +1 -0
  41. package/dist/express.d.ts +3 -0
  42. package/dist/express.d.ts.map +1 -0
  43. package/dist/express.js +3 -0
  44. package/dist/express.js.map +1 -0
  45. package/dist/fastify.d.ts +3 -0
  46. package/dist/fastify.d.ts.map +1 -0
  47. package/dist/fastify.js +3 -0
  48. package/dist/fastify.js.map +1 -0
  49. package/dist/http/express.d.ts +24 -0
  50. package/dist/http/express.d.ts.map +1 -0
  51. package/dist/http/express.js +60 -0
  52. package/dist/http/express.js.map +1 -0
  53. package/dist/http/fastify.d.ts +25 -0
  54. package/dist/http/fastify.d.ts.map +1 -0
  55. package/dist/http/fastify.js +57 -0
  56. package/dist/http/fastify.js.map +1 -0
  57. package/dist/i18n.d.ts +5 -0
  58. package/dist/i18n.d.ts.map +1 -0
  59. package/dist/i18n.js +66 -0
  60. package/dist/i18n.js.map +1 -0
  61. package/dist/index.d.ts +13 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +13 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/kernel.d.ts +83 -0
  66. package/dist/kernel.d.ts.map +1 -0
  67. package/dist/kernel.js +395 -0
  68. package/dist/kernel.js.map +1 -0
  69. package/dist/memory-store.d.ts +13 -0
  70. package/dist/memory-store.d.ts.map +1 -0
  71. package/dist/memory-store.js +261 -0
  72. package/dist/memory-store.js.map +1 -0
  73. package/dist/realtime/hub.d.ts +42 -0
  74. package/dist/realtime/hub.d.ts.map +1 -0
  75. package/dist/realtime/hub.js +152 -0
  76. package/dist/realtime/hub.js.map +1 -0
  77. package/dist/realtime/outbox.d.ts +12 -0
  78. package/dist/realtime/outbox.d.ts.map +1 -0
  79. package/dist/realtime/outbox.js +18 -0
  80. package/dist/realtime/outbox.js.map +1 -0
  81. package/dist/realtime.d.ts +3 -0
  82. package/dist/realtime.d.ts.map +1 -0
  83. package/dist/realtime.js +3 -0
  84. package/dist/realtime.js.map +1 -0
  85. package/dist/redis.d.ts +49 -0
  86. package/dist/redis.d.ts.map +1 -0
  87. package/dist/redis.js +233 -0
  88. package/dist/redis.js.map +1 -0
  89. package/dist/resilience.d.ts +23 -0
  90. package/dist/resilience.d.ts.map +1 -0
  91. package/dist/resilience.js +85 -0
  92. package/dist/resilience.js.map +1 -0
  93. package/dist/security.d.ts +21 -0
  94. package/dist/security.d.ts.map +1 -0
  95. package/dist/security.js +94 -0
  96. package/dist/security.js.map +1 -0
  97. package/dist/session.d.ts +32 -0
  98. package/dist/session.d.ts.map +1 -0
  99. package/dist/session.js +97 -0
  100. package/dist/session.js.map +1 -0
  101. package/dist/stores.d.ts +73 -0
  102. package/dist/stores.d.ts.map +1 -0
  103. package/dist/stores.js +4 -0
  104. package/dist/stores.js.map +1 -0
  105. package/dist/testing.d.ts +32 -0
  106. package/dist/testing.d.ts.map +1 -0
  107. package/dist/testing.js +60 -0
  108. package/dist/testing.js.map +1 -0
  109. package/dist/types.d.ts +183 -0
  110. package/dist/types.d.ts.map +1 -0
  111. package/dist/types.js +4 -0
  112. package/dist/types.js.map +1 -0
  113. package/docs/WRITING_STYLE.md +118 -0
  114. package/docs/architecture.md +83 -0
  115. package/docs/custom-stores.md +111 -0
  116. package/docs/owner-management.md +44 -0
  117. package/docs/research-notes.md +61 -0
  118. package/docs/setup.md +104 -0
  119. package/docs/threat-model.md +79 -0
  120. package/docs/tr/arastirma-notlari.md +41 -0
  121. package/docs/tr/guvenlik-bildirimi.md +7 -0
  122. package/docs/tr/kurulum.md +63 -0
  123. package/docs/tr/mimari.md +59 -0
  124. package/docs/tr/sahip-yonetimi.md +7 -0
  125. package/docs/tr/tehdit-modeli.md +39 -0
  126. package/docs/tr/veritabani-suruculeri.md +23 -0
  127. package/docs/tr/yazim-kilavuzu.md +116 -0
  128. package/docs/uml/action-sequence.mmd +39 -0
  129. package/docs/uml/components.mmd +84 -0
  130. package/docs/uml/deployment.mmd +19 -0
  131. package/docs/uml/oauth-sequence.mmd +30 -0
  132. package/docs/uml/realtime-sequence.mmd +28 -0
  133. package/package.json +90 -0
@@ -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,4 @@
1
+ export function defineAction(definition) {
2
+ return Object.freeze({ definition });
3
+ }
4
+ //# sourceMappingURL=action.js.map
@@ -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"}
@@ -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"}
@@ -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":"AAAA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,WAAW,EACX,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,MAAM,UAAU,WAAW,CAAC,KAAK,GAAG,EAAE;IACpC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAA2B,EAAE,KAAa;IACnE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa;IACvD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,qBAAqB,CAAC;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5G,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC;IACzG,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,cAAc,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;IACxF,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC;IACrF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,OAAO,KAAK,EAAE,CAAC,CAAC;IACrG,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACtG,CAAC;AAOD,MAAM,UAAU,iBAAiB,CAAC,KAAyE;IACzG,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IAE5E,MAAM,YAAY,GAAG,CAAC,KAA0B,EAAU,EAAE;QAC1D,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1F,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAClF,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhG,OAAO;QACL,OAAO,CAAC,SAAiB;YACvB,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACvE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACrF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpI,CAAC;QACD,OAAO,CAAC,OAAe;YACrB,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC7E,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;gBACzE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACrD,CAAC;YACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC/B,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,EAAE,CAAC,CAAC;YACjE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;YACxF,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;YACvD,OAAO,MAAM,CAAC,MAAM,CAAC;gBACnB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;gBACzD,QAAQ,CAAC,KAAK,EAAE;aACjB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,aAA0B;IACpE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;IACvF,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QAC3E,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACvG,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -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"}