@lastshotlabs/bunshot 0.0.13 → 0.0.18

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 (123) hide show
  1. package/README.md +2816 -1747
  2. package/dist/adapters/memoryAuth.d.ts +7 -0
  3. package/dist/adapters/memoryAuth.js +177 -2
  4. package/dist/adapters/mongoAuth.js +94 -0
  5. package/dist/adapters/sqliteAuth.d.ts +9 -0
  6. package/dist/adapters/sqliteAuth.js +190 -2
  7. package/dist/app.d.ts +120 -2
  8. package/dist/app.js +104 -4
  9. package/dist/entrypoints/queue.d.ts +2 -2
  10. package/dist/entrypoints/queue.js +1 -1
  11. package/dist/index.d.ts +24 -8
  12. package/dist/index.js +15 -5
  13. package/dist/lib/appConfig.d.ts +81 -0
  14. package/dist/lib/appConfig.js +30 -0
  15. package/dist/lib/authAdapter.d.ts +54 -0
  16. package/dist/lib/authRateLimit.d.ts +2 -0
  17. package/dist/lib/authRateLimit.js +4 -0
  18. package/dist/lib/clientIp.d.ts +14 -0
  19. package/dist/lib/clientIp.js +52 -0
  20. package/dist/lib/constants.d.ts +4 -0
  21. package/dist/lib/constants.js +4 -0
  22. package/dist/lib/context.d.ts +2 -0
  23. package/dist/lib/createDtoMapper.d.ts +33 -0
  24. package/dist/lib/createDtoMapper.js +69 -0
  25. package/dist/lib/crypto.d.ts +11 -0
  26. package/dist/lib/crypto.js +22 -0
  27. package/dist/lib/emailVerification.d.ts +4 -0
  28. package/dist/lib/emailVerification.js +20 -12
  29. package/dist/lib/jwt.d.ts +1 -1
  30. package/dist/lib/jwt.js +19 -6
  31. package/dist/lib/mfaChallenge.d.ts +42 -0
  32. package/dist/lib/mfaChallenge.js +293 -0
  33. package/dist/lib/oauth.d.ts +14 -1
  34. package/dist/lib/oauth.js +19 -1
  35. package/dist/lib/oauthCode.d.ts +15 -0
  36. package/dist/lib/oauthCode.js +90 -0
  37. package/dist/lib/queue.d.ts +33 -0
  38. package/dist/lib/queue.js +98 -0
  39. package/dist/lib/resetPassword.js +12 -16
  40. package/dist/lib/roles.d.ts +4 -0
  41. package/dist/lib/roles.js +27 -0
  42. package/dist/lib/session.d.ts +12 -0
  43. package/dist/lib/session.js +165 -5
  44. package/dist/lib/tenant.d.ts +15 -0
  45. package/dist/lib/tenant.js +65 -0
  46. package/dist/lib/ws.js +5 -1
  47. package/dist/lib/zodToMongoose.d.ts +38 -0
  48. package/dist/lib/zodToMongoose.js +84 -0
  49. package/dist/middleware/bearerAuth.js +4 -3
  50. package/dist/middleware/botProtection.js +2 -2
  51. package/dist/middleware/cacheResponse.d.ts +1 -0
  52. package/dist/middleware/cacheResponse.js +18 -3
  53. package/dist/middleware/cors.d.ts +2 -0
  54. package/dist/middleware/cors.js +22 -8
  55. package/dist/middleware/csrf.d.ts +18 -0
  56. package/dist/middleware/csrf.js +115 -0
  57. package/dist/middleware/rateLimit.d.ts +2 -1
  58. package/dist/middleware/rateLimit.js +7 -5
  59. package/dist/middleware/requireRole.d.ts +14 -3
  60. package/dist/middleware/requireRole.js +46 -6
  61. package/dist/middleware/tenant.d.ts +5 -0
  62. package/dist/middleware/tenant.js +116 -0
  63. package/dist/models/AuthUser.d.ts +17 -0
  64. package/dist/models/AuthUser.js +17 -0
  65. package/dist/models/TenantRole.d.ts +15 -0
  66. package/dist/models/TenantRole.js +23 -0
  67. package/dist/routes/auth.d.ts +5 -3
  68. package/dist/routes/auth.js +173 -30
  69. package/dist/routes/jobs.d.ts +2 -0
  70. package/dist/routes/jobs.js +270 -0
  71. package/dist/routes/mfa.d.ts +5 -0
  72. package/dist/routes/mfa.js +616 -0
  73. package/dist/routes/oauth.js +378 -23
  74. package/dist/schemas/auth.d.ts +2 -0
  75. package/dist/schemas/auth.js +22 -1
  76. package/dist/server.d.ts +6 -0
  77. package/dist/server.js +19 -3
  78. package/dist/services/auth.d.ts +18 -5
  79. package/dist/services/auth.js +112 -18
  80. package/dist/services/mfa.d.ts +84 -0
  81. package/dist/services/mfa.js +543 -0
  82. package/dist/ws/index.js +3 -2
  83. package/docs/sections/adding-middleware/full.md +35 -0
  84. package/docs/sections/adding-models/full.md +125 -0
  85. package/docs/sections/adding-models/overview.md +13 -0
  86. package/docs/sections/adding-routes/full.md +182 -0
  87. package/docs/sections/adding-routes/overview.md +23 -0
  88. package/docs/sections/auth-flow/full.md +634 -0
  89. package/docs/sections/auth-flow/overview.md +10 -0
  90. package/docs/sections/cli/full.md +30 -0
  91. package/docs/sections/configuration/full.md +155 -0
  92. package/docs/sections/configuration/overview.md +17 -0
  93. package/docs/sections/configuration-example/full.md +117 -0
  94. package/docs/sections/configuration-example/overview.md +30 -0
  95. package/docs/sections/documentation/full.md +171 -0
  96. package/docs/sections/environment-variables/full.md +55 -0
  97. package/docs/sections/exports/full.md +92 -0
  98. package/docs/sections/extending-context/full.md +59 -0
  99. package/docs/sections/header.md +3 -0
  100. package/docs/sections/installation/full.md +6 -0
  101. package/docs/sections/jobs/full.md +140 -0
  102. package/docs/sections/jobs/overview.md +15 -0
  103. package/docs/sections/mongodb-connections/full.md +45 -0
  104. package/docs/sections/mongodb-connections/overview.md +7 -0
  105. package/docs/sections/multi-tenancy/full.md +66 -0
  106. package/docs/sections/multi-tenancy/overview.md +15 -0
  107. package/docs/sections/oauth/full.md +189 -0
  108. package/docs/sections/oauth/overview.md +16 -0
  109. package/docs/sections/package-development/full.md +7 -0
  110. package/docs/sections/peer-dependencies/full.md +47 -0
  111. package/docs/sections/quick-start/full.md +43 -0
  112. package/docs/sections/response-caching/full.md +117 -0
  113. package/docs/sections/response-caching/overview.md +13 -0
  114. package/docs/sections/roles/full.md +136 -0
  115. package/docs/sections/roles/overview.md +12 -0
  116. package/docs/sections/running-without-redis/full.md +16 -0
  117. package/docs/sections/running-without-redis-or-mongodb/full.md +60 -0
  118. package/docs/sections/stack/full.md +10 -0
  119. package/docs/sections/websocket/full.md +101 -0
  120. package/docs/sections/websocket/overview.md +5 -0
  121. package/docs/sections/websocket-rooms/full.md +97 -0
  122. package/docs/sections/websocket-rooms/overview.md +5 -0
  123. package/package.json +30 -9
@@ -0,0 +1,155 @@
1
+ ## Configuration
2
+
3
+ ```ts
4
+ await createServer({
5
+ // Required
6
+ routesDir: import.meta.dir + "/routes",
7
+
8
+ // Shared schemas (imported before routes; see "Shared schemas across routes" above)
9
+ modelSchemas: import.meta.dir + "/schemas", // string shorthand — registration: "auto"
10
+ // modelSchemas: [dir + "/schemas", dir + "/models"], // multiple dirs
11
+ // modelSchemas: { paths: dir + "/schemas", registration: "explicit" }, // full object
12
+
13
+ // App metadata (shown in root endpoint + OpenAPI docs)
14
+ app: {
15
+ name: "My App", // default: "Bun Core API"
16
+ version: "1.0.0", // default: "1.0.0"
17
+ },
18
+
19
+ // Auth, roles, and OAuth
20
+ auth: {
21
+ enabled: true, // default: true — set false to disable /auth/* routes
22
+ adapter: pgAuthAdapter, // custom adapter — overrides db.auth (use for Postgres etc.)
23
+ roles: ["admin", "editor", "user"], // valid roles — required to use requireRole
24
+ defaultRole: "user", // assigned to every new user on /auth/register
25
+ primaryField: "email", // default: "email" — use "username" or "phone" to change the login identifier
26
+ emailVerification: { // optional — only active when primaryField is "email"
27
+ required: true, // default: false (soft gate) — set true to block login until verified
28
+ tokenExpiry: 60 * 60, // default: 86400 (24 hours) — token TTL in seconds
29
+ onSend: async (email, token) => { // called after registration and resend — use any email provider
30
+ await resend.emails.send({ to: email, subject: "Verify your email", text: `Token: ${token}` });
31
+ },
32
+ },
33
+ passwordReset: { // optional — only active when primaryField is "email"
34
+ tokenExpiry: 60 * 60, // default: 3600 (1 hour) — token TTL in seconds
35
+ onSend: async (email, token) => { // called by POST /auth/forgot-password — use any email provider
36
+ await resend.emails.send({ to: email, subject: "Reset your password", text: `Token: ${token}` });
37
+ },
38
+ },
39
+ rateLimit: { // optional — built-in auth endpoint rate limiting
40
+ login: { windowMs: 15 * 60 * 1000, max: 10 }, // default: 10 failures / 15 min
41
+ register: { windowMs: 60 * 60 * 1000, max: 5 }, // default: 5 attempts / hour (per IP)
42
+ verifyEmail: { windowMs: 15 * 60 * 1000, max: 10 }, // default: 10 attempts / 15 min (per IP)
43
+ resendVerification: { windowMs: 60 * 60 * 1000, max: 3 }, // default: 3 attempts / hour (per user)
44
+ forgotPassword: { windowMs: 15 * 60 * 1000, max: 5 }, // default: 5 attempts / 15 min (per IP)
45
+ resetPassword: { windowMs: 15 * 60 * 1000, max: 10 }, // default: 10 attempts / 15 min (per IP)
46
+ mfaVerify: { windowMs: 15 * 60 * 1000, max: 10 }, // default: 10 attempts / 15 min (per IP)
47
+ mfaResend: { windowMs: 60 * 1000, max: 5 }, // default: 5 attempts / minute (per IP)
48
+ store: "redis", // default: "redis" when Redis is enabled, else "memory"
49
+ },
50
+ sessionPolicy: { // optional — session concurrency and metadata
51
+ maxSessions: 6, // default: 6 — max simultaneous sessions per user; oldest evicted when exceeded
52
+ persistSessionMetadata: true, // default: true — keep IP/UA/timestamp row after session expires (for device detection)
53
+ includeInactiveSessions: false, // default: false — include expired/deleted sessions in GET /auth/sessions
54
+ trackLastActive: false, // default: false — update lastActiveAt on every auth'd request (adds one DB write)
55
+ },
56
+ passwordPolicy: { // optional — password complexity rules (applies to register + reset, not login)
57
+ minLength: 8, // default: 8
58
+ requireLetter: true, // default: true — at least one a–z or A–Z
59
+ requireDigit: true, // default: true — at least one 0–9
60
+ requireSpecial: false, // default: false — at least one non-alphanumeric character
61
+ },
62
+ oauth: {
63
+ providers: { google: { ... }, apple: { ... } }, // omit a provider to disable it
64
+ postRedirect: "/dashboard", // default: "/"
65
+ allowedRedirectUrls: ["https://myapp.com"], // optional — validate postRedirect against allowlist at startup
66
+ },
67
+ refreshTokens: { // optional — short-lived access + long-lived refresh tokens
68
+ accessTokenExpiry: 900, // default: 900 (15 min)
69
+ refreshTokenExpiry: 2_592_000, // default: 2_592_000 (30 days)
70
+ rotationGraceSeconds: 30, // default: 30 — old token still works briefly after rotation
71
+ },
72
+ mfa: { // optional — TOTP/MFA support (requires otpauth peer dep)
73
+ issuer: "My App", // shown in authenticator apps (default: app name)
74
+ recoveryCodes: 10, // default: 10
75
+ challengeTtlSeconds: 300, // default: 300 (5 min)
76
+ emailOtp: { // optional — email OTP as alternative MFA method
77
+ onSend: async (email, code) => {}, // called to deliver the OTP code — use any email provider
78
+ codeLength: 6, // default: 6
79
+ },
80
+ },
81
+ accountDeletion: { // optional — enables DELETE /auth/me
82
+ onBeforeDelete: async (userId) => {}, // throw to abort
83
+ onAfterDelete: async (userId) => {}, // cleanup callback
84
+ },
85
+ },
86
+
87
+ // Multi-tenancy
88
+ tenancy: {
89
+ resolution: "header", // "header" | "subdomain" | "path"
90
+ headerName: "x-tenant-id", // header name (when resolution is "header")
91
+ onResolve: async (tenantId) => ({}), // validate/load tenant — return null to reject
92
+ cacheTtlMs: 60_000, // LRU cache TTL (default: 60s, 0 to disable)
93
+ cacheMaxSize: 500, // max cached entries (default: 500)
94
+ exemptPaths: [], // extra paths that skip tenant resolution
95
+ rejectionStatus: 403, // 403 (default) or 404
96
+ },
97
+
98
+ // Job status endpoint
99
+ jobs: {
100
+ statusEndpoint: true, // default: false
101
+ auth: "userAuth", // "userAuth" | "none" | MiddlewareHandler[]
102
+ roles: ["admin"], // require roles (works with userAuth)
103
+ allowedQueues: ["export"], // whitelist — empty = nothing exposed
104
+ scopeToUser: false, // when true with userAuth, users see only their own jobs
105
+ },
106
+
107
+ // Security
108
+ security: {
109
+ cors: ["https://myapp.com"], // default: "*"
110
+ rateLimit: { windowMs: 60_000, max: 100 }, // default: 100 req/min
111
+ bearerAuth: true, // default: true — set false to disable, or { bypass: ["/my-public-route"] }
112
+ botProtection: {
113
+ fingerprintRateLimit: true, // rate-limit by HTTP fingerprint (IP-rotation resistant). default: false
114
+ blockList: ["198.51.100.0/24"], // IPv4 CIDRs or exact IPs to block with 403. default: []
115
+ },
116
+ headers: { // optional — additional security headers via Hono secureHeaders
117
+ contentSecurityPolicy: "default-src 'self'", // CSP header value
118
+ permissionsPolicy: "camera=(), microphone=()", // Permissions-Policy header value
119
+ },
120
+ trustProxy: 1, // default: false — see "Trusted Proxy" section below
121
+ csrf: { // opt-in CSRF protection for cookie-based auth
122
+ enabled: true, // default: false
123
+ exemptPaths: ["/webhooks/*"], // additional exempt paths (OAuth callbacks auto-exempt)
124
+ checkOrigin: true, // validate Origin header against CORS origins (default: true)
125
+ },
126
+ },
127
+
128
+ // Extra middleware injected after identify, before route matching
129
+ middleware: [],
130
+
131
+ // Connections & store routing (all optional — shown with defaults)
132
+ db: {
133
+ mongo: "single", // "single" | "separate" | false
134
+ redis: true, // false to skip auto-connect
135
+ sqlite: undefined, // absolute path to .db file — required when any store is "sqlite"
136
+ auth: "mongo", // "mongo" | "sqlite" | "memory" — which built-in auth adapter to use
137
+ sessions: "redis", // "redis" | "mongo" | "sqlite" | "memory"
138
+ oauthState: "redis", // default: follows sessions
139
+ cache: "redis", // global default for cacheResponse (overridable per-route)
140
+ },
141
+
142
+ // Server
143
+ port: 3000, // default: process.env.PORT ?? 3000
144
+ workersDir: import.meta.dir + "/workers", // auto-imports all .ts files after server starts
145
+ enableWorkers: true, // default: true — set false to disable auto-loading
146
+
147
+ // WebSocket (see WebSocket section for full examples)
148
+ ws: {
149
+ handler: { ... }, // override open/message/close/drain handlers
150
+ upgradeHandler: async (req, server) => { ... }, // replace default cookie-JWT upgrade logic
151
+ onRoomSubscribe(ws, room) { return true; }, // gate room subscriptions; can be async
152
+ maxMessageSize: 65_536, // default: 65536 (64 KB) — close connection on oversized messages
153
+ },
154
+ });
155
+ ```
@@ -0,0 +1,17 @@
1
+ ## Configuration
2
+
3
+ `createServer` / `createApp` accept a config object with these top-level keys:
4
+
5
+ | Key | Purpose |
6
+ |-----|---------|
7
+ | `routesDir` | **(required)** Path to auto-discovered route files |
8
+ | `app` | App name and version (shown in docs) |
9
+ | `auth` | Roles, OAuth, email verification, MFA, refresh tokens, rate limiting, account deletion |
10
+ | `db` | Connection and store routing — mongo, redis, sqlite, sessions, cache, auth adapter |
11
+ | `security` | CORS, bearer auth, rate limiting, bot protection, CSRF |
12
+ | `tenancy` | Multi-tenant resolution (header/subdomain/path) |
13
+ | `jobs` | Job status REST endpoint config |
14
+ | `ws` | WebSocket handler and upgrade overrides |
15
+ | `middleware` | Extra global middleware array |
16
+ | `modelSchemas` | Schema auto-discovery paths |
17
+ | `port`, `workersDir`, `enableWorkers` | Server options |
@@ -0,0 +1,117 @@
1
+ ## Full Configuration Example
2
+
3
+ For production apps, break config into its own file. Here's a real-world setup with MongoDB, Redis, OAuth, and email verification:
4
+
5
+ ```ts
6
+ // src/config/index.ts
7
+ import path from "path";
8
+ import {
9
+ type CreateServerConfig,
10
+ type AppMeta,
11
+ type AuthConfig,
12
+ type DbConfig,
13
+ type SecurityConfig,
14
+ type ModelSchemasConfig,
15
+ } from "@lastshotlabs/bunshot";
16
+
17
+ const app: AppMeta = {
18
+ name: "My App",
19
+ version: "1.0.0",
20
+ };
21
+
22
+ const db: DbConfig = {
23
+ mongo: "single", // "single" | "separate" | false
24
+ redis: true, // false to skip Redis
25
+ sessions: "redis", // "redis" | "mongo" | "sqlite" | "memory"
26
+ cache: "memory", // default store for cacheResponse
27
+ auth: "mongo", // "mongo" | "sqlite" | "memory"
28
+ oauthState: "memory", // where to store OAuth state tokens
29
+ };
30
+
31
+ const auth: AuthConfig = {
32
+ roles: ["admin", "user"],
33
+ defaultRole: "user",
34
+ primaryField: "email",
35
+ rateLimit: { store: "redis" },
36
+ emailVerification: {
37
+ required: true,
38
+ tokenExpiry: 60 * 60, // 1 hour
39
+ onSend: async (email, token) => {
40
+ // send verification email using any provider (Resend, SES, etc.)
41
+ },
42
+ },
43
+ oauth: {
44
+ postRedirect: "http://localhost:5175/oauth/callback",
45
+ providers: {
46
+ google: {
47
+ clientId: process.env.GOOGLE_CLIENT_ID!,
48
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
49
+ redirectUri: `http://localhost:${process.env.PORT ?? 3000}/auth/google/callback`,
50
+ },
51
+ apple: {
52
+ clientId: process.env.APPLE_CLIENT_ID!,
53
+ teamId: process.env.APPLE_TEAM_ID!,
54
+ keyId: process.env.APPLE_KEY_ID!,
55
+ privateKey: process.env.APPLE_PRIVATE_KEY!,
56
+ redirectUri: `http://localhost:${process.env.PORT ?? 3000}/auth/apple/callback`,
57
+ },
58
+ microsoft: {
59
+ tenantId: process.env.MICROSOFT_TENANT_ID!,
60
+ clientId: process.env.MICROSOFT_CLIENT_ID!,
61
+ clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
62
+ redirectUri: `http://localhost:${process.env.PORT ?? 3000}/auth/microsoft/callback`,
63
+ },
64
+ github: {
65
+ clientId: process.env.GITHUB_CLIENT_ID!,
66
+ clientSecret: process.env.GITHUB_CLIENT_SECRET!,
67
+ redirectUri: `http://localhost:${process.env.PORT ?? 3000}/auth/github/callback`,
68
+ },
69
+ },
70
+ },
71
+ };
72
+
73
+ const security: SecurityConfig = {
74
+ bearerAuth: true,
75
+ cors: ["*", "http://localhost:5173"],
76
+ botProtection: { fingerprintRateLimit: true },
77
+ };
78
+
79
+ const modelSchemas: ModelSchemasConfig = {
80
+ registration: "auto",
81
+ paths: [path.join(import.meta.dir, "../schemas/*.ts")],
82
+ };
83
+
84
+ export const appConfig: CreateServerConfig = {
85
+ app,
86
+ routesDir: path.join(import.meta.dir, "../routes"),
87
+ workersDir: path.join(import.meta.dir, "../workers"),
88
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
89
+ db,
90
+ auth,
91
+ security,
92
+ modelSchemas,
93
+ middleware: [/* your global middleware here */],
94
+ };
95
+ ```
96
+
97
+ Every field above is optional except `routesDir`. See the [Configuration](#configuration) section for the full reference.
98
+
99
+ ### Built-in endpoints
100
+
101
+ | Endpoint | Description |
102
+ |---|---|
103
+ | `POST /auth/register` | Create account, returns JWT |
104
+ | `POST /auth/login` | Login, returns JWT (includes `emailVerified` when verification is configured) |
105
+ | `POST /auth/logout` | Invalidates the current session only |
106
+ | `GET /auth/me` | Returns current user's `userId`, `email`, `emailVerified`, and `googleLinked` (requires login) |
107
+ | `POST /auth/set-password` | Set or update password (requires login) |
108
+ | `GET /auth/sessions` | List active sessions with metadata — IP, user-agent, timestamps (requires login) |
109
+ | `DELETE /auth/sessions/:sessionId` | Revoke a specific session by ID (requires login) |
110
+ | `POST /auth/verify-email` | Verify email with token (when `emailVerification` is configured) |
111
+ | `POST /auth/resend-verification` | Resend verification email (requires credentials, when `emailVerification` is configured) |
112
+ | `POST /auth/forgot-password` | Request a password reset email (when `passwordReset` is configured) |
113
+ | `POST /auth/reset-password` | Reset password using a token from the reset email (when `passwordReset` is configured) |
114
+ | `GET /health` | Health check |
115
+ | `GET /docs` | Scalar API docs UI |
116
+ | `GET /openapi.json` | OpenAPI spec |
117
+ | `WS /ws` | WebSocket endpoint (cookie-JWT auth) |
@@ -0,0 +1,30 @@
1
+ ## Full Configuration Example
2
+
3
+ For production apps, break config into its own file with MongoDB, Redis, OAuth, and email verification. See the [Configuration](#configuration) section for the full reference.
4
+
5
+ ```ts
6
+ // src/config/index.ts
7
+ import { type CreateServerConfig } from "@lastshotlabs/bunshot";
8
+
9
+ export const appConfig: CreateServerConfig = {
10
+ app: { name: "My App", version: "1.0.0" },
11
+ routesDir: import.meta.dir + "/routes",
12
+ workersDir: import.meta.dir + "/workers",
13
+ db: { mongo: "single", redis: true, sessions: "redis", cache: "memory", auth: "mongo" },
14
+ auth: { roles: ["admin", "user"], defaultRole: "user", primaryField: "email" },
15
+ security: { bearerAuth: true, cors: ["*"] },
16
+ };
17
+ ```
18
+
19
+ ### Built-in endpoints
20
+
21
+ | Endpoint | Description |
22
+ |---|---|
23
+ | `POST /auth/register` | Create account, returns JWT |
24
+ | `POST /auth/login` | Login, returns JWT |
25
+ | `POST /auth/logout` | Invalidates the current session |
26
+ | `GET /auth/me` | Current user profile |
27
+ | `GET /health` | Health check |
28
+ | `GET /docs` | Scalar API docs UI |
29
+ | `GET /openapi.json` | OpenAPI spec |
30
+ | `WS /ws` | WebSocket endpoint |
@@ -0,0 +1,171 @@
1
+ ## Documentation Generation
2
+
3
+ Bunshot ships its documentation as modular markdown sections that you can pull into your own project's README.
4
+
5
+ ### Setup
6
+
7
+ Create a `docs/` directory in your project with a config and build script:
8
+
9
+ ```
10
+ my-app/
11
+ docs/
12
+ readme.config.json
13
+ build-readme.ts
14
+ sections/
15
+ intro/
16
+ full.md
17
+ my-api/
18
+ full.md
19
+ overview.md
20
+ ```
21
+
22
+ ### Config — `docs/readme.config.json`
23
+
24
+ ```json
25
+ {
26
+ "output": "../README.md",
27
+ "separator": "---",
28
+ "sections": [
29
+ { "topic": "intro", "default": "full", "separator": false },
30
+ { "topic": "my-api", "default": "full" },
31
+ { "topic": "bunshot-auth", "file": "@lastshotlabs/bunshot/docs/auth-flow/overview.md" },
32
+ { "topic": "bunshot-config", "file": "@lastshotlabs/bunshot/docs/configuration/full.md" }
33
+ ],
34
+ "profiles": {
35
+ "short": {
36
+ "my-api": "overview"
37
+ }
38
+ }
39
+ }
40
+ ```
41
+
42
+ **Section entries:**
43
+
44
+ | Field | Description |
45
+ |-------|-------------|
46
+ | `topic` | Section identifier. Maps to `sections/{topic}/` directory when no `file` is specified. |
47
+ | `default` | Variant to use: `"full"` or `"overview"`. Falls back to `"full"` if the requested variant doesn't exist. |
48
+ | `file` | Explicit file path. Supports relative paths (`sections/header.md`) and package paths (`@lastshotlabs/bunshot/docs/auth-flow/overview.md`). |
49
+ | `separator` | `true`/`false` — whether to insert `---` before this section. Defaults to `true` (except the first section). |
50
+
51
+ **Profiles** override specific sections' variants. Only list sections you want to change:
52
+
53
+ ```json
54
+ "profiles": {
55
+ "short": { "my-api": "overview", "bunshot-auth": "overview" }
56
+ }
57
+ ```
58
+
59
+ ### Build script — `docs/build-readme.ts`
60
+
61
+ Copy this into your project:
62
+
63
+ ```ts
64
+ const configPath = import.meta.dir + "/readme.config.json";
65
+ const config = await Bun.file(configPath).json();
66
+ const profile = Bun.argv[2];
67
+ const overrides: Record<string, string> = profile
68
+ ? config.profiles?.[profile] ?? {}
69
+ : {};
70
+ const separator: string = config.separator ?? "---";
71
+
72
+ if (profile && !config.profiles?.[profile]) {
73
+ console.error(`Unknown profile: "${profile}". Available: ${Object.keys(config.profiles ?? {}).join(", ")}`);
74
+ process.exit(1);
75
+ }
76
+
77
+ function resolveFilePath(file: string): string {
78
+ if (file.startsWith("./") || file.startsWith("/") || file.startsWith("../")) {
79
+ return import.meta.dir + "/" + file;
80
+ }
81
+ if (file.includes("/") && !file.startsWith("sections")) {
82
+ const resolved = import.meta.resolve(file);
83
+ return resolved.replace(/^file:\/\/\//, "");
84
+ }
85
+ return import.meta.dir + "/" + file;
86
+ }
87
+
88
+ const parts: string[] = [
89
+ "<!-- AUTO-GENERATED — edit docs/sections/, not this file. Run: bun run readme -->",
90
+ ];
91
+
92
+ for (let i = 0; i < config.sections.length; i++) {
93
+ const section = config.sections[i];
94
+
95
+ let filePath: string;
96
+ if (section.file) {
97
+ filePath = resolveFilePath(section.file);
98
+ } else {
99
+ const variant = overrides[section.topic] ?? section.default ?? "full";
100
+ const candidate = `${import.meta.dir}/sections/${section.topic}/${variant}.md`;
101
+ filePath = (await Bun.file(candidate).exists())
102
+ ? candidate
103
+ : `${import.meta.dir}/sections/${section.topic}/full.md`;
104
+ }
105
+
106
+ const content = (await Bun.file(filePath).text()).replace(/\r\n/g, "\n");
107
+
108
+ const useSeparator = section.separator !== undefined ? section.separator : i > 0;
109
+ if (useSeparator) parts.push(separator);
110
+
111
+ parts.push(content.trimEnd());
112
+ }
113
+
114
+ const outputPath = import.meta.dir + "/" + (config.output ?? "../README.md");
115
+ await Bun.write(outputPath, parts.join("\n\n") + "\n");
116
+ console.log(
117
+ `README.md compiled (${config.sections.length} sections${profile ? `, profile: ${profile}` : ""})`
118
+ );
119
+ ```
120
+
121
+ ### Add to package.json
122
+
123
+ ```json
124
+ "scripts": {
125
+ "readme": "bun docs/build-readme.ts",
126
+ "readme:short": "bun docs/build-readme.ts short"
127
+ }
128
+ ```
129
+
130
+ ### Available bunshot sections
131
+
132
+ Pull any of these into your project's README via `"file": "@lastshotlabs/bunshot/docs/{section}/{variant}.md"`:
133
+
134
+ | Section | Variants |
135
+ |---------|----------|
136
+ | `quick-start` | `full` |
137
+ | `stack` | `full` |
138
+ | `cli` | `full` |
139
+ | `installation` | `full` |
140
+ | `configuration-example` | `full`, `overview` |
141
+ | `adding-routes` | `full`, `overview` |
142
+ | `mongodb-connections` | `full`, `overview` |
143
+ | `adding-models` | `full`, `overview` |
144
+ | `jobs` | `full`, `overview` |
145
+ | `websocket` | `full`, `overview` |
146
+ | `websocket-rooms` | `full`, `overview` |
147
+ | `adding-middleware` | `full` |
148
+ | `response-caching` | `full`, `overview` |
149
+ | `extending-context` | `full` |
150
+ | `configuration` | `full`, `overview` |
151
+ | `running-without-redis` | `full` |
152
+ | `running-without-redis-or-mongodb` | `full` |
153
+ | `auth-flow` | `full`, `overview` |
154
+ | `roles` | `full`, `overview` |
155
+ | `multi-tenancy` | `full`, `overview` |
156
+ | `oauth` | `full`, `overview` |
157
+ | `peer-dependencies` | `full` |
158
+ | `environment-variables` | `full` |
159
+ | `exports` | `full` |
160
+
161
+ ### Writing your own sections
162
+
163
+ Each section file is self-contained markdown starting with a `## Heading`. Create `docs/sections/{topic}/full.md` and optionally `overview.md`:
164
+
165
+ ```markdown
166
+ ## My Feature
167
+
168
+ Description and code examples here...
169
+ ```
170
+
171
+ The `---` separators between sections are inserted by the build script — don't include them in section files.
@@ -0,0 +1,55 @@
1
+ ## Environment Variables
2
+
3
+ ```env
4
+ NODE_ENV=development
5
+ PORT=...
6
+
7
+ # MongoDB (single connection — used by connectMongo())
8
+ MONGO_USER_DEV=...
9
+ MONGO_PW_DEV=...
10
+ MONGO_HOST_DEV=...
11
+ MONGO_DB_DEV=...
12
+ MONGO_USER_PROD=...
13
+ MONGO_PW_PROD=...
14
+ MONGO_HOST_PROD=...
15
+ MONGO_DB_PROD=...
16
+
17
+ # MongoDB auth connection (separate server — used by connectAuthMongo())
18
+ # Only needed when running auth on a different cluster from app data
19
+ MONGO_AUTH_USER_DEV=...
20
+ MONGO_AUTH_PW_DEV=...
21
+ MONGO_AUTH_HOST_DEV=...
22
+ MONGO_AUTH_DB_DEV=...
23
+ MONGO_AUTH_USER_PROD=...
24
+ MONGO_AUTH_PW_PROD=...
25
+ MONGO_AUTH_HOST_PROD=...
26
+ MONGO_AUTH_DB_PROD=...
27
+
28
+ # Redis
29
+ REDIS_HOST_DEV=host:port
30
+ REDIS_USER_DEV=...
31
+ REDIS_PW_DEV=...
32
+ REDIS_HOST_PROD=host:port
33
+ REDIS_USER_PROD=...
34
+ REDIS_PW_PROD=...
35
+
36
+ # JWT
37
+ JWT_SECRET_DEV=...
38
+ JWT_SECRET_PROD=...
39
+
40
+ # Bearer API key (required on every non-bypassed request)
41
+ BEARER_TOKEN_DEV=...
42
+ BEARER_TOKEN_PROD=...
43
+
44
+ # Logging (optional — defaults to on in dev)
45
+ LOGGING_VERBOSE=true
46
+
47
+ # OAuth (only needed if using oauthProviders)
48
+ GOOGLE_CLIENT_ID=...
49
+ GOOGLE_CLIENT_SECRET=...
50
+
51
+ APPLE_CLIENT_ID=...
52
+ APPLE_TEAM_ID=...
53
+ APPLE_KEY_ID=...
54
+ APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."
55
+ ```
@@ -0,0 +1,92 @@
1
+ ## Exports
2
+
3
+ ```ts
4
+ import {
5
+ // Server factory
6
+ createServer, createApp,
7
+
8
+ // DB
9
+ connectMongo, connectAuthMongo, connectAppMongo, disconnectMongo,
10
+ authConnection, appConnection, mongoose,
11
+ connectRedis, disconnectRedis, getRedis,
12
+
13
+ // Jobs
14
+ createQueue, createWorker,
15
+ type Job,
16
+
17
+ // WebSocket
18
+ websocket, createWsUpgradeHandler, publish,
19
+ subscribe, unsubscribe, getSubscriptions, handleRoomActions,
20
+ getRooms, getRoomSubscribers,
21
+
22
+ // Auth utilities
23
+ signToken, verifyToken,
24
+ createSession, getSession, deleteSession, getUserSessions, getActiveSessionCount,
25
+ evictOldestSession, updateSessionLastActive, setSessionStore, deleteUserSessions,
26
+ setRefreshToken, getSessionByRefreshToken, rotateRefreshToken, // refresh token management
27
+ createVerificationToken, getVerificationToken, deleteVerificationToken, // email verification tokens
28
+ createResetToken, consumeResetToken, setPasswordResetStore, // password reset tokens
29
+ createMfaChallenge, consumeMfaChallenge, replaceMfaChallengeOtp, setMfaChallengeStore, // MFA challenge tokens
30
+ storeOAuthCode, consumeOAuthCode, setOAuthCodeStore, // OAuth one-time authorization codes
31
+ bustAuthLimit, trackAttempt, isLimited, clearMemoryRateLimitStore, // auth rate limiting — use in custom routes or admin unlocks
32
+ buildFingerprint, // HTTP fingerprint hash (IP-independent) — use in custom bot detection logic
33
+ sqliteAuthAdapter, setSqliteDb, startSqliteCleanup, // SQLite backend (persisted)
34
+ memoryAuthAdapter, clearMemoryStore, // in-memory backend (ephemeral)
35
+ setUserRoles, addUserRole, removeUserRole, // app-wide role management
36
+ getTenantRoles, setTenantRoles, addTenantRole, removeTenantRole, // tenant-scoped role management
37
+ type AuthAdapter, type OAuthProfile, type OAuthProviderConfig, type MfaChallengeData,
38
+ type AuthRateLimitConfig, type BotProtectionConfig, type BotProtectionOptions,
39
+ type LimitOpts, type RateLimitOptions,
40
+ type SessionMetadata, type SessionInfo, type RefreshResult,
41
+
42
+ // Tenancy
43
+ createTenant, deleteTenant, getTenant, listTenants, // tenant provisioning (MongoDB)
44
+ invalidateTenantCache, // invalidate LRU cache entry
45
+ type TenantInfo, type CreateTenantOptions,
46
+ type TenancyConfig, type TenantConfig,
47
+
48
+ // Middleware
49
+ bearerAuth, identify, userAuth, rateLimit,
50
+ botProtection, // CIDR blocklist + per-route bot protection
51
+ requireRole, // role-based access control (tenant-aware)
52
+ requireVerifiedEmail, // blocks unverified email addresses
53
+ cacheResponse, bustCache, bustCachePattern, setCacheStore, // response caching (tenant-namespaced)
54
+
55
+ // Crypto utilities
56
+ timingSafeEqual, // constant-time string comparison for secrets/hashes
57
+ sha256, // SHA-256 hash helper
58
+
59
+ // IP / proxy utilities
60
+ getClientIp, // centralized IP extraction — respects security.trustProxy setting
61
+ setTrustProxy, // configure trust level (called automatically by createApp)
62
+
63
+ // Utilities
64
+ HttpError, log, validate, createRouter, createRoute,
65
+ registerSchema, registerSchemas, // named OpenAPI schema registration
66
+ zodToMongoose, // Zod → Mongoose schema conversion
67
+ createDtoMapper, // DB document → API DTO mapper factory
68
+ type ZodToMongooseConfig, type ZodToMongooseRefConfig, type DtoMapperConfig,
69
+ getAppRoles, // returns the valid roles list configured at startup
70
+
71
+ // Constants
72
+ COOKIE_TOKEN, HEADER_USER_TOKEN,
73
+ COOKIE_REFRESH_TOKEN, HEADER_REFRESH_TOKEN, // refresh token cookie/header names
74
+
75
+ // Types
76
+ type AppEnv, type AppVariables,
77
+ type CreateServerConfig, type CreateAppConfig, type ModelSchemasConfig,
78
+ type DbConfig, type AppMeta, type AuthConfig, type OAuthConfig, type SecurityConfig,
79
+ type PrimaryField, type EmailVerificationConfig, type PasswordResetConfig,
80
+ type RefreshTokenConfig, type MfaConfig, type MfaEmailOtpConfig, type JobsConfig,
81
+ type AccountDeletionConfig, type PasswordPolicyConfig, type OAuthCodePayload,
82
+ type SocketData, type WsConfig,
83
+ } from "@lastshotlabs/bunshot";
84
+
85
+ // Jobs (separate entrypoint)
86
+ import {
87
+ createQueue, createWorker,
88
+ createCronWorker, cleanupStaleSchedulers, getRegisteredCronNames,
89
+ createDLQHandler,
90
+ type Job,
91
+ } from "@lastshotlabs/bunshot/queue";
92
+ ```