@lastshotlabs/bunshot 0.0.3 → 0.0.5

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 (96) hide show
  1. package/README.md +1 -0
  2. package/dist/adapters/memoryAuth.d.ts +22 -0
  3. package/dist/adapters/mongoAuth.d.ts +2 -0
  4. package/dist/adapters/sqliteAuth.d.ts +23 -0
  5. package/dist/app.d.ts +174 -0
  6. package/dist/cli.js +149 -0
  7. package/dist/index.d.ts +41 -0
  8. package/dist/index.js +2 -0
  9. package/dist/lib/HttpError.d.ts +4 -0
  10. package/dist/lib/appConfig.d.ts +20 -0
  11. package/dist/lib/authAdapter.d.ts +53 -0
  12. package/dist/lib/authRateLimit.d.ts +11 -0
  13. package/dist/lib/constants.d.ts +2 -0
  14. package/dist/lib/context.d.ts +9 -0
  15. package/dist/lib/emailVerification.d.ts +9 -0
  16. package/dist/lib/fingerprint.d.ts +6 -0
  17. package/dist/lib/jwt.d.ts +2 -0
  18. package/dist/lib/logger.d.ts +1 -0
  19. package/dist/lib/mongo.d.ts +34 -0
  20. package/dist/lib/oauth.d.ts +27 -0
  21. package/dist/lib/queue.d.ts +5 -0
  22. package/dist/lib/redis.d.ts +14 -0
  23. package/dist/lib/roles.d.ts +3 -0
  24. package/dist/lib/session.d.ts +6 -0
  25. package/dist/lib/validate.d.ts +2 -0
  26. package/dist/lib/ws.d.ts +21 -0
  27. package/dist/middleware/bearerAuth.d.ts +2 -0
  28. package/dist/middleware/botProtection.d.ts +9 -0
  29. package/dist/middleware/cacheResponse.d.ts +14 -0
  30. package/dist/middleware/cors.d.ts +2 -0
  31. package/dist/middleware/errorHandler.d.ts +2 -0
  32. package/dist/middleware/identify.d.ts +3 -0
  33. package/dist/middleware/index.d.ts +3 -0
  34. package/dist/middleware/logger.d.ts +2 -0
  35. package/dist/middleware/rateLimit.d.ts +8 -0
  36. package/dist/middleware/requireRole.d.ts +17 -0
  37. package/dist/middleware/requireVerifiedEmail.d.ts +12 -0
  38. package/dist/middleware/userAuth.d.ts +3 -0
  39. package/dist/models/AuthUser.d.ts +112 -0
  40. package/dist/routes/auth.d.ts +8 -0
  41. package/dist/routes/health.d.ts +1 -0
  42. package/dist/routes/home.d.ts +1 -0
  43. package/dist/routes/oauth.d.ts +2 -0
  44. package/dist/schemas/auth.d.ts +10 -0
  45. package/dist/server.d.ts +25 -0
  46. package/dist/services/auth.d.ts +6 -0
  47. package/dist/ws/index.d.ts +10 -0
  48. package/package.json +18 -6
  49. package/CLAUDE.md +0 -102
  50. package/bun.lock +0 -170
  51. package/src/adapters/memoryAuth.ts +0 -240
  52. package/src/adapters/mongoAuth.ts +0 -91
  53. package/src/adapters/sqliteAuth.ts +0 -320
  54. package/src/app.ts +0 -368
  55. package/src/cli.ts +0 -265
  56. package/src/index.ts +0 -52
  57. package/src/lib/HttpError.ts +0 -5
  58. package/src/lib/appConfig.ts +0 -29
  59. package/src/lib/authAdapter.ts +0 -46
  60. package/src/lib/authRateLimit.ts +0 -104
  61. package/src/lib/constants.ts +0 -2
  62. package/src/lib/context.ts +0 -17
  63. package/src/lib/emailVerification.ts +0 -105
  64. package/src/lib/fingerprint.ts +0 -43
  65. package/src/lib/jwt.ts +0 -17
  66. package/src/lib/logger.ts +0 -9
  67. package/src/lib/mongo.ts +0 -82
  68. package/src/lib/oauth.ts +0 -114
  69. package/src/lib/queue.ts +0 -18
  70. package/src/lib/redis.ts +0 -56
  71. package/src/lib/roles.ts +0 -23
  72. package/src/lib/session.ts +0 -91
  73. package/src/lib/validate.ts +0 -14
  74. package/src/lib/ws.ts +0 -82
  75. package/src/middleware/bearerAuth.ts +0 -15
  76. package/src/middleware/botProtection.ts +0 -73
  77. package/src/middleware/cacheResponse.ts +0 -189
  78. package/src/middleware/cors.ts +0 -19
  79. package/src/middleware/errorHandler.ts +0 -14
  80. package/src/middleware/identify.ts +0 -36
  81. package/src/middleware/index.ts +0 -8
  82. package/src/middleware/logger.ts +0 -9
  83. package/src/middleware/rateLimit.ts +0 -37
  84. package/src/middleware/requireRole.ts +0 -42
  85. package/src/middleware/requireVerifiedEmail.ts +0 -31
  86. package/src/middleware/userAuth.ts +0 -9
  87. package/src/models/AuthUser.ts +0 -17
  88. package/src/routes/auth.ts +0 -245
  89. package/src/routes/health.ts +0 -27
  90. package/src/routes/home.ts +0 -21
  91. package/src/routes/oauth.ts +0 -174
  92. package/src/schemas/auth.ts +0 -14
  93. package/src/server.ts +0 -91
  94. package/src/services/auth.ts +0 -59
  95. package/src/ws/index.ts +0 -42
  96. package/tsconfig.json +0 -43
package/README.md CHANGED
@@ -686,6 +686,7 @@ await createServer({
686
686
  primaryField: "email", // default: "email" — use "username" or "phone" to change the login identifier
687
687
  emailVerification: { // optional — only active when primaryField is "email"
688
688
  required: true, // default: false (soft gate) — set true to block login until verified
689
+ tokenExpiry: 60 * 60, // default: 86400 (24 hours) — token TTL in seconds
689
690
  onSend: async (email, token) => { // called after registration and resend — use any email provider
690
691
  await resend.emails.send({ to: email, subject: "Verify your email", text: `Token: ${token}` });
691
692
  },
@@ -0,0 +1,22 @@
1
+ import type { AuthAdapter } from "../lib/authAdapter";
2
+ /** Reset all in-memory state. Useful for test isolation. */
3
+ export declare const clearMemoryStore: () => void;
4
+ export declare const memoryAuthAdapter: AuthAdapter;
5
+ export declare const memoryCreateSession: (userId: string, token: string) => void;
6
+ export declare const memoryGetSession: (userId: string) => string | null;
7
+ export declare const memoryDeleteSession: (userId: string) => void;
8
+ export declare const memoryStoreOAuthState: (state: string, codeVerifier?: string, linkUserId?: string) => void;
9
+ export declare const memoryConsumeOAuthState: (state: string) => {
10
+ codeVerifier?: string;
11
+ linkUserId?: string;
12
+ } | null;
13
+ export declare const memoryGetCache: (key: string) => string | null;
14
+ export declare const memorySetCache: (key: string, value: string, ttlSeconds?: number) => void;
15
+ export declare const memoryDelCache: (key: string) => void;
16
+ export declare const memoryDelCachePattern: (pattern: string) => void;
17
+ export declare const memoryCreateVerificationToken: (token: string, userId: string, email: string, ttlSeconds: number) => void;
18
+ export declare const memoryGetVerificationToken: (token: string) => {
19
+ userId: string;
20
+ email: string;
21
+ } | null;
22
+ export declare const memoryDeleteVerificationToken: (token: string) => void;
@@ -0,0 +1,2 @@
1
+ import type { AuthAdapter } from "../lib/authAdapter";
2
+ export declare const mongoAuthAdapter: AuthAdapter;
@@ -0,0 +1,23 @@
1
+ import type { AuthAdapter } from "../lib/authAdapter";
2
+ export declare const setSqliteDb: (path: string) => void;
3
+ export declare const sqliteAuthAdapter: AuthAdapter;
4
+ export declare const sqliteCreateSession: (userId: string, token: string) => void;
5
+ export declare const sqliteGetSession: (userId: string) => string | null;
6
+ export declare const sqliteDeleteSession: (userId: string) => void;
7
+ export declare const sqliteStoreOAuthState: (state: string, codeVerifier?: string, linkUserId?: string) => void;
8
+ export declare const sqliteConsumeOAuthState: (state: string) => {
9
+ codeVerifier?: string;
10
+ linkUserId?: string;
11
+ } | null;
12
+ export declare const isSqliteReady: () => boolean;
13
+ export declare const sqliteGetCache: (key: string) => string | null;
14
+ export declare const sqliteSetCache: (key: string, value: string, ttlSeconds?: number) => void;
15
+ export declare const sqliteDelCache: (key: string) => void;
16
+ export declare const sqliteDelCachePattern: (pattern: string) => void;
17
+ export declare const sqliteCreateVerificationToken: (token: string, userId: string, email: string, ttlSeconds: number) => void;
18
+ export declare const sqliteGetVerificationToken: (token: string) => {
19
+ userId: string;
20
+ email: string;
21
+ } | null;
22
+ export declare const sqliteDeleteVerificationToken: (token: string) => void;
23
+ export declare const startSqliteCleanup: (intervalMs?: number) => ReturnType<typeof setInterval>;
package/dist/app.d.ts ADDED
@@ -0,0 +1,174 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ import type { MiddlewareHandler } from "hono";
3
+ import type { AppEnv } from "./lib/context";
4
+ import type { PrimaryField, EmailVerificationConfig } from "./lib/appConfig";
5
+ import type { AuthAdapter } from "./lib/authAdapter";
6
+ import type { OAuthProviderConfig } from "./lib/oauth";
7
+ type StoreType = "redis" | "mongo" | "sqlite" | "memory";
8
+ export interface DbConfig {
9
+ /**
10
+ * Absolute path to the SQLite database file.
11
+ * Required when any store is "sqlite".
12
+ * Example: import.meta.dir + "/../data.db"
13
+ */
14
+ sqlite?: string;
15
+ /**
16
+ * MongoDB auto-connect mode.
17
+ * - "single" (default): calls connectMongo() — auth and app share one server (MONGO_* env vars)
18
+ * - "separate": calls connectAuthMongo() + connectAppMongo() — auth on MONGO_AUTH_* server, app on MONGO_* server
19
+ * - false: skip auto-connect (call connectMongo / connectAuthMongo / connectAppMongo yourself)
20
+ */
21
+ mongo?: "single" | "separate" | false;
22
+ /**
23
+ * Auto-connect Redis before starting. Defaults to true.
24
+ * Set false to skip (e.g. when using sqlite or memory stores only).
25
+ */
26
+ redis?: boolean;
27
+ /**
28
+ * Where to store JWT sessions. Default: "redis".
29
+ * Sessions are stored on appConnection (not authConnection) so they are isolated per-app
30
+ * in "separate" mongo mode.
31
+ */
32
+ sessions?: StoreType;
33
+ /**
34
+ * Where to store OAuth state (PKCE code verifier, link user ID). Default: follows `sessions`.
35
+ */
36
+ oauthState?: StoreType;
37
+ /**
38
+ * Global default store for cacheResponse middleware. Default: "redis".
39
+ * Can be overridden per-route via cacheResponse({ store: "..." }).
40
+ */
41
+ cache?: StoreType;
42
+ /**
43
+ * Which built-in auth adapter to use for /auth/* routes.
44
+ * - "mongo" (default when mongo is enabled): Mongoose adapter (requires connectMongo)
45
+ * - "sqlite": bun:sqlite adapter (requires sqlite path)
46
+ * - "memory": in-memory Maps (ephemeral, great for tests)
47
+ * When `mongo: false`, defaults to the same store as `sessions`.
48
+ * Ignored when `auth.adapter` is explicitly passed in CreateAppConfig.
49
+ */
50
+ auth?: "mongo" | "sqlite" | "memory";
51
+ }
52
+ export interface AppMeta {
53
+ /** App name shown in the root endpoint and OpenAPI docs title. Defaults to "Bun Core API" */
54
+ name?: string;
55
+ /** Version shown in OpenAPI docs. Defaults to "1.0.0" */
56
+ version?: string;
57
+ }
58
+ export interface OAuthConfig {
59
+ /** OAuth provider credentials. Configured providers get automatic /auth/{provider} routes. */
60
+ providers?: OAuthProviderConfig;
61
+ /** Where to redirect after a successful OAuth login. Defaults to "/" */
62
+ postRedirect?: string;
63
+ }
64
+ export interface AuthRateLimitConfig {
65
+ /** Max login failures per window before the account is locked. Default: 10 per 15 min. */
66
+ login?: {
67
+ windowMs?: number;
68
+ max?: number;
69
+ };
70
+ /** Max registration attempts per IP per window. Default: 5 per hour. */
71
+ register?: {
72
+ windowMs?: number;
73
+ max?: number;
74
+ };
75
+ /** Max email verification attempts per IP per window. Default: 10 per 15 min. */
76
+ verifyEmail?: {
77
+ windowMs?: number;
78
+ max?: number;
79
+ };
80
+ /** Max resend-verification attempts per user per window. Default: 3 per hour. */
81
+ resendVerification?: {
82
+ windowMs?: number;
83
+ max?: number;
84
+ };
85
+ /**
86
+ * Store backend for auth rate limit counters.
87
+ * Defaults to "redis" when Redis is enabled, otherwise "memory".
88
+ * Use "redis" for multi-instance deployments so limits are shared across servers.
89
+ */
90
+ store?: "memory" | "redis";
91
+ }
92
+ export interface AuthConfig {
93
+ /** Set false to skip mounting /auth/* routes. Defaults to true */
94
+ enabled?: boolean;
95
+ /**
96
+ * Custom auth adapter for the built-in /auth/* routes.
97
+ * Use this for fully custom backends (e.g. Postgres).
98
+ * For built-in backends prefer `db.auth: "mongo" | "sqlite" | "memory"`.
99
+ * When both are set, this takes precedence.
100
+ */
101
+ adapter?: AuthAdapter;
102
+ /** Valid roles for this app (e.g. ["admin", "editor", "user"]). Used by requireRole middleware. */
103
+ roles?: string[];
104
+ /** Role automatically assigned to new users on registration. Must be one of roles. */
105
+ defaultRole?: string;
106
+ /** OAuth provider and redirect configuration */
107
+ oauth?: OAuthConfig;
108
+ /**
109
+ * The primary identifier field used for registration and login.
110
+ * Defaults to "email". Use "username" or "phone" for apps that identify users differently.
111
+ * Email verification is only available when primaryField is "email".
112
+ */
113
+ primaryField?: PrimaryField;
114
+ /**
115
+ * Email verification configuration. Only active when primaryField is "email".
116
+ * Provide an onSend callback to send the verification email via any provider (Resend, SendGrid, etc.).
117
+ */
118
+ emailVerification?: EmailVerificationConfig;
119
+ /** Rate limit configuration for built-in auth endpoints. */
120
+ rateLimit?: AuthRateLimitConfig;
121
+ }
122
+ export type { PrimaryField, EmailVerificationConfig };
123
+ export interface BotProtectionConfig {
124
+ /**
125
+ * List of IPv4 CIDRs (e.g. "198.51.100.0/24"), IPv4 addresses, or IPv6 addresses to block outright.
126
+ * Matched requests receive a 403 before any other processing.
127
+ * Example: ["198.51.100.0/24", "203.0.113.42"]
128
+ */
129
+ blockList?: string[];
130
+ /**
131
+ * Also rate-limit by HTTP fingerprint (User-Agent, Accept-*, Connection, browser header presence)
132
+ * in addition to IP. Bots that rotate IPs but use the same HTTP client share a bucket.
133
+ * Uses the same store as auth rate limiting (Redis or memory).
134
+ * Default: false
135
+ */
136
+ fingerprintRateLimit?: boolean;
137
+ }
138
+ export interface SecurityConfig {
139
+ /** CORS origins. Defaults to "*" */
140
+ cors?: string | string[];
141
+ /** Global rate limit. Defaults to 100 req / 60s */
142
+ rateLimit?: {
143
+ windowMs: number;
144
+ max: number;
145
+ };
146
+ /**
147
+ * Bearer auth check. Set false to disable entirely.
148
+ * Pass an object with bypass paths (merged with built-in defaults: /docs, /health, /openapi.json, etc.).
149
+ * Defaults to enabled with no extra bypass paths.
150
+ */
151
+ bearerAuth?: boolean | {
152
+ bypass?: string[];
153
+ };
154
+ /**
155
+ * Bot protection: CIDR blocklist and fingerprint-based rate limiting.
156
+ * Runs before IP rate limiting so blocked IPs are rejected immediately.
157
+ */
158
+ botProtection?: BotProtectionConfig;
159
+ }
160
+ export interface CreateAppConfig {
161
+ /** Absolute path to the service's routes directory (use import.meta.dir + "/routes") */
162
+ routesDir: string;
163
+ /** App name and version for the root endpoint and OpenAPI docs */
164
+ app?: AppMeta;
165
+ /** Auth, roles, and OAuth configuration */
166
+ auth?: AuthConfig;
167
+ /** Security: CORS, rate limiting, bearer auth */
168
+ security?: SecurityConfig;
169
+ /** Extra middleware injected after identify, before route matching */
170
+ middleware?: MiddlewareHandler<AppEnv>[];
171
+ /** Database connection and store routing configuration */
172
+ db?: DbConfig;
173
+ }
174
+ export declare const createApp: (config: CreateAppConfig) => Promise<OpenAPIHono<AppEnv>>;
package/dist/cli.js ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+ var R=import.meta.require;import{existsSync as V,mkdirSync as E,writeFileSync as J,readSync as v,rmSync as I}from"fs";import{join as z}from"path";import{spawnSync as Q}from"child_process";function W($){process.stdout.write($);let L=Buffer.alloc(1024),_=v(0,L,0,L.length,null);return L.subarray(0,_).toString().trim().replace(/\r/g,"")}var X=process.argv[2],q=process.argv[3],K=X||W("App name: ");if(!K)console.error("App name is required."),process.exit(1);var M=K.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,""),A=q||(X?M:W(`Directory (${M}): `))||M,B=z(process.cwd(),A),G=z(B,"src"),b=z(G,"routes"),C=z(G,"workers"),x=z(G,"queues"),F=z(G,"ws"),P=z(G,"services");if(V(B))console.error(`Directory "${A}" already exists.`),process.exit(1);var T=`import { createServer, type CreateServerConfig } from "@lastshotlabs/bunshot";
4
+
5
+ const roles = {
6
+ admin: "admin",
7
+ user: "user",
8
+ };
9
+
10
+ const config: CreateServerConfig = {
11
+ routesDir: import.meta.dir + "/routes",
12
+ workersDir: import.meta.dir + "/workers",
13
+ app: {
14
+ name: "${K}",
15
+ version: "1.0.0",
16
+ },
17
+ auth: {
18
+ roles: Object.values(roles),
19
+ defaultRole: roles.user,
20
+ },
21
+ security: {
22
+ cors: ["*"],
23
+ },
24
+ db: {
25
+ mongo: "single",
26
+ },
27
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
28
+ };
29
+
30
+ await createServer(config);
31
+ `,h=`# ${K}
32
+
33
+ Built with [@lastshotlabs/bunshot](https://github.com/Last-Shot-Labs/bunshot).
34
+
35
+ ## Getting started
36
+
37
+ \`\`\`bash
38
+ # fill in .env with your values
39
+ bun dev
40
+ \`\`\`
41
+
42
+ | Endpoint | Description |
43
+ |---|---|
44
+ | \`POST /auth/register\` | Create account |
45
+ | \`POST /auth/login\` | Sign in, returns JWT |
46
+ | \`GET /docs\` | OpenAPI docs (Scalar) |
47
+ | \`GET /health\` | Health check |
48
+
49
+ ## Project structure
50
+
51
+ \`\`\`
52
+ src/
53
+ index.ts # server entry point
54
+ routes/ # file-based routing (each file = a router)
55
+ workers/ # BullMQ workers (auto-imported on start)
56
+ \`\`\`
57
+
58
+ ## Adding routes
59
+
60
+ Create a file in \`src/routes/\`:
61
+
62
+ \`\`\`ts
63
+ // src/routes/products.ts
64
+ import { createRouter } from "@lastshotlabs/bunshot";
65
+ import { z } from "zod";
66
+
67
+ export const router = createRouter();
68
+
69
+ router.get("/products", (c) => c.json({ products: [] }));
70
+ \`\`\`
71
+
72
+ ## Adding models
73
+
74
+ \`\`\`ts
75
+ // src/models/Product.ts
76
+ import { appConnection, mongoose } from "@lastshotlabs/bunshot";
77
+
78
+ const ProductSchema = new mongoose.Schema({
79
+ name: { type: String, required: true },
80
+ price: { type: Number, required: true },
81
+ }, { timestamps: true });
82
+
83
+ export const Product = appConnection.model("Product", ProductSchema);
84
+ \`\`\`
85
+
86
+ ## Environment variables
87
+
88
+ See \`.env\` \u2014 fill in MongoDB, Redis, JWT, Bearer token, and OAuth provider values before running.
89
+ `,w=`NODE_ENV=development
90
+ PORT=3000
91
+
92
+ # MongoDB (single connection)
93
+ MONGO_USER_DEV=
94
+ MONGO_PW_DEV=
95
+ MONGO_HOST_DEV=
96
+ MONGO_DB_DEV=
97
+ MONGO_USER_PROD=
98
+ MONGO_PW_PROD=
99
+ MONGO_HOST_PROD=
100
+ MONGO_DB_PROD=
101
+
102
+ # MongoDB auth connection (only needed if mongo: "separate")
103
+ MONGO_AUTH_USER_DEV=
104
+ MONGO_AUTH_PW_DEV=
105
+ MONGO_AUTH_HOST_DEV=
106
+ MONGO_AUTH_DB_DEV=
107
+ MONGO_AUTH_USER_PROD=
108
+ MONGO_AUTH_PW_PROD=
109
+ MONGO_AUTH_HOST_PROD=
110
+ MONGO_AUTH_DB_PROD=
111
+
112
+ # Redis
113
+ REDIS_HOST_DEV=
114
+ REDIS_USER_DEV=
115
+ REDIS_PW_DEV=
116
+ REDIS_HOST_PROD=
117
+ REDIS_USER_PROD=
118
+ REDIS_PW_PROD=
119
+
120
+ # JWT
121
+ JWT_SECRET_DEV=
122
+ JWT_SECRET_PROD=
123
+
124
+ # Bearer API key
125
+ BEARER_TOKEN_DEV=
126
+ BEARER_TOKEN_PROD=
127
+
128
+ # OAuth \u2014 Google (optional)
129
+ GOOGLE_CLIENT_ID=
130
+ GOOGLE_CLIENT_SECRET=
131
+ GOOGLE_REDIRECT_URI=
132
+
133
+ # OAuth \u2014 Apple (optional)
134
+ APPLE_CLIENT_ID=
135
+ APPLE_TEAM_ID=
136
+ APPLE_KEY_ID=
137
+ APPLE_PRIVATE_KEY=
138
+ APPLE_REDIRECT_URI=
139
+ `;console.log(`
140
+ @lastshotlabs/bunshot \u2014 creating ${A}
141
+ `);E(B,{recursive:!0});console.log(" Running bun init...");Q("bun",["init","-y"],{cwd:B,stdio:"inherit"});var U=z(B,"index.ts");if(V(U))I(U);var Y=z(B,"package.json"),H=JSON.parse(R("fs").readFileSync(Y,"utf-8"));H.module="src/index.ts";H.scripts={dev:"bun --watch src/index.ts",start:"bun src/index.ts"};H.dependencies={...H.dependencies,"@lastshotlabs/bunshot":"*"};J(Y,JSON.stringify(H,null,2)+`
142
+ `,"utf-8");var Z=z(B,"tsconfig.json"),O=JSON.parse(R("fs").readFileSync(Z,"utf-8"));O.compilerOptions={...O.compilerOptions,paths:{"@lib/*":["./src/lib/*"],"@middleware/*":["./src/middleware/*"],"@models/*":["./src/models/*"],"@queues/*":["./src/queues/*"],"@routes/*":["./src/routes/*"],"@scripts/*":["./src/scripts/*"],"@services/*":["./src/services/*"],"@workers/*":["./src/workers/*"],"@queues":["./src/queues/index.ts"],"@jobs":["./src/queues/jobs.ts"],"@ws":["./src/ws/index.ts"]}};J(Z,JSON.stringify(O,null,2)+`
143
+ `,"utf-8");E(b,{recursive:!0});E(C,{recursive:!0});E(x,{recursive:!0});E(F,{recursive:!0});E(P,{recursive:!0});J(z(G,"index.ts"),T,"utf-8");J(z(B,".env"),w,"utf-8");J(z(B,"README.md"),h,"utf-8");console.log(" Created:");console.log(` + ${A}/src/index.ts`);console.log(` + ${A}/src/routes/`);console.log(` + ${A}/src/workers/`);console.log(` + ${A}/src/queues/`);console.log(` + ${A}/src/ws/`);console.log(` + ${A}/src/services/`);console.log(` + ${A}/.env`);console.log(` + ${A}/README.md`);console.log(`
144
+ Initializing git...`);var N=Q("git",["init"],{cwd:B,stdio:"inherit"});if(N.status!==0)console.error(" git init failed \u2014 skipping.");console.log(`
145
+ Installing dependencies...`);var u=Q("bun",["install"],{cwd:B,stdio:"inherit"});if(u.status!==0)console.error(`
146
+ bun install failed. Run it manually inside the directory.`),process.exit(1);console.log(`
147
+ Done! Next steps:
148
+ `);console.log(` cd ${A}`);console.log(" # fill in .env");console.log(` bun dev
149
+ `);
@@ -0,0 +1,41 @@
1
+ export { createApp } from "./app";
2
+ export { createServer } from "./server";
3
+ export type { CreateAppConfig, DbConfig, AppMeta, AuthConfig, AuthRateLimitConfig, OAuthConfig, SecurityConfig, BotProtectionConfig, PrimaryField, EmailVerificationConfig } from "./app";
4
+ export type { CreateServerConfig, WsConfig } from "./server";
5
+ export { getAppRoles } from "./lib/appConfig";
6
+ export { HttpError } from "./lib/HttpError";
7
+ export { COOKIE_TOKEN, HEADER_USER_TOKEN } from "./lib/constants";
8
+ export { createRouter } from "./lib/context";
9
+ export type { AppEnv, AppVariables } from "./lib/context";
10
+ export { signToken, verifyToken } from "./lib/jwt";
11
+ export { log } from "./lib/logger";
12
+ export { connectMongo, connectAuthMongo, connectAppMongo, disconnectMongo, authConnection, appConnection, mongoose } from "./lib/mongo";
13
+ export { connectRedis, disconnectRedis, getRedis } from "./lib/redis";
14
+ export { createQueue, createWorker } from "./lib/queue";
15
+ export type { Job } from "./lib/queue";
16
+ export { createSession, getSession, deleteSession, setSessionStore } from "./lib/session";
17
+ export { createVerificationToken, getVerificationToken, deleteVerificationToken } from "./lib/emailVerification";
18
+ export { bustAuthLimit, trackAttempt, isLimited } from "./lib/authRateLimit";
19
+ export type { LimitOpts } from "./lib/authRateLimit";
20
+ export { validate } from "./lib/validate";
21
+ export { bearerAuth } from "./middleware/bearerAuth";
22
+ export { botProtection } from "./middleware/botProtection";
23
+ export type { BotProtectionOptions } from "./middleware/botProtection";
24
+ export { identify } from "./middleware/identify";
25
+ export { rateLimit } from "./middleware/rateLimit";
26
+ export type { RateLimitOptions } from "./middleware/rateLimit";
27
+ export { userAuth } from "./middleware/userAuth";
28
+ export { requireRole } from "./middleware/requireRole";
29
+ export { requireVerifiedEmail } from "./middleware/requireVerifiedEmail";
30
+ export { cacheResponse, bustCache, bustCachePattern, setCacheStore } from "./middleware/cacheResponse";
31
+ export { buildFingerprint } from "./lib/fingerprint";
32
+ export { AuthUser } from "./models/AuthUser";
33
+ export { mongoAuthAdapter } from "./adapters/mongoAuth";
34
+ export { sqliteAuthAdapter, setSqliteDb, startSqliteCleanup } from "./adapters/sqliteAuth";
35
+ export { memoryAuthAdapter, clearMemoryStore } from "./adapters/memoryAuth";
36
+ export { setUserRoles, addUserRole, removeUserRole } from "./lib/roles";
37
+ export type { AuthAdapter, OAuthProfile } from "./lib/authAdapter";
38
+ export type { OAuthProviderConfig } from "./lib/oauth";
39
+ export { websocket, createWsUpgradeHandler } from "./ws/index";
40
+ export type { SocketData } from "./ws/index";
41
+ export { publish, subscribe, unsubscribe, getSubscriptions, handleRoomActions, getRooms, getRoomSubscribers } from "./lib/ws";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{createApp as v}from"./app";import{createServer as B}from"./server";import{getAppRoles as D}from"./lib/appConfig";import{HttpError as G}from"./lib/HttpError";import{COOKIE_TOKEN as I,HEADER_USER_TOKEN as J}from"./lib/constants";import{createRouter as L}from"./lib/context";import{signToken as N,verifyToken as O}from"./lib/jwt";import{log as Q}from"./lib/logger";import{connectMongo as T,connectAuthMongo as W,connectAppMongo as X,disconnectMongo as Y,authConnection as Z,appConnection as _,mongoose as $}from"./lib/mongo";import{connectRedis as y,disconnectRedis as E,getRedis as R}from"./lib/redis";import{createQueue as V,createWorker as c}from"./lib/queue";import{createSession as g,getSession as q,deleteSession as b,setSessionStore as w}from"./lib/session";import{createVerificationToken as h,getVerificationToken as n,deleteVerificationToken as p}from"./lib/emailVerification";import{bustAuthLimit as o,trackAttempt as m,isLimited as l}from"./lib/authRateLimit";import{validate as a}from"./lib/validate";import{bearerAuth as s}from"./middleware/bearerAuth";import{botProtection as r}from"./middleware/botProtection";import{identify as jj}from"./middleware/identify";import{rateLimit as vj}from"./middleware/rateLimit";import{userAuth as Bj}from"./middleware/userAuth";import{requireRole as Dj}from"./middleware/requireRole";import{requireVerifiedEmail as Gj}from"./middleware/requireVerifiedEmail";import{cacheResponse as Ij,bustCache as Jj,bustCachePattern as Kj,setCacheStore as Lj}from"./middleware/cacheResponse";import{buildFingerprint as Nj}from"./lib/fingerprint";import{AuthUser as Pj}from"./models/AuthUser";import{mongoAuthAdapter as Sj}from"./adapters/mongoAuth";import{sqliteAuthAdapter as Wj,setSqliteDb as Xj,startSqliteCleanup as Yj}from"./adapters/sqliteAuth";import{memoryAuthAdapter as _j,clearMemoryStore as $j}from"./adapters/memoryAuth";import{setUserRoles as yj,addUserRole as Ej,removeUserRole as Rj}from"./lib/roles";import{websocket as Vj,createWsUpgradeHandler as cj}from"./ws/index";import{publish as gj,subscribe as qj,unsubscribe as bj,getSubscriptions as wj,handleRoomActions as Aj,getRooms as hj,getRoomSubscribers as nj}from"./lib/ws";export{Vj as websocket,O as verifyToken,a as validate,Bj as userAuth,bj as unsubscribe,m as trackAttempt,qj as subscribe,Yj as startSqliteCleanup,Wj as sqliteAuthAdapter,N as signToken,yj as setUserRoles,Xj as setSqliteDb,w as setSessionStore,Lj as setCacheStore,Gj as requireVerifiedEmail,Dj as requireRole,Rj as removeUserRole,vj as rateLimit,gj as publish,$ as mongoose,Sj as mongoAuthAdapter,_j as memoryAuthAdapter,Q as log,l as isLimited,jj as identify,Aj as handleRoomActions,n as getVerificationToken,wj as getSubscriptions,q as getSession,hj as getRooms,nj as getRoomSubscribers,R as getRedis,D as getAppRoles,E as disconnectRedis,Y as disconnectMongo,p as deleteVerificationToken,b as deleteSession,cj as createWsUpgradeHandler,c as createWorker,h as createVerificationToken,g as createSession,B as createServer,L as createRouter,V as createQueue,v as createApp,y as connectRedis,T as connectMongo,W as connectAuthMongo,X as connectAppMongo,$j as clearMemoryStore,Ij as cacheResponse,Kj as bustCachePattern,Jj as bustCache,o as bustAuthLimit,Nj as buildFingerprint,r as botProtection,s as bearerAuth,Z as authConnection,_ as appConnection,Ej as addUserRole,G as HttpError,J as HEADER_USER_TOKEN,I as COOKIE_TOKEN,Pj as AuthUser};
@@ -0,0 +1,4 @@
1
+ export declare class HttpError extends Error {
2
+ status: number;
3
+ constructor(status: number, message: string);
4
+ }
@@ -0,0 +1,20 @@
1
+ export type PrimaryField = "email" | "username" | "phone";
2
+ export interface EmailVerificationConfig {
3
+ /** Block login until email is verified. Defaults to false (soft gate — emailVerified returned in login response). */
4
+ required?: boolean;
5
+ /** Token time-to-live in seconds. Defaults to 86 400 (24 hours). */
6
+ tokenExpiry?: number;
7
+ /** Called after registration with the identifier and verification token. Use to send the email. */
8
+ onSend: (email: string, token: string) => Promise<void>;
9
+ }
10
+ export declare const setAppName: (name: string) => void;
11
+ export declare const getAppName: () => string;
12
+ export declare const setAppRoles: (roles: string[]) => void;
13
+ export declare const getAppRoles: () => string[];
14
+ export declare const setDefaultRole: (role: string | null) => void;
15
+ export declare const getDefaultRole: () => string | null;
16
+ export declare const setPrimaryField: (field: PrimaryField) => void;
17
+ export declare const getPrimaryField: () => PrimaryField;
18
+ export declare const setEmailVerificationConfig: (config: EmailVerificationConfig | null) => void;
19
+ export declare const getEmailVerificationConfig: () => EmailVerificationConfig | null;
20
+ export declare const getTokenExpiry: () => number;
@@ -0,0 +1,53 @@
1
+ export interface OAuthProfile {
2
+ email?: string;
3
+ name?: string;
4
+ avatarUrl?: string;
5
+ }
6
+ export interface AuthAdapter {
7
+ findByEmail(email: string): Promise<{
8
+ id: string;
9
+ passwordHash: string;
10
+ } | null>;
11
+ create(email: string, passwordHash: string): Promise<{
12
+ id: string;
13
+ }>;
14
+ /** Required when using OAuth providers. Find or create a user by provider + provider user ID. */
15
+ findOrCreateByProvider?(provider: string, providerId: string, profile: OAuthProfile): Promise<{
16
+ id: string;
17
+ created: boolean;
18
+ }>;
19
+ /** Optional. Set or update the password hash for a user (used by /auth/set-password). */
20
+ setPassword?(userId: string, passwordHash: string): Promise<void>;
21
+ /** Optional. Link a provider identity to an existing user (used by /auth/:provider/link). */
22
+ linkProvider?(userId: string, provider: string, providerId: string): Promise<void>;
23
+ /** Optional. Return the roles assigned to a user (used by requireRole middleware). */
24
+ getRoles?(userId: string): Promise<string[]>;
25
+ /** Optional. Set the roles for a user, replacing any existing roles. */
26
+ setRoles?(userId: string, roles: string[]): Promise<void>;
27
+ /** Optional. Add a single role to a user without affecting their other roles. */
28
+ addRole?(userId: string, role: string): Promise<void>;
29
+ /** Optional. Remove a single role from a user without affecting their other roles. */
30
+ removeRole?(userId: string, role: string): Promise<void>;
31
+ /** Optional. Return basic profile info for a user by ID (used by GET /auth/me). */
32
+ getUser?(userId: string): Promise<{
33
+ email?: string;
34
+ providerIds?: string[];
35
+ emailVerified?: boolean;
36
+ } | null>;
37
+ /** Optional. Unlink a provider identity from a user (used by DELETE /auth/:provider/link). */
38
+ unlinkProvider?(userId: string, provider: string): Promise<void>;
39
+ /**
40
+ * Optional. Look up a user by their primary identifier (email, username, or phone depending on config).
41
+ * When provided, used instead of findByEmail for credential login/register flows.
42
+ */
43
+ findByIdentifier?(value: string): Promise<{
44
+ id: string;
45
+ passwordHash: string;
46
+ } | null>;
47
+ /** Optional. Mark a user's email address as verified (used by POST /auth/verify-email). */
48
+ setEmailVerified?(userId: string, verified: boolean): Promise<void>;
49
+ /** Optional. Return whether a user's email address has been verified. */
50
+ getEmailVerified?(userId: string): Promise<boolean>;
51
+ }
52
+ export declare const setAuthAdapter: (adapter: AuthAdapter) => void;
53
+ export declare const getAuthAdapter: () => AuthAdapter;
@@ -0,0 +1,11 @@
1
+ export declare const setAuthRateLimitStore: (store: "memory" | "redis") => void;
2
+ export interface LimitOpts {
3
+ windowMs: number;
4
+ max: number;
5
+ }
6
+ /** Returns true if the key is currently over the limit (read-only, no increment). */
7
+ export declare const isLimited: (key: string, opts: LimitOpts) => Promise<boolean>;
8
+ /** Increments the counter and returns true if now over the limit. */
9
+ export declare const trackAttempt: (key: string, opts: LimitOpts) => Promise<boolean>;
10
+ /** Resets a rate limit key. Use on login success or for admin unlock. */
11
+ export declare const bustAuthLimit: (key: string) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ export declare const COOKIE_TOKEN = "token";
2
+ export declare const HEADER_USER_TOKEN = "x-user-token";
@@ -0,0 +1,9 @@
1
+ import { OpenAPIHono } from "@hono/zod-openapi";
2
+ export type AppVariables = {
3
+ authUserId: string | null;
4
+ roles: string[] | null;
5
+ };
6
+ export type AppEnv = {
7
+ Variables: AppVariables;
8
+ };
9
+ export declare const createRouter: () => OpenAPIHono<AppEnv, {}, "/">;
@@ -0,0 +1,9 @@
1
+ type VerificationStore = "redis" | "mongo" | "sqlite" | "memory";
2
+ export declare const setEmailVerificationStore: (store: VerificationStore) => void;
3
+ export declare const createVerificationToken: (userId: string, email: string) => Promise<string>;
4
+ export declare const getVerificationToken: (token: string) => Promise<{
5
+ userId: string;
6
+ email: string;
7
+ } | null>;
8
+ export declare const deleteVerificationToken: (token: string) => Promise<void>;
9
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Builds a 12-hex-char fingerprint from stable HTTP headers.
3
+ * IP-independent: bots that rotate IPs but use the same HTTP client
4
+ * will produce the same fingerprint and share a rate-limit bucket.
5
+ */
6
+ export declare function buildFingerprint(req: Request): Promise<string>;
@@ -0,0 +1,2 @@
1
+ export declare const signToken: (userId: string) => Promise<string>;
2
+ export declare const verifyToken: (token: string) => Promise<import("jose").JWTPayload>;
@@ -0,0 +1 @@
1
+ export declare const log: (...args: unknown[]) => void;
@@ -0,0 +1,34 @@
1
+ import mongoose from "mongoose";
2
+ /**
3
+ * Named connection used exclusively for auth data (AuthUser model).
4
+ * Connected via connectAuthMongo() or connectMongo() (backward compat).
5
+ */
6
+ export declare const authConnection: mongoose.Connection;
7
+ /**
8
+ * Named connection for app/tenant data.
9
+ * Connected via connectAppMongo() or connectMongo() (backward compat).
10
+ * Use this when registering your own models: appConnection.model("Product", schema).
11
+ */
12
+ export declare const appConnection: mongoose.Connection;
13
+ /**
14
+ * Connect the auth connection to its dedicated MongoDB server.
15
+ * Uses MONGO_AUTH_USER_*, MONGO_AUTH_PW_*, MONGO_AUTH_HOST_*, MONGO_AUTH_DB_* env vars.
16
+ */
17
+ export declare const connectAuthMongo: () => Promise<void>;
18
+ /**
19
+ * Connect the app connection to its MongoDB server.
20
+ * Uses MONGO_USER_*, MONGO_PW_*, MONGO_HOST_*, MONGO_DB_* env vars.
21
+ */
22
+ export declare const connectAppMongo: () => Promise<void>;
23
+ /**
24
+ * Connect both auth and app connections to the same MongoDB server.
25
+ * Backward-compatible shorthand for single-DB setups.
26
+ * Uses MONGO_USER_*, MONGO_PW_*, MONGO_HOST_*, MONGO_DB_* env vars.
27
+ */
28
+ export declare const connectMongo: () => Promise<void>;
29
+ /**
30
+ * Close both auth and app Mongo connections.
31
+ * Useful for one-off scripts that need a clean exit.
32
+ */
33
+ export declare const disconnectMongo: () => Promise<void>;
34
+ export { mongoose };
@@ -0,0 +1,27 @@
1
+ import { Google, Apple, generateState, generateCodeVerifier } from "arctic";
2
+ export type OAuthProviderConfig = {
3
+ google?: {
4
+ clientId: string;
5
+ clientSecret: string;
6
+ redirectUri: string;
7
+ };
8
+ apple?: {
9
+ clientId: string;
10
+ teamId: string;
11
+ keyId: string;
12
+ privateKey: string;
13
+ redirectUri: string;
14
+ };
15
+ };
16
+ export declare const initOAuthProviders: (config: OAuthProviderConfig) => void;
17
+ export declare const getGoogle: () => Google;
18
+ export declare const getApple: () => Apple;
19
+ export declare const getConfiguredOAuthProviders: () => string[];
20
+ type OAuthStateStore = "redis" | "mongo" | "sqlite" | "memory";
21
+ export declare const setOAuthStateStore: (store: OAuthStateStore) => void;
22
+ export declare const storeOAuthState: (state: string, codeVerifier?: string, linkUserId?: string) => Promise<void>;
23
+ export declare const consumeOAuthState: (state: string) => Promise<{
24
+ codeVerifier?: string;
25
+ linkUserId?: string;
26
+ } | null>;
27
+ export { generateState, generateCodeVerifier };
@@ -0,0 +1,5 @@
1
+ import { Queue, Worker } from "bullmq";
2
+ import type { Processor, QueueOptions, WorkerOptions, Job } from "bullmq";
3
+ export declare const createQueue: <T = unknown, R = unknown>(name: string, options?: Omit<QueueOptions, "connection">) => Queue<T, R>;
4
+ export declare const createWorker: <T = unknown, R = unknown>(name: string, processor: Processor<T, R>, options?: Omit<WorkerOptions, "connection">) => Worker<T, R>;
5
+ export type { Job };