@shaferllc/keel 0.59.0 → 0.68.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 (166) hide show
  1. package/AGENTS.md +167 -0
  2. package/README.md +30 -1
  3. package/bin/keel-mcp.mjs +9 -0
  4. package/dist/core/application.d.ts +5 -5
  5. package/dist/core/application.js +14 -2
  6. package/dist/core/auth.d.ts +47 -0
  7. package/dist/core/auth.js +77 -0
  8. package/dist/core/authorization.d.ts +9 -1
  9. package/dist/core/authorization.js +22 -2
  10. package/dist/core/cache.d.ts +82 -5
  11. package/dist/core/cache.js +181 -23
  12. package/dist/core/cli/stubs.d.ts +12 -0
  13. package/dist/core/cli/stubs.js +120 -0
  14. package/dist/core/container.d.ts +20 -0
  15. package/dist/core/container.js +52 -0
  16. package/dist/core/cors.d.ts +29 -0
  17. package/dist/core/cors.js +72 -0
  18. package/dist/core/crypto.d.ts +40 -4
  19. package/dist/core/crypto.js +66 -6
  20. package/dist/core/csrf.d.ts +25 -0
  21. package/dist/core/csrf.js +78 -0
  22. package/dist/core/database.d.ts +49 -4
  23. package/dist/core/database.js +89 -21
  24. package/dist/core/events.d.ts +129 -5
  25. package/dist/core/events.js +165 -7
  26. package/dist/core/health.d.ts +141 -0
  27. package/dist/core/health.js +226 -0
  28. package/dist/core/helpers.d.ts +15 -3
  29. package/dist/core/helpers.js +23 -3
  30. package/dist/core/index.d.ts +33 -18
  31. package/dist/core/index.js +16 -8
  32. package/dist/core/lock.d.ts +139 -0
  33. package/dist/core/lock.js +215 -0
  34. package/dist/core/logger.d.ts +82 -4
  35. package/dist/core/logger.js +141 -23
  36. package/dist/core/mail.d.ts +128 -7
  37. package/dist/core/mail.js +264 -16
  38. package/dist/core/model.d.ts +2 -0
  39. package/dist/core/model.js +16 -14
  40. package/dist/core/provider.d.ts +7 -0
  41. package/dist/core/provider.js +7 -0
  42. package/dist/core/queue.d.ts +134 -9
  43. package/dist/core/queue.js +304 -14
  44. package/dist/core/rate-limit.js +3 -0
  45. package/dist/core/relations.js +13 -13
  46. package/dist/core/request.d.ts +26 -0
  47. package/dist/core/request.js +77 -0
  48. package/dist/core/shield.d.ts +39 -0
  49. package/dist/core/shield.js +60 -0
  50. package/dist/core/social.d.ts +173 -0
  51. package/dist/core/social.js +337 -0
  52. package/dist/core/storage.d.ts +159 -6
  53. package/dist/core/storage.js +287 -7
  54. package/dist/core/tokens.d.ts +74 -0
  55. package/dist/core/tokens.js +155 -0
  56. package/dist/db/d1.d.ts +32 -0
  57. package/dist/db/d1.js +26 -0
  58. package/dist/db/libsql.d.ts +29 -0
  59. package/dist/db/libsql.js +32 -0
  60. package/dist/db/pg.d.ts +29 -0
  61. package/dist/db/pg.js +33 -0
  62. package/dist/mcp/server.d.ts +19 -0
  63. package/dist/mcp/server.js +355 -0
  64. package/docs/ai-manifest.json +2472 -0
  65. package/docs/ai.md +128 -0
  66. package/docs/architecture.md +331 -0
  67. package/docs/authentication.md +453 -0
  68. package/docs/authorization.md +167 -0
  69. package/docs/broadcasting.md +137 -0
  70. package/docs/broker.md +500 -0
  71. package/docs/cache.md +558 -0
  72. package/docs/configuration.md +311 -0
  73. package/docs/console.md +356 -0
  74. package/docs/container.md +467 -0
  75. package/docs/controllers.md +265 -0
  76. package/docs/cors.md +51 -0
  77. package/docs/database.md +530 -0
  78. package/docs/debugging.md +129 -0
  79. package/docs/decorators.md +127 -0
  80. package/docs/errors.md +395 -0
  81. package/docs/events.md +496 -0
  82. package/docs/examples/architecture-app.ts +27 -0
  83. package/docs/examples/authentication.ts +61 -0
  84. package/docs/examples/authorization.ts +79 -0
  85. package/docs/examples/broadcasting.ts +60 -0
  86. package/docs/examples/broker-cache-validate.ts +34 -0
  87. package/docs/examples/broker-fault-tolerance.ts +29 -0
  88. package/docs/examples/broker-middleware.ts +27 -0
  89. package/docs/examples/broker.ts +203 -0
  90. package/docs/examples/cache.ts +222 -0
  91. package/docs/examples/configuration.ts +81 -0
  92. package/docs/examples/container.ts +134 -0
  93. package/docs/examples/controllers.ts +86 -0
  94. package/docs/examples/database.ts +118 -0
  95. package/docs/examples/debugging.ts +41 -0
  96. package/docs/examples/decorators.ts +40 -0
  97. package/docs/examples/errors.ts +121 -0
  98. package/docs/examples/events.ts +204 -0
  99. package/docs/examples/factories.ts +84 -0
  100. package/docs/examples/hashing.ts +71 -0
  101. package/docs/examples/health.ts +94 -0
  102. package/docs/examples/helpers.ts +171 -0
  103. package/docs/examples/hooks.ts +54 -0
  104. package/docs/examples/inertia.ts +81 -0
  105. package/docs/examples/locks.ts +120 -0
  106. package/docs/examples/logger.ts +92 -0
  107. package/docs/examples/mail.ts +160 -0
  108. package/docs/examples/middleware.ts +119 -0
  109. package/docs/examples/migrations.ts +126 -0
  110. package/docs/examples/models.ts +239 -0
  111. package/docs/examples/notification.ts +124 -0
  112. package/docs/examples/providers.ts +123 -0
  113. package/docs/examples/queues.ts +254 -0
  114. package/docs/examples/rate-limiting.ts +42 -0
  115. package/docs/examples/redis.ts +99 -0
  116. package/docs/examples/request-response.ts +197 -0
  117. package/docs/examples/routing.ts +186 -0
  118. package/docs/examples/scheduling.ts +62 -0
  119. package/docs/examples/sessions.ts +102 -0
  120. package/docs/examples/static-files.ts +63 -0
  121. package/docs/examples/storage.ts +132 -0
  122. package/docs/examples/templates.ts +58 -0
  123. package/docs/examples/testing.ts +66 -0
  124. package/docs/examples/transformer.ts +141 -0
  125. package/docs/examples/transformers.ts +49 -0
  126. package/docs/examples/url-builder.ts +86 -0
  127. package/docs/examples/validation.ts +102 -0
  128. package/docs/examples/views.tsx +62 -0
  129. package/docs/examples/vite.ts +106 -0
  130. package/docs/factories.md +166 -0
  131. package/docs/getting-started.md +290 -0
  132. package/docs/hashing.md +259 -0
  133. package/docs/health.md +225 -0
  134. package/docs/helpers.md +347 -0
  135. package/docs/hono.md +186 -0
  136. package/docs/hooks.md +118 -0
  137. package/docs/inertia.md +241 -0
  138. package/docs/locks.md +323 -0
  139. package/docs/logger.md +290 -0
  140. package/docs/mail.md +678 -0
  141. package/docs/middleware.md +425 -0
  142. package/docs/migrations.md +476 -0
  143. package/docs/models.md +810 -0
  144. package/docs/notifications.md +474 -0
  145. package/docs/providers.md +363 -0
  146. package/docs/queues.md +679 -0
  147. package/docs/rate-limiting.md +155 -0
  148. package/docs/redis.md +178 -0
  149. package/docs/request-response.md +953 -0
  150. package/docs/routing.md +804 -0
  151. package/docs/scheduling.md +110 -0
  152. package/docs/security.md +85 -0
  153. package/docs/sessions.md +354 -0
  154. package/docs/social-auth.md +174 -0
  155. package/docs/static-files.md +211 -0
  156. package/docs/storage.md +450 -0
  157. package/docs/templates.md +315 -0
  158. package/docs/testing.md +125 -0
  159. package/docs/transformers.md +381 -0
  160. package/docs/url-builder.md +295 -0
  161. package/docs/validation.md +288 -0
  162. package/docs/views.md +267 -0
  163. package/docs/vite.md +434 -0
  164. package/llms-full.txt +17694 -0
  165. package/llms.txt +116 -0
  166. package/package.json +38 -7
@@ -33,6 +33,8 @@ interface ResponseHelper {
33
33
  text(body: string, status?: number): Response;
34
34
  html(body: string, status?: number): Response;
35
35
  redirect(location: string, status?: number): Response;
36
+ /** Redirect to the `Referer` header, or `fallback` (default "/") if absent. */
37
+ back(fallback?: string, status?: number): Response;
36
38
  /** Send a value — objects become JSON, everything else becomes text. */
37
39
  send(data: unknown, status?: number): Response;
38
40
  /** Set the response status (chainable). */
@@ -47,6 +49,8 @@ interface ResponseHelper {
47
49
  hasHeader(name: string): boolean;
48
50
  /** Set the Content-Type (chainable). */
49
51
  type(mime: string): ResponseHelper;
52
+ /** Mark the response as a downloadable attachment via Content-Disposition (chainable). */
53
+ attachment(filename?: string): ResponseHelper;
50
54
  /** Append a value to a (possibly multi-value) header (chainable). */
51
55
  append(name: string, value: string): ResponseHelper;
52
56
  /** Remove a response header (chainable). */
@@ -74,6 +78,20 @@ export declare const request: {
74
78
  readonly method: string;
75
79
  readonly path: string;
76
80
  readonly url: string;
81
+ /** The request protocol — "https" or "http" — honoring X-Forwarded-Proto. */
82
+ readonly protocol: string;
83
+ /** Whether the request came in over HTTPS. */
84
+ readonly secure: boolean;
85
+ /** The host with port, honoring X-Forwarded-Host (e.g. "example.com:443"). */
86
+ readonly host: string;
87
+ /** The host without port (e.g. "example.com"). */
88
+ readonly hostname: string;
89
+ /** Scheme + host — "https://example.com" — with no trailing slash. */
90
+ readonly origin: string;
91
+ /** The absolute request URL, rebuilt from the (proxy-aware) origin. */
92
+ readonly fullUrl: string;
93
+ /** The raw query string without the leading "?" (empty when none). */
94
+ readonly querystring: string;
77
95
  /** The response status (useful after `await next()` in middleware). */
78
96
  readonly status: number;
79
97
  header(name: string): string | undefined;
@@ -128,6 +146,14 @@ export declare const request: {
128
146
  language(languages: string[]): string | null;
129
147
  /** Accepted languages, ordered by preference. */
130
148
  languages(): string[];
149
+ /** The best of the offered content encodings per Accept-Encoding, or null. */
150
+ encoding(encodings: string[]): string | null;
151
+ /** Accepted content encodings, ordered by preference. */
152
+ encodings(): string[];
153
+ /** The best of the offered charsets per Accept-Charset, or null. */
154
+ charset(charsets: string[]): string | null;
155
+ /** Accepted charsets, ordered by preference. */
156
+ charsets(): string[];
131
157
  /** A single input (from query or body), with an optional fallback (async). */
132
158
  input<T = unknown>(key: string, fallback?: T): Promise<T>;
133
159
  /** Only the named inputs (async). */
@@ -67,6 +67,20 @@ function negotiate(headerName, offered) {
67
67
  return a;
68
68
  return null;
69
69
  }
70
+ /* ------------------------------ url / host ----------------------------- */
71
+ /* Proxy-aware URL introspection: X-Forwarded-* wins over the raw URL, so an
72
+ * app behind a TLS-terminating proxy sees the client's protocol and host. */
73
+ /** The first value of a (possibly comma-listed) header. */
74
+ function firstForwarded(name) {
75
+ const v = ctx().req.header(name);
76
+ return v ? v.split(",")[0].trim() : undefined;
77
+ }
78
+ function requestProtocol() {
79
+ return firstForwarded("x-forwarded-proto") ?? new URL(ctx().req.url).protocol.replace(/:$/, "");
80
+ }
81
+ function requestHost() {
82
+ return firstForwarded("x-forwarded-host") ?? ctx().req.header("host") ?? new URL(ctx().req.url).host;
83
+ }
70
84
  /* ------------------------------ responses ------------------------------ */
71
85
  /* These work inside a handler AND standalone (e.g. as a static route value).
72
86
  * Inside a request they build on the context (merging any headers); outside a
@@ -98,6 +112,9 @@ export function html(body, status) {
98
112
  }
99
113
  export function redirect(location, status) {
100
114
  const c = maybeCtx();
115
+ // Koa-style `redirect("back")`: bounce to the Referer, or "/" if there isn't one.
116
+ if (location === "back")
117
+ location = c?.req.header("referer") ?? "/";
101
118
  return c
102
119
  ? c.redirect(location, status)
103
120
  : new Response(null, { status: status ?? 302, headers: { location } });
@@ -115,6 +132,9 @@ export const response = {
115
132
  redirect(location, status) {
116
133
  return redirect(location, status);
117
134
  },
135
+ back(fallback = "/", status) {
136
+ return redirect(ctx().req.header("referer") ?? fallback, status);
137
+ },
118
138
  send(data, status) {
119
139
  return typeof data === "object" && data !== null
120
140
  ? json(data, status)
@@ -143,6 +163,18 @@ export const response = {
143
163
  ctx().header("content-type", mime);
144
164
  return response;
145
165
  },
166
+ attachment(filename) {
167
+ if (filename === undefined) {
168
+ ctx().header("content-disposition", "attachment");
169
+ }
170
+ else {
171
+ // Quote the ASCII-safe name; add RFC 5987 filename* for anything else.
172
+ const ascii = filename.replace(/[^\x20-\x7e]/g, "?").replace(/["\\]/g, "_");
173
+ const encoded = encodeURIComponent(filename);
174
+ ctx().header("content-disposition", `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`);
175
+ }
176
+ return response;
177
+ },
146
178
  append(name, value) {
147
179
  ctx().header(name, value, { append: true });
148
180
  return response;
@@ -189,6 +221,35 @@ export const request = {
189
221
  get url() {
190
222
  return ctx().req.url;
191
223
  },
224
+ /** The request protocol — "https" or "http" — honoring X-Forwarded-Proto. */
225
+ get protocol() {
226
+ return requestProtocol();
227
+ },
228
+ /** Whether the request came in over HTTPS. */
229
+ get secure() {
230
+ return requestProtocol() === "https";
231
+ },
232
+ /** The host with port, honoring X-Forwarded-Host (e.g. "example.com:443"). */
233
+ get host() {
234
+ return requestHost();
235
+ },
236
+ /** The host without port (e.g. "example.com"). */
237
+ get hostname() {
238
+ return requestHost().split(":")[0];
239
+ },
240
+ /** Scheme + host — "https://example.com" — with no trailing slash. */
241
+ get origin() {
242
+ return `${requestProtocol()}://${requestHost()}`;
243
+ },
244
+ /** The absolute request URL, rebuilt from the (proxy-aware) origin. */
245
+ get fullUrl() {
246
+ const u = new URL(ctx().req.url);
247
+ return `${requestProtocol()}://${requestHost()}${u.pathname}${u.search}`;
248
+ },
249
+ /** The raw query string without the leading "?" (empty when none). */
250
+ get querystring() {
251
+ return new URL(ctx().req.url).search.replace(/^\?/, "");
252
+ },
192
253
  /** The response status (useful after `await next()` in middleware). */
193
254
  get status() {
194
255
  return ctx().res.status;
@@ -332,6 +393,22 @@ export const request = {
332
393
  languages() {
333
394
  return parseAccept(ctx().req.header("accept-language"));
334
395
  },
396
+ /** The best of the offered content encodings per Accept-Encoding, or null. */
397
+ encoding(encodings) {
398
+ return negotiate("accept-encoding", encodings);
399
+ },
400
+ /** Accepted content encodings, ordered by preference. */
401
+ encodings() {
402
+ return parseAccept(ctx().req.header("accept-encoding"));
403
+ },
404
+ /** The best of the offered charsets per Accept-Charset, or null. */
405
+ charset(charsets) {
406
+ return negotiate("accept-charset", charsets);
407
+ },
408
+ /** Accepted charsets, ordered by preference. */
409
+ charsets() {
410
+ return parseAccept(ctx().req.header("accept-charset"));
411
+ },
335
412
  /** A single input (from query or body), with an optional fallback (async). */
336
413
  async input(key, fallback) {
337
414
  const all = await this.all();
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Security headers — the "shield" for server-rendered apps. One middleware that
3
+ * sets the defensive HTTP headers browsers act on: a Content-Security-Policy,
4
+ * HSTS, clickjacking and MIME-sniffing guards, and a referrer policy.
5
+ *
6
+ * this.use(securityHeaders()); // sensible defaults
7
+ *
8
+ * this.use(securityHeaders({
9
+ * csp: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "https://cdn.example.com"] },
10
+ * hsts: { maxAge: 15552000, includeSubDomains: true },
11
+ * frameGuard: "DENY",
12
+ * }));
13
+ *
14
+ * Each header can be turned off with `false`. CSP takes a ready-made string or a
15
+ * directives object whose camelCase keys become `kebab-case` (`defaultSrc` →
16
+ * `default-src`). Pair with [`csrf()`](./csrf.ts) for form protection.
17
+ */
18
+ import type { MiddlewareHandler } from "hono";
19
+ export interface HstsOptions {
20
+ /** Max-age in seconds. Default 180 days. */
21
+ maxAge?: number;
22
+ /** Apply to subdomains too. Default true. */
23
+ includeSubDomains?: boolean;
24
+ /** Add `preload` (only if you've submitted to the HSTS preload list). Default false. */
25
+ preload?: boolean;
26
+ }
27
+ export interface SecurityHeadersOptions {
28
+ /** Content-Security-Policy — a raw string, a directives object, or `false` to omit. */
29
+ csp?: string | Record<string, string[]> | false;
30
+ /** Strict-Transport-Security. `true` for defaults, an object to tune, `false` to omit. Default off unless set. */
31
+ hsts?: boolean | HstsOptions;
32
+ /** X-Frame-Options clickjacking guard. Default `"SAMEORIGIN"`; `false` to omit. */
33
+ frameGuard?: false | "DENY" | "SAMEORIGIN";
34
+ /** X-Content-Type-Options: nosniff. Default true. */
35
+ noSniff?: boolean;
36
+ /** Referrer-Policy. Default `"strict-origin-when-cross-origin"`; `false` to omit. */
37
+ referrerPolicy?: string | false;
38
+ }
39
+ export declare function securityHeaders(options?: SecurityHeadersOptions): MiddlewareHandler;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Security headers — the "shield" for server-rendered apps. One middleware that
3
+ * sets the defensive HTTP headers browsers act on: a Content-Security-Policy,
4
+ * HSTS, clickjacking and MIME-sniffing guards, and a referrer policy.
5
+ *
6
+ * this.use(securityHeaders()); // sensible defaults
7
+ *
8
+ * this.use(securityHeaders({
9
+ * csp: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "https://cdn.example.com"] },
10
+ * hsts: { maxAge: 15552000, includeSubDomains: true },
11
+ * frameGuard: "DENY",
12
+ * }));
13
+ *
14
+ * Each header can be turned off with `false`. CSP takes a ready-made string or a
15
+ * directives object whose camelCase keys become `kebab-case` (`defaultSrc` →
16
+ * `default-src`). Pair with [`csrf()`](./csrf.ts) for form protection.
17
+ */
18
+ /** camelCase directive names → the kebab-case CSP spelling. */
19
+ function buildCsp(directives) {
20
+ return Object.entries(directives)
21
+ .map(([key, values]) => {
22
+ const name = key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
23
+ return values.length ? `${name} ${values.join(" ")}` : name;
24
+ })
25
+ .join("; ");
26
+ }
27
+ export function securityHeaders(options = {}) {
28
+ const frame = options.frameGuard === undefined ? "SAMEORIGIN" : options.frameGuard;
29
+ const referrer = options.referrerPolicy === undefined ? "strict-origin-when-cross-origin" : options.referrerPolicy;
30
+ const noSniff = options.noSniff !== false;
31
+ // Precompute the static header values once.
32
+ const csp = options.csp === false || options.csp === undefined
33
+ ? null
34
+ : typeof options.csp === "string"
35
+ ? options.csp
36
+ : buildCsp(options.csp);
37
+ let hstsValue = null;
38
+ if (options.hsts) {
39
+ const h = options.hsts === true ? {} : options.hsts;
40
+ const parts = [`max-age=${h.maxAge ?? 15552000}`];
41
+ if (h.includeSubDomains !== false)
42
+ parts.push("includeSubDomains");
43
+ if (h.preload)
44
+ parts.push("preload");
45
+ hstsValue = parts.join("; ");
46
+ }
47
+ return async (c, next) => {
48
+ await next();
49
+ if (csp)
50
+ c.header("Content-Security-Policy", csp);
51
+ if (hstsValue)
52
+ c.header("Strict-Transport-Security", hstsValue);
53
+ if (frame)
54
+ c.header("X-Frame-Options", frame);
55
+ if (noSniff)
56
+ c.header("X-Content-Type-Options", "nosniff");
57
+ if (referrer)
58
+ c.header("Referrer-Policy", referrer);
59
+ };
60
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Social authentication — OAuth 2.0 "sign in with GitHub/Google/…". Like Adonis
3
+ * Ally, this owns the OAuth dance only: it hands you a normalized `SocialUser`,
4
+ * and *you* find-or-create your own user and log them in (with a session,
5
+ * `jwt`, or an access `token`). It stores nothing.
6
+ *
7
+ * const github = social.github({ clientId, clientSecret, redirectUri });
8
+ *
9
+ * // 1. send the user off to the provider
10
+ * router.get("/auth/github", () => redirect(github.redirect({ state })));
11
+ *
12
+ * // 2. handle the callback
13
+ * router.get("/auth/github/callback", async () => {
14
+ * const gh = await github.user(request.query("code")); // { id, email, name, … }
15
+ * const user = await users.firstOrCreate({ github_id: gh.id }, { email: gh.email });
16
+ * auth().login(user.id);
17
+ * });
18
+ *
19
+ * Every driver is `fetch`-based — no SDK, no native deps — so it runs on Node and
20
+ * the edge alike. Presets cover GitHub, Google, and Discord; build your own with
21
+ * `oauthDriver()` for anything else OAuth2.
22
+ */
23
+ /** An OAuth token set returned by the provider's token endpoint. */
24
+ export interface OAuthToken {
25
+ accessToken: string;
26
+ tokenType?: string;
27
+ refreshToken?: string;
28
+ /** Seconds until the access token expires, if the provider says. */
29
+ expiresIn?: number;
30
+ scope?: string;
31
+ /** The raw token response, for provider-specific fields. */
32
+ raw: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * A provider's user, normalized to a common shape across every driver. `Token`
36
+ * is the OAuth2 `OAuthToken` by default, or an `OAuth1Token` for OAuth 1.0a
37
+ * providers.
38
+ */
39
+ export interface SocialUser<Token = OAuthToken> {
40
+ /** The provider's stable id for this user (always a string). */
41
+ id: string;
42
+ email: string | null;
43
+ name: string | null;
44
+ /** Username / handle (e.g. GitHub login, Discord username). */
45
+ nickname: string | null;
46
+ avatarUrl: string | null;
47
+ /** The token used to fetch this profile — for calling the provider's API. */
48
+ token: Token;
49
+ /** The raw provider profile, for fields not in the normalized shape. */
50
+ raw: Record<string, unknown>;
51
+ }
52
+ export interface OAuthConfig {
53
+ clientId: string;
54
+ clientSecret: string;
55
+ /** The callback URL registered with the provider. */
56
+ redirectUri: string;
57
+ /** Override the provider's default scopes. */
58
+ scopes?: string[];
59
+ }
60
+ export interface RedirectOptions {
61
+ /** A CSRF `state` value — generate with `oauthState()`, stash it, verify on callback. */
62
+ state?: string;
63
+ /** Scopes for this redirect (overrides config + provider defaults). */
64
+ scopes?: string[];
65
+ /** Extra query parameters to add to the authorize URL (e.g. `prompt`, `access_type`). */
66
+ params?: Record<string, string>;
67
+ }
68
+ /** The provider-specific bits an `OAuthDriver` needs. */
69
+ export interface ProviderSpec {
70
+ name: string;
71
+ authorizeUrl: string;
72
+ tokenUrl: string;
73
+ defaultScopes: string[];
74
+ /** How scopes are joined in the URL — space for most, comma for a few. */
75
+ scopeSeparator?: string;
76
+ /** Fetch and normalize the provider's user for an access token. */
77
+ fetchUser(token: OAuthToken): Promise<SocialUser>;
78
+ }
79
+ /** Thrown when the token exchange or profile fetch fails. */
80
+ export declare class OAuthError extends Error {
81
+ readonly provider: string;
82
+ constructor(message: string, provider: string);
83
+ }
84
+ /** A random, URL-safe `state` for CSRF protection — stash it, then verify on callback. */
85
+ export declare function oauthState(bytes?: number): string;
86
+ /** A generic OAuth 2.0 authorization-code driver. */
87
+ export declare class OAuthDriver {
88
+ private spec;
89
+ private config;
90
+ constructor(spec: ProviderSpec, config: OAuthConfig);
91
+ /** Build the provider's authorize URL to redirect the user to. */
92
+ redirect(options?: RedirectOptions): string;
93
+ /** Exchange an authorization `code` (from the callback) for an access token. */
94
+ exchangeCode(code: string): Promise<OAuthToken>;
95
+ /** Fetch the normalized user for an already-obtained access token. */
96
+ userFromToken(token: OAuthToken): Promise<SocialUser>;
97
+ /** The full callback step: exchange the `code`, then fetch the user. */
98
+ user(code: string): Promise<SocialUser>;
99
+ }
100
+ /** Build a driver for any OAuth2 provider from a spec + config. */
101
+ export declare function oauthDriver(spec: ProviderSpec, config: OAuthConfig): OAuthDriver;
102
+ /** GitHub OAuth (`user:email` gives access to a verified primary email). */
103
+ export declare function github(config: OAuthConfig): OAuthDriver;
104
+ /** Google OAuth / OpenID Connect. */
105
+ export declare function google(config: OAuthConfig): OAuthDriver;
106
+ /** Discord OAuth. */
107
+ export declare function discord(config: OAuthConfig): OAuthDriver;
108
+ export interface OAuth1Config {
109
+ /** Consumer (API) key. */
110
+ clientId: string;
111
+ /** Consumer (API) secret. */
112
+ clientSecret: string;
113
+ /** The `oauth_callback` URL registered with the provider. */
114
+ redirectUri: string;
115
+ }
116
+ /** An OAuth 1.0a token pair — both the request token and the final access token. */
117
+ export interface OAuth1Token {
118
+ token: string;
119
+ tokenSecret: string;
120
+ raw: Record<string, string>;
121
+ }
122
+ export interface OAuth1ProviderSpec {
123
+ name: string;
124
+ requestTokenUrl: string;
125
+ authorizeUrl: string;
126
+ accessTokenUrl: string;
127
+ fetchUser(token: OAuth1Token, driver: OAuth1Driver): Promise<SocialUser<OAuth1Token>>;
128
+ }
129
+ /**
130
+ * Compute an OAuth 1.0a HMAC-SHA1 signature (RFC 5849). `params` holds every
131
+ * signed parameter with *raw* (unencoded) values — the oauth_* fields plus any
132
+ * query/body params, minus `oauth_signature`. Exposed for signing custom API
133
+ * requests beyond the built-in flow.
134
+ */
135
+ export declare function oauth1Signature(input: {
136
+ method: string;
137
+ url: string;
138
+ params: Record<string, string>;
139
+ consumerSecret: string;
140
+ tokenSecret?: string;
141
+ }): Promise<string>;
142
+ /** A generic OAuth 1.0a driver. */
143
+ export declare class OAuth1Driver {
144
+ private spec;
145
+ private config;
146
+ constructor(spec: OAuth1ProviderSpec, config: OAuth1Config);
147
+ /** Sign a request and build its `Authorization: OAuth …` header. */
148
+ private authHeader;
149
+ /** Step 1 — obtain a temporary request token. Stash its `tokenSecret` for the callback. */
150
+ requestToken(): Promise<OAuth1Token>;
151
+ /** Step 2 — the URL to send the user to, carrying the request token. */
152
+ redirect(requestToken: string | OAuth1Token): string;
153
+ /** Step 3 — swap the callback's `oauth_token` + `oauth_verifier` for an access token. */
154
+ accessToken(oauthToken: string, verifier: string, requestTokenSecret: string): Promise<OAuth1Token>;
155
+ /** The full callback step: exchange for an access token, then fetch the user. */
156
+ user(oauthToken: string, verifier: string, requestTokenSecret: string): Promise<SocialUser<OAuth1Token>>;
157
+ /** A signed GET against the provider's API on the user's behalf (for `fetchUser`). */
158
+ get(url: string, token: OAuth1Token): Promise<Record<string, unknown>>;
159
+ }
160
+ /** Build a driver for any OAuth 1.0a provider from a spec + config. */
161
+ export declare function oauth1Driver(spec: OAuth1ProviderSpec, config: OAuth1Config): OAuth1Driver;
162
+ /** Twitter / X (OAuth 1.0a). Enable "Request email" in your app settings for `email`. */
163
+ export declare function twitter(config: OAuth1Config): OAuth1Driver;
164
+ /** All social providers under one namespace: `social.github({...})`, `social.twitter({...})`. */
165
+ export declare const social: {
166
+ github: typeof github;
167
+ google: typeof google;
168
+ discord: typeof discord;
169
+ driver: typeof oauthDriver;
170
+ state: typeof oauthState;
171
+ twitter: typeof twitter;
172
+ driver1: typeof oauth1Driver;
173
+ };