@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
package/dist/redis.js ADDED
@@ -0,0 +1,233 @@
1
+ export function fromNodeRedis(client) {
2
+ return {
3
+ get: (key) => client.get(key),
4
+ async set(key, value, options) {
5
+ const result = await client.set(key, value, {
6
+ ...(options?.px ? { PX: options.px } : {}),
7
+ ...(options?.nx ? { NX: true } : {}),
8
+ });
9
+ return result === "OK";
10
+ },
11
+ del: (...keys) => client.del(keys),
12
+ sAdd: (key, ...members) => client.sAdd(key, members),
13
+ sRem: (key, ...members) => client.sRem(key, members),
14
+ sMembers: (key) => client.sMembers(key),
15
+ pTtl: (key) => client.pTTL(key),
16
+ eval: (script, keys, args) => client.eval(script, { keys, arguments: args }),
17
+ };
18
+ }
19
+ export function fromIoRedis(client) {
20
+ return {
21
+ get: (key) => client.get(key),
22
+ async set(key, value, options) {
23
+ const args = [];
24
+ if (options?.px)
25
+ args.push("PX", String(options.px));
26
+ if (options?.nx)
27
+ args.push("NX");
28
+ return await client.set(key, value, ...args) === "OK";
29
+ },
30
+ del: (...keys) => client.del(...keys),
31
+ sAdd: (key, ...members) => client.sadd(key, ...members),
32
+ sRem: (key, ...members) => client.srem(key, ...members),
33
+ sMembers: (key) => client.smembers(key),
34
+ pTtl: (key) => client.pttl(key),
35
+ eval: (script, keys, args) => client.eval(script, keys.length, ...keys, ...args),
36
+ };
37
+ }
38
+ export function createRedisEphemeralStores(redis, input) {
39
+ const prefix = input?.prefix ?? "guildgate";
40
+ const key = (kind, id) => `${prefix}:${kind}:${id}`;
41
+ const sessions = {
42
+ async get(idHash) {
43
+ return parse(await redis.get(key("session", idHash)));
44
+ },
45
+ async set(record) {
46
+ const ttl = Math.max(1, new Date(record.expiresAt).getTime() - Date.now());
47
+ await redis.set(key("session", record.idHash), JSON.stringify(record), { px: ttl });
48
+ await redis.sAdd(key("user-sessions", record.userId), record.idHash);
49
+ },
50
+ async delete(idHash) {
51
+ const record = await sessions.get(idHash);
52
+ await redis.del(key("session", idHash));
53
+ if (record)
54
+ await redis.sRem(key("user-sessions", record.userId), idHash);
55
+ },
56
+ async listByUser(userId) {
57
+ const ids = await redis.sMembers(key("user-sessions", userId));
58
+ const rows = await Promise.all(ids.map((id) => sessions.get(id)));
59
+ const missing = ids.filter((_, index) => !rows[index]);
60
+ if (missing.length)
61
+ await redis.sRem(key("user-sessions", userId), ...missing);
62
+ return rows.filter((row) => Boolean(row));
63
+ },
64
+ };
65
+ const oauthStates = {
66
+ async put(record) {
67
+ const ttl = Math.max(1, new Date(record.expiresAt).getTime() - Date.now());
68
+ await redis.set(key("oauth-state", record.stateHash), JSON.stringify(record), { px: ttl });
69
+ },
70
+ async consume(stateHash, nowIso) {
71
+ const raw = await redis.eval(GET_AND_DELETE_SCRIPT, [key("oauth-state", stateHash)], []);
72
+ if (typeof raw !== "string")
73
+ return null;
74
+ const record = parse(raw);
75
+ return record && record.expiresAt > nowIso ? record : null;
76
+ },
77
+ };
78
+ const rateLimits = {
79
+ async hit(hit) {
80
+ const result = await redis.eval(RATE_LIMIT_SCRIPT, [key("rate", hit.key)], [
81
+ String(hit.cost ?? 1),
82
+ String(hit.limit),
83
+ String(hit.windowMs),
84
+ ]);
85
+ const [allowed, count, ttl] = asNumberArray(result);
86
+ return {
87
+ allowed: allowed === 1,
88
+ limit: hit.limit,
89
+ remaining: Math.max(0, hit.limit - count),
90
+ resetAtMs: hit.nowMs + ttl,
91
+ retryAfterMs: allowed === 1 ? 0 : ttl,
92
+ };
93
+ },
94
+ async reset(rateKey) {
95
+ await redis.del(key("rate", rateKey));
96
+ },
97
+ };
98
+ const cache = {
99
+ async get(cacheKey) {
100
+ return parse(await redis.get(key("cache", cacheKey)));
101
+ },
102
+ async set(cacheKey, entry) {
103
+ const ttl = Math.max(1, (entry.staleUntilMs ?? entry.expiresAtMs) - Date.now());
104
+ const storageKey = key("cache", cacheKey);
105
+ await redis.set(storageKey, JSON.stringify(entry), { px: ttl });
106
+ for (const tag of entry.tags)
107
+ await redis.sAdd(key("cache-tag", tag), cacheKey);
108
+ },
109
+ async delete(cacheKey) {
110
+ const storageKey = key("cache", cacheKey);
111
+ const entry = parse(await redis.get(storageKey));
112
+ await redis.del(storageKey);
113
+ if (entry) {
114
+ for (const tag of entry.tags)
115
+ await redis.sRem(key("cache-tag", tag), cacheKey);
116
+ }
117
+ },
118
+ async deleteByTags(tags) {
119
+ const ids = new Set();
120
+ for (const tag of tags) {
121
+ for (const id of await redis.sMembers(key("cache-tag", tag)))
122
+ ids.add(id);
123
+ }
124
+ await Promise.all([...ids].map((id) => cache.delete(id)));
125
+ await Promise.all(tags.map((tag) => redis.del(key("cache-tag", tag))));
126
+ return ids.size;
127
+ },
128
+ };
129
+ const idempotency = {
130
+ async begin(record) {
131
+ const result = await redis.eval(IDEMPOTENCY_BEGIN_SCRIPT, [key("idempotency", record.key)], [
132
+ JSON.stringify(record),
133
+ record.requestHash,
134
+ String(Math.max(1, record.expiresAtMs - Date.now())),
135
+ ]);
136
+ if (!Array.isArray(result))
137
+ throw new Error("Unexpected Redis idempotency response");
138
+ const status = String(result[0]);
139
+ if (status === "started")
140
+ return { status: "started" };
141
+ if (status === "inflight")
142
+ return { status: "inflight" };
143
+ if (status === "conflict")
144
+ return { status: "conflict" };
145
+ if (status === "completed") {
146
+ const existing = parse(String(result[1]));
147
+ return { status: "completed", response: existing?.response };
148
+ }
149
+ throw new Error(`Unknown idempotency status: ${status}`);
150
+ },
151
+ async complete(idempotencyKey, response, expiresAtMs) {
152
+ const storageKey = key("idempotency", idempotencyKey);
153
+ const record = parse(await redis.get(storageKey));
154
+ if (!record)
155
+ return;
156
+ await redis.set(storageKey, JSON.stringify({ ...record, state: "completed", response, expiresAtMs }), {
157
+ px: Math.max(1, expiresAtMs - Date.now()),
158
+ });
159
+ },
160
+ async fail(idempotencyKey) {
161
+ await redis.del(key("idempotency", idempotencyKey));
162
+ },
163
+ async get(idempotencyKey) {
164
+ return parse(await redis.get(key("idempotency", idempotencyKey)));
165
+ },
166
+ };
167
+ const locks = {
168
+ async acquire(lockKey, token, ttlMs, waitMs) {
169
+ const deadline = Date.now() + Math.max(0, waitMs);
170
+ do {
171
+ if (await redis.set(key("lock", lockKey), token, { px: ttlMs, nx: true }))
172
+ return true;
173
+ if (waitMs <= 0)
174
+ return false;
175
+ await new Promise((resolve) => setTimeout(resolve, 25));
176
+ } while (Date.now() <= deadline);
177
+ return false;
178
+ },
179
+ async release(lockKey, token) {
180
+ await redis.eval(COMPARE_AND_DELETE_SCRIPT, [key("lock", lockKey)], [token]);
181
+ },
182
+ };
183
+ return { sessions, oauthStates, rateLimits, cache, idempotency, locks };
184
+ }
185
+ const GET_AND_DELETE_SCRIPT = `
186
+ local value = redis.call('GET', KEYS[1])
187
+ if value then redis.call('DEL', KEYS[1]) end
188
+ return value
189
+ `;
190
+ const RATE_LIMIT_SCRIPT = `
191
+ local current = redis.call('GET', KEYS[1])
192
+ local count = tonumber(current or '0')
193
+ local cost = tonumber(ARGV[1])
194
+ local limit = tonumber(ARGV[2])
195
+ local window = tonumber(ARGV[3])
196
+ if not current then redis.call('PSETEX', KEYS[1], window, '0') end
197
+ local allowed = 0
198
+ if count + cost <= limit then
199
+ count = redis.call('INCRBY', KEYS[1], cost)
200
+ allowed = 1
201
+ end
202
+ local ttl = redis.call('PTTL', KEYS[1])
203
+ if ttl < 0 then redis.call('PEXPIRE', KEYS[1], window); ttl = window end
204
+ return {allowed, count, ttl}
205
+ `;
206
+ const COMPARE_AND_DELETE_SCRIPT = `
207
+ if redis.call('GET', KEYS[1]) == ARGV[1] then
208
+ return redis.call('DEL', KEYS[1])
209
+ end
210
+ return 0
211
+ `;
212
+ const IDEMPOTENCY_BEGIN_SCRIPT = `
213
+ local existing = redis.call('GET', KEYS[1])
214
+ if not existing then
215
+ redis.call('PSETEX', KEYS[1], ARGV[3], ARGV[1])
216
+ return {'started'}
217
+ end
218
+ local decoded = cjson.decode(existing)
219
+ if decoded.requestHash ~= ARGV[2] then return {'conflict'} end
220
+ if decoded.state == 'completed' then return {'completed', existing} end
221
+ return {'inflight'}
222
+ `;
223
+ function parse(value) {
224
+ if (!value)
225
+ return null;
226
+ return JSON.parse(value);
227
+ }
228
+ function asNumberArray(value) {
229
+ if (!Array.isArray(value) || value.length < 3)
230
+ throw new Error("Unexpected Redis script response");
231
+ return [Number(value[0]), Number(value[1]), Number(value[2])];
232
+ }
233
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../src/redis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface RetryOptions {
2
+ attempts: number;
3
+ baseDelayMs: number;
4
+ maximumDelayMs: number;
5
+ shouldRetry?: (error: unknown, attempt: number) => boolean;
6
+ delayMs?: (error: unknown, attempt: number) => number | undefined;
7
+ }
8
+ export declare function runWithDeadline<T>(input: {
9
+ timeoutMs: number;
10
+ parentSignal?: AbortSignal;
11
+ retry?: RetryOptions;
12
+ execute: (signal: AbortSignal, attempt: number) => Promise<T>;
13
+ }): Promise<T>;
14
+ export declare class CircuitBreaker {
15
+ private readonly input;
16
+ private readonly states;
17
+ constructor(input: {
18
+ failureThreshold: number;
19
+ resetAfterMs: number;
20
+ });
21
+ run<T>(name: string, execute: () => Promise<T>): Promise<T>;
22
+ }
23
+ //# sourceMappingURL=resilience.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resilience.d.ts","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACnE;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC/D,GAAG,OAAO,CAAC,CAAC,CAAC,CAkCb;AA4BD,qBAAa,cAAc;IAGb,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;gBAE7B,KAAK,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAEhF,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAelE"}
@@ -0,0 +1,85 @@
1
+ import { errors, GuildGateError } from "./errors.js";
2
+ export async function runWithDeadline(input) {
3
+ const startedAt = Date.now();
4
+ const attempts = Math.max(1, input.retry?.attempts ?? 1);
5
+ let lastError;
6
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
7
+ const remaining = input.timeoutMs - (Date.now() - startedAt);
8
+ if (remaining <= 0)
9
+ throw errors.timeout();
10
+ const controller = new AbortController();
11
+ const timer = setTimeout(() => controller.abort(errors.timeout()), remaining);
12
+ const onParentAbort = () => controller.abort(input.parentSignal?.reason);
13
+ input.parentSignal?.addEventListener("abort", onParentAbort, { once: true });
14
+ try {
15
+ return await input.execute(controller.signal, attempt);
16
+ }
17
+ catch (error) {
18
+ lastError = error;
19
+ if (controller.signal.aborted && !input.parentSignal?.aborted)
20
+ throw errors.timeout();
21
+ const retryable = input.retry?.shouldRetry?.(error, attempt) ?? defaultShouldRetry(error);
22
+ if (!retryable || attempt >= attempts)
23
+ throw error;
24
+ const remainingAfterFailure = input.timeoutMs - (Date.now() - startedAt);
25
+ if (remainingAfterFailure <= 0)
26
+ throw errors.timeout();
27
+ const configuredDelay = input.retry?.delayMs?.(error, attempt);
28
+ const delay = configuredDelay === undefined
29
+ ? jitteredDelay(input.retry, attempt)
30
+ : Math.max(0, configuredDelay);
31
+ await sleep(Math.min(delay, remainingAfterFailure), input.parentSignal);
32
+ }
33
+ finally {
34
+ clearTimeout(timer);
35
+ input.parentSignal?.removeEventListener("abort", onParentAbort);
36
+ }
37
+ }
38
+ throw lastError;
39
+ }
40
+ function defaultShouldRetry(error) {
41
+ if (error instanceof GuildGateError)
42
+ return error.retryable;
43
+ return error instanceof TypeError;
44
+ }
45
+ function jitteredDelay(options, attempt) {
46
+ const exponential = Math.min(options.maximumDelayMs, options.baseDelayMs * 2 ** Math.max(0, attempt - 1));
47
+ return Math.floor(exponential * (0.5 + Math.random() * 0.5));
48
+ }
49
+ function sleep(ms, signal) {
50
+ return new Promise((resolve, reject) => {
51
+ if (signal?.aborted)
52
+ return reject(signal.reason);
53
+ const timer = setTimeout(resolve, ms);
54
+ signal?.addEventListener("abort", () => {
55
+ clearTimeout(timer);
56
+ reject(signal.reason);
57
+ }, { once: true });
58
+ });
59
+ }
60
+ export class CircuitBreaker {
61
+ input;
62
+ states = new Map();
63
+ constructor(input) {
64
+ this.input = input;
65
+ }
66
+ async run(name, execute) {
67
+ const now = Date.now();
68
+ const state = this.states.get(name) ?? { failures: 0, openUntilMs: 0 };
69
+ if (state.openUntilMs > now)
70
+ throw errors.upstreamUnavailable({ circuit: name, openUntilMs: state.openUntilMs });
71
+ try {
72
+ const result = await execute();
73
+ this.states.delete(name);
74
+ return result;
75
+ }
76
+ catch (error) {
77
+ state.failures += 1;
78
+ if (state.failures >= this.input.failureThreshold)
79
+ state.openUntilMs = now + this.input.resetAfterMs;
80
+ this.states.set(name, state);
81
+ throw error;
82
+ }
83
+ }
84
+ }
85
+ //# sourceMappingURL=resilience.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resilience.js","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { EnvironmentName, HttpMethod } from "./types.js";
2
+ export declare function isUnsafeMethod(method: HttpMethod): boolean;
3
+ export declare function normalizeOrigin(value: string): string;
4
+ export declare function assertAllowedOrigin(origin: string | undefined, allowedOrigins: Set<string>): void;
5
+ export declare function validateAllowedOrigins(origins: string[], environment: EnvironmentName): Set<string>;
6
+ export interface CsrfService {
7
+ issue(sessionIdHash: string): string;
8
+ verify(sessionIdHash: string, token: string | undefined): boolean;
9
+ }
10
+ export declare function createCsrfService(secret: string): CsrfService;
11
+ export interface CookieConfig {
12
+ name: string;
13
+ path: string;
14
+ secure: boolean;
15
+ sameSite: "Lax" | "Strict";
16
+ maxAgeSeconds: number;
17
+ }
18
+ export declare function validateCookieConfig(config: CookieConfig): void;
19
+ export declare function serializeSessionCookie(value: string, config: CookieConfig): string;
20
+ export declare function serializeClearedCookie(config: CookieConfig): string;
21
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI9D,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAE1D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAQjG;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,eAAe,GAAG,GAAG,CAAC,MAAM,CAAC,CAenG;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;CACnE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAc7D;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAW/D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAUlF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAUnE"}
@@ -0,0 +1,94 @@
1
+ import { hmacSha256, randomToken, safeEqualText } from "./crypto.js";
2
+ import { errors } from "./errors.js";
3
+ const unsafeMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
4
+ export function isUnsafeMethod(method) {
5
+ return unsafeMethods.has(method);
6
+ }
7
+ export function normalizeOrigin(value) {
8
+ const url = new URL(value);
9
+ return `${url.protocol}//${url.host}`;
10
+ }
11
+ export function assertAllowedOrigin(origin, allowedOrigins) {
12
+ if (!origin)
13
+ throw errors.originNotAllowed();
14
+ try {
15
+ if (!allowedOrigins.has(normalizeOrigin(origin)))
16
+ throw errors.originNotAllowed();
17
+ }
18
+ catch (error) {
19
+ if (error instanceof Error && error.name === "GuildGateError")
20
+ throw error;
21
+ throw errors.originNotAllowed();
22
+ }
23
+ }
24
+ export function validateAllowedOrigins(origins, environment) {
25
+ if (environment === "production" && origins.length === 0) {
26
+ throw errors.configuration("At least one allowed origin is required in production");
27
+ }
28
+ const normalized = origins.map(normalizeOrigin);
29
+ if (environment === "production") {
30
+ for (const origin of normalized) {
31
+ const url = new URL(origin);
32
+ if (url.protocol !== "https:")
33
+ throw errors.configuration("Production origins must use HTTPS");
34
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
35
+ throw errors.configuration("Production origins cannot point to localhost");
36
+ }
37
+ }
38
+ }
39
+ return new Set(normalized);
40
+ }
41
+ export function createCsrfService(secret) {
42
+ if (Buffer.byteLength(secret) < 32)
43
+ throw errors.configuration("CSRF secret must contain at least 32 bytes");
44
+ return {
45
+ issue(sessionIdHash) {
46
+ const nonce = randomToken(24);
47
+ return `${nonce}.${hmacSha256(secret, `${sessionIdHash}.${nonce}`)}`;
48
+ },
49
+ verify(sessionIdHash, token) {
50
+ if (!token)
51
+ return false;
52
+ const [nonce, signature] = token.split(".");
53
+ if (!nonce || !signature)
54
+ return false;
55
+ return safeEqualText(signature, hmacSha256(secret, `${sessionIdHash}.${nonce}`));
56
+ },
57
+ };
58
+ }
59
+ export function validateCookieConfig(config) {
60
+ if (!config.name || /[\s;,=]/.test(config.name)) {
61
+ throw errors.configuration("Session cookie name is invalid");
62
+ }
63
+ if (!config.path.startsWith("/"))
64
+ throw errors.configuration("Session cookie path must start with /");
65
+ if (config.name.startsWith("__Host-") && (!config.secure || config.path !== "/")) {
66
+ throw errors.configuration("__Host- cookies require Secure and Path=/");
67
+ }
68
+ if (config.name.startsWith("__Secure-") && !config.secure) {
69
+ throw errors.configuration("__Secure- cookies require Secure");
70
+ }
71
+ }
72
+ export function serializeSessionCookie(value, config) {
73
+ validateCookieConfig(config);
74
+ return [
75
+ `${config.name}=${encodeURIComponent(value)}`,
76
+ `Path=${config.path}`,
77
+ `Max-Age=${config.maxAgeSeconds}`,
78
+ "HttpOnly",
79
+ config.secure ? "Secure" : "",
80
+ `SameSite=${config.sameSite}`,
81
+ ].filter(Boolean).join("; ");
82
+ }
83
+ export function serializeClearedCookie(config) {
84
+ validateCookieConfig(config);
85
+ return [
86
+ `${config.name}=`,
87
+ `Path=${config.path}`,
88
+ "Max-Age=0",
89
+ "HttpOnly",
90
+ config.secure ? "Secure" : "",
91
+ `SameSite=${config.sameSite}`,
92
+ ].filter(Boolean).join("; ");
93
+ }
94
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAa,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE9E,MAAM,UAAU,cAAc,CAAC,MAAkB;IAC/C,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAA0B,EAAE,cAA2B;IACzF,IAAI,CAAC,MAAM;QAAE,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;IAC7C,IAAI,CAAC;QACH,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAAE,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;IACpF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;YAAE,MAAM,KAAK,CAAC;QAC3E,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;IAClC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAiB,EAAE,WAA4B;IACpF,IAAI,WAAW,KAAK,YAAY,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,MAAM,CAAC,aAAa,CAAC,uDAAuD,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAChD,IAAI,WAAW,KAAK,YAAY,EAAE,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;gBAAE,MAAM,MAAM,CAAC,aAAa,CAAC,mCAAmC,CAAC,CAAC;YAC/F,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACjE,MAAM,MAAM,CAAC,aAAa,CAAC,8CAA8C,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAOD,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE;QAAE,MAAM,MAAM,CAAC,aAAa,CAAC,4CAA4C,CAAC,CAAC;IAC7G,OAAO;QACL,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;YAC9B,OAAO,GAAG,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC;QACvE,CAAC;QACD,MAAM,CAAC,aAAa,EAAE,KAAK;YACzB,IAAI,CAAC,KAAK;gBAAE,OAAO,KAAK,CAAC;YACzB,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAC;YACvC,OAAO,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;QACnF,CAAC;KACF,CAAC;AACJ,CAAC;AAUD,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACvD,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,MAAM,CAAC,aAAa,CAAC,gCAAgC,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,MAAM,CAAC,aAAa,CAAC,uCAAuC,CAAC,CAAC;IACtG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACjF,MAAM,MAAM,CAAC,aAAa,CAAC,2CAA2C,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC1D,MAAM,MAAM,CAAC,aAAa,CAAC,kCAAkC,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,MAAoB;IACxE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO;QACL,GAAG,MAAM,CAAC,IAAI,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE;QAC7C,QAAQ,MAAM,CAAC,IAAI,EAAE;QACrB,WAAW,MAAM,CAAC,aAAa,EAAE;QACjC,UAAU;QACV,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;QAC7B,YAAY,MAAM,CAAC,QAAQ,EAAE;KAC9B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAoB;IACzD,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO;QACL,GAAG,MAAM,CAAC,IAAI,GAAG;QACjB,QAAQ,MAAM,CAAC,IAAI,EAAE;QACrB,WAAW;QACX,UAAU;QACV,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;QAC7B,YAAY,MAAM,CAAC,QAAQ,EAAE;KAC9B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { SessionStore } from "./stores.js";
2
+ import type { Clock, SessionRecord, SessionResolution, SupportedLocale } from "./types.js";
3
+ export interface SessionManagerOptions {
4
+ store: SessionStore;
5
+ clock: Clock;
6
+ ttlMs: number;
7
+ idleTimeoutMs: number;
8
+ rotateAfterMs: number;
9
+ maximumSessionsPerUser: number;
10
+ }
11
+ export interface CreateSessionInput {
12
+ userId: string;
13
+ locale: SupportedLocale;
14
+ metadata?: Record<string, unknown>;
15
+ }
16
+ export declare class SessionManager {
17
+ private readonly options;
18
+ constructor(options: SessionManagerOptions);
19
+ create(input: CreateSessionInput): Promise<{
20
+ token: string;
21
+ session: SessionRecord;
22
+ }>;
23
+ resolve(token: string | undefined): Promise<SessionResolution>;
24
+ validate(token: string | undefined): Promise<SessionRecord | null>;
25
+ revokeToken(token: string): Promise<void>;
26
+ revokeHash(idHash: string): Promise<void>;
27
+ revokeAll(userId: string): Promise<number>;
28
+ list(userId: string): Promise<SessionRecord[]>;
29
+ private buildRecord;
30
+ private enforceSessionLimit;
31
+ }
32
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE3F,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,qBAAqB;IAErD,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAE,CAAC;IASrF,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA+B9D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAalE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM1C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIpD,OAAO,CAAC,WAAW;YAcL,mBAAmB;CAQlC"}
@@ -0,0 +1,97 @@
1
+ import { randomToken, sha256 } from "./crypto.js";
2
+ import { errors } from "./errors.js";
3
+ export class SessionManager {
4
+ options;
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ async create(input) {
9
+ const now = this.options.clock.now();
10
+ const token = randomToken();
11
+ const session = this.buildRecord(token, input, now);
12
+ await this.options.store.set(session);
13
+ await this.enforceSessionLimit(input.userId, session.idHash);
14
+ return { token, session };
15
+ }
16
+ async resolve(token) {
17
+ if (!token)
18
+ return { session: null };
19
+ const now = this.options.clock.now();
20
+ const idHash = sha256(token);
21
+ const session = await this.options.store.get(idHash);
22
+ if (!session)
23
+ return { session: null, clearCookie: true };
24
+ if (session.revokedAt)
25
+ throw errors.sessionRevoked();
26
+ if (new Date(session.expiresAt) <= now || new Date(session.idleExpiresAt) <= now) {
27
+ await this.options.store.delete(idHash);
28
+ throw errors.sessionExpired();
29
+ }
30
+ if (new Date(session.rotateAfter) <= now) {
31
+ const replacement = await this.create({
32
+ userId: session.userId,
33
+ locale: session.locale,
34
+ metadata: session.metadata,
35
+ });
36
+ await this.options.store.delete(session.idHash);
37
+ return { session: replacement.session, replacementToken: replacement.token };
38
+ }
39
+ const updated = {
40
+ ...session,
41
+ lastSeenAt: now.toISOString(),
42
+ idleExpiresAt: new Date(now.getTime() + this.options.idleTimeoutMs).toISOString(),
43
+ };
44
+ await this.options.store.set(updated);
45
+ return { session: updated };
46
+ }
47
+ async validate(token) {
48
+ if (!token)
49
+ return null;
50
+ const now = this.options.clock.now();
51
+ const idHash = sha256(token);
52
+ const session = await this.options.store.get(idHash);
53
+ if (!session)
54
+ return null;
55
+ if (session.revokedAt || new Date(session.expiresAt) <= now || new Date(session.idleExpiresAt) <= now) {
56
+ await this.options.store.delete(idHash);
57
+ return null;
58
+ }
59
+ return session;
60
+ }
61
+ async revokeToken(token) {
62
+ await this.options.store.delete(sha256(token));
63
+ }
64
+ async revokeHash(idHash) {
65
+ await this.options.store.delete(idHash);
66
+ }
67
+ async revokeAll(userId) {
68
+ const rows = await this.options.store.listByUser(userId);
69
+ await Promise.all(rows.map((row) => this.options.store.delete(row.idHash)));
70
+ return rows.length;
71
+ }
72
+ async list(userId) {
73
+ return this.options.store.listByUser(userId);
74
+ }
75
+ buildRecord(token, input, now) {
76
+ return {
77
+ idHash: sha256(token),
78
+ userId: input.userId,
79
+ locale: input.locale,
80
+ createdAt: now.toISOString(),
81
+ lastSeenAt: now.toISOString(),
82
+ expiresAt: new Date(now.getTime() + this.options.ttlMs).toISOString(),
83
+ idleExpiresAt: new Date(now.getTime() + this.options.idleTimeoutMs).toISOString(),
84
+ rotateAfter: new Date(now.getTime() + this.options.rotateAfterMs).toISOString(),
85
+ metadata: input.metadata,
86
+ };
87
+ }
88
+ async enforceSessionLimit(userId, keepIdHash) {
89
+ const rows = await this.options.store.listByUser(userId);
90
+ const active = rows
91
+ .filter((row) => row.idHash !== keepIdHash)
92
+ .sort((a, b) => a.lastSeenAt.localeCompare(b.lastSeenAt));
93
+ const excess = Math.max(0, active.length + 1 - this.options.maximumSessionsPerUser);
94
+ await Promise.all(active.slice(0, excess).map((row) => this.options.store.delete(row.idHash)));
95
+ }
96
+ }
97
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}