@lpdjs/firestore-repo-service 2.2.9-beta.2 → 2.2.9-beta.4

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 (41) hide show
  1. package/dist/{create-servers-xx0m3wwM.d.ts → create-servers-Dbmq3aN0.d.ts} +3 -3
  2. package/dist/{create-servers-CfLtucbQ.d.cts → create-servers-XKBCK7vV.d.cts} +3 -3
  3. package/dist/firebase-auth-CBqtAeb5.d.cts +309 -0
  4. package/dist/firebase-auth-CBqtAeb5.d.ts +309 -0
  5. package/dist/{index-Njwf8jvu.d.ts → index-BTrgC2ZA.d.cts} +7 -119
  6. package/dist/{index-BvKyjs6k.d.cts → index-CP4WoShV.d.ts} +7 -119
  7. package/dist/index.cjs +96 -96
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +6 -5
  10. package/dist/index.d.ts +6 -5
  11. package/dist/index.js +96 -96
  12. package/dist/index.js.map +1 -1
  13. package/dist/{openapi-BHZ2XHgn.d.ts → openapi-BSdeinkq.d.ts} +1 -1
  14. package/dist/{openapi-DXUE_MEP.d.cts → openapi-BSp3x2yW.d.cts} +1 -1
  15. package/dist/servers/admin/index.cjs +46 -46
  16. package/dist/servers/admin/index.cjs.map +1 -1
  17. package/dist/servers/admin/index.d.cts +3 -2
  18. package/dist/servers/admin/index.d.ts +3 -2
  19. package/dist/servers/admin/index.js +46 -46
  20. package/dist/servers/admin/index.js.map +1 -1
  21. package/dist/servers/auth/index.cjs +194 -0
  22. package/dist/servers/auth/index.cjs.map +1 -0
  23. package/dist/servers/auth/index.d.cts +13 -0
  24. package/dist/servers/auth/index.d.ts +13 -0
  25. package/dist/servers/auth/index.js +194 -0
  26. package/dist/servers/auth/index.js.map +1 -0
  27. package/dist/servers/crud/index.cjs +2 -2
  28. package/dist/servers/crud/index.cjs.map +1 -1
  29. package/dist/servers/crud/index.d.cts +5 -4
  30. package/dist/servers/crud/index.d.ts +5 -4
  31. package/dist/servers/crud/index.js +2 -2
  32. package/dist/servers/crud/index.js.map +1 -1
  33. package/dist/servers/index.cjs +108 -108
  34. package/dist/servers/index.cjs.map +1 -1
  35. package/dist/servers/index.d.cts +6 -5
  36. package/dist/servers/index.d.ts +6 -5
  37. package/dist/servers/index.js +108 -108
  38. package/dist/servers/index.js.map +1 -1
  39. package/dist/{types-C822D0dX.d.ts → types-CqXbEeXp.d.ts} +93 -6
  40. package/dist/{types-Z1Zy-2hs.d.cts → types-DptSRAK6.d.cts} +93 -6
  41. package/package.json +14 -1
@@ -1,11 +1,11 @@
1
1
  import { S as SyncQueue } from './queue-D_-aMf4H.js';
2
2
  import { F as FirestoreSyncConfig, S as SyncEvent } from './types-CX5AbZWV.js';
3
- import { O as OpenAPIDocument } from './openapi-BHZ2XHgn.js';
3
+ import { O as OpenAPIDocument } from './openapi-BSdeinkq.js';
4
4
  import * as firebase_functions_v2_https from 'firebase-functions/v2/https';
5
5
  import { onRequest, HttpsOptions } from 'firebase-functions/v2/https';
6
6
  import { a as HistoryTriggersConfig } from './read-D4xAmsbE.js';
7
- import { C as ConfiguredRepository, m as CrudServerOptions, l as CrudRepoConfig } from './types-C822D0dX.js';
8
- import { b as AdminServerOptions, A as AdminRepoConfig } from './index-Njwf8jvu.js';
7
+ import { C as ConfiguredRepository, m as CrudServerOptions, l as CrudRepoConfig } from './types-CqXbEeXp.js';
8
+ import { b as AdminServerOptions, A as AdminRepoConfig } from './index-CP4WoShV.js';
9
9
 
10
10
  /** Per-repo admin config with `repo` omitted (auto-bound from the registry key). */
11
11
  type BoundAdminRepoConfig<TRepo extends ConfiguredRepository<any>> = Omit<AdminRepoConfig<TRepo>, "repo">;
@@ -1,11 +1,11 @@
1
1
  import { S as SyncQueue } from './queue-Dk1Ezhkf.cjs';
2
2
  import { F as FirestoreSyncConfig, S as SyncEvent } from './types-CX5AbZWV.cjs';
3
- import { O as OpenAPIDocument } from './openapi-DXUE_MEP.cjs';
3
+ import { O as OpenAPIDocument } from './openapi-BSp3x2yW.cjs';
4
4
  import * as firebase_functions_v2_https from 'firebase-functions/v2/https';
5
5
  import { onRequest, HttpsOptions } from 'firebase-functions/v2/https';
6
6
  import { a as HistoryTriggersConfig } from './read-D4xAmsbE.cjs';
7
- import { C as ConfiguredRepository, m as CrudServerOptions, l as CrudRepoConfig } from './types-Z1Zy-2hs.cjs';
8
- import { b as AdminServerOptions, A as AdminRepoConfig } from './index-BvKyjs6k.cjs';
7
+ import { C as ConfiguredRepository, m as CrudServerOptions, l as CrudRepoConfig } from './types-DptSRAK6.cjs';
8
+ import { b as AdminServerOptions, A as AdminRepoConfig } from './index-BTrgC2ZA.cjs';
9
9
 
10
10
  /** Per-repo admin config with `repo` omitted (auto-bound from the registry key). */
11
11
  type BoundAdminRepoConfig<TRepo extends ConfiguredRepository<any>> = Omit<AdminRepoConfig<TRepo>, "repo">;
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Minimal zero-dependency HTTP router for Firebase Functions.
3
+ * Compatible with any Express-like (req, res) handler.
4
+ *
5
+ * Supports:
6
+ * - Named path parameters (e.g. "/repos/:name/:id")
7
+ * - GET, POST, DELETE methods
8
+ * - Global middleware (before each route)
9
+ * - 404 / error fallbacks
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { MiniRouter } from "@lpdjs/firestore-repo-service/servers/admin";
14
+ *
15
+ * // Create router
16
+ * const router = new MiniRouter();
17
+ *
18
+ * // Add global middleware (executed before every route)
19
+ * router.use(async (req, res, next) => {
20
+ * console.log(`${req.method} ${req.url}`);
21
+ * await next();
22
+ * });
23
+ *
24
+ * // Auth middleware
25
+ * router.use((req, res, next) => {
26
+ * if (!req.headers?.authorization) {
27
+ * res.status(401).send("Unauthorized");
28
+ * return;
29
+ * }
30
+ * next();
31
+ * });
32
+ *
33
+ * // Define routes with path parameters
34
+ * router.get("/users", async (req, res) => {
35
+ * res.json({ users: await getAllUsers() });
36
+ * });
37
+ *
38
+ * router.get("/users/:id", async (req, res) => {
39
+ * const user = await getUser(req.params.id); // Access path params
40
+ * if (!user) {
41
+ * res.status(404).send("User not found");
42
+ * return;
43
+ * }
44
+ * res.json(user);
45
+ * });
46
+ *
47
+ * router.post("/users", async (req, res) => {
48
+ * const user = await createUser(req.body);
49
+ * res.status(201).json(user);
50
+ * });
51
+ *
52
+ * router.delete("/users/:id", async (req, res) => {
53
+ * await deleteUser(req.params.id);
54
+ * res.status(204).end();
55
+ * });
56
+ *
57
+ * // Custom 404 handler
58
+ * router.onNotFound((req, res) => {
59
+ * res.status(404).json({ error: "Route not found", path: req.url });
60
+ * });
61
+ *
62
+ * // Custom error handler
63
+ * router.onError((err, req, res) => {
64
+ * console.error("Error:", err);
65
+ * res.status(500).json({ error: "Internal server error" });
66
+ * });
67
+ *
68
+ * // Use with Firebase Functions
69
+ * export const api = onRequest(async (req, res) => {
70
+ * await router.handle(req, res);
71
+ * });
72
+ * ```
73
+ */
74
+ type AnyReq = {
75
+ method?: string;
76
+ url?: string;
77
+ /** Express originalUrl — preserved before any router stripping, contains the full path including the Firebase Functions prefix */
78
+ originalUrl?: string;
79
+ path?: string;
80
+ headers?: Record<string, string | string[] | undefined>;
81
+ body?: unknown;
82
+ query?: Record<string, string | string[] | undefined>;
83
+ };
84
+ type AnyRes = {
85
+ status: (code: number) => AnyRes;
86
+ set: (key: string, value: string) => AnyRes;
87
+ send: (body: string) => void;
88
+ json: (body: unknown) => void;
89
+ end: () => void;
90
+ };
91
+ type RouteParams = Record<string, string>;
92
+ type RouteHandler = (req: AnyReq & {
93
+ params: RouteParams;
94
+ }, res: AnyRes) => void | Promise<void>;
95
+ type Middleware = (req: AnyReq & {
96
+ params: RouteParams;
97
+ }, res: AnyRes, next: () => void | Promise<void>) => void | Promise<void>;
98
+ declare class MiniRouter {
99
+ private routes;
100
+ private middlewares;
101
+ private notFoundHandler;
102
+ private errorHandler;
103
+ use(middleware: Middleware): this;
104
+ get(path: string, handler: RouteHandler): this;
105
+ post(path: string, handler: RouteHandler): this;
106
+ put(path: string, handler: RouteHandler): this;
107
+ patch(path: string, handler: RouteHandler): this;
108
+ delete(path: string, handler: RouteHandler): this;
109
+ onNotFound(handler: RouteHandler): this;
110
+ onError(handler: (err: unknown, req: AnyReq, res: AnyRes) => void): this;
111
+ private addRoute;
112
+ handle(req: AnyReq, res: AnyRes): Promise<void>;
113
+ private runMiddlewareChain;
114
+ }
115
+
116
+ /**
117
+ * Firebase Auth helper for the admin & CRUD servers.
118
+ *
119
+ * Returns an {@link AuthExtension} ready to plug into `servers.admin()` or
120
+ * `servers.crud()`. Supports two transport modes:
121
+ *
122
+ * - **`cookie`** — session cookie pattern (default for admin UIs). Mounts
123
+ * `/__login`, `/__session`, `/__logout` routes; the page lets the user sign
124
+ * in client-side with the Firebase JS SDK and exchanges the resulting ID
125
+ * token for an HttpOnly session cookie via Firebase Admin SDK.
126
+ * - **`bearer`** — verifies `Authorization: Bearer <idToken>` on every request
127
+ * (default for REST APIs). No login routes mounted.
128
+ * - **`both`** — accept either cookie or bearer.
129
+ *
130
+ * The helper is **agnostic** about authorization: pass an `allow` callback
131
+ * returning whatever role/context shape you need. The result is exposed as
132
+ * `req.user.context` to downstream middlewares and route handlers.
133
+ *
134
+ * @example Admin (cookie + role trio)
135
+ * ```ts
136
+ * import { firebaseAuth } from "@lpdjs/firestore-repo-service/servers/auth";
137
+ * import { getAuth } from "firebase-admin/auth";
138
+ *
139
+ * servers.admin({
140
+ * auth: firebaseAuth({
141
+ * getAuth,
142
+ * mode: "cookie",
143
+ * apiKey: process.env.FIREBASE_WEB_API_KEY!,
144
+ * authDomain: process.env.FIREBASE_AUTH_DOMAIN!,
145
+ * allow: ({ email, claims }) => {
146
+ * if (claims.superAdmin) return { role: "superAdmin" };
147
+ * if (email?.endsWith("@solarpush.io")) return { role: "admin" };
148
+ * if (email) return { role: "viewer" };
149
+ * return null;
150
+ * },
151
+ * }),
152
+ * repos: { ... },
153
+ * });
154
+ * ```
155
+ *
156
+ * @example CRUD (bearer + business rules per repo)
157
+ * ```ts
158
+ * servers.crud({
159
+ * auth: firebaseAuth({ getAuth, mode: "bearer", allow: (u) => u }),
160
+ * repos: {
161
+ * comments: {
162
+ * repo: repos.comments,
163
+ * rules: {
164
+ * list: () => true,
165
+ * get: ({ user, doc }) => doc.public || doc.authorId === user.uid,
166
+ * create: ({ user }) => !!user.uid,
167
+ * update: ({ user, doc }) => user.uid === doc.authorId,
168
+ * delete: ({ user, doc }) => user.claims.role === "moderator",
169
+ * },
170
+ * },
171
+ * },
172
+ * });
173
+ * ```
174
+ */
175
+
176
+ /**
177
+ * Minimal Firebase Admin Auth surface needed by this helper.
178
+ * Avoids a hard import of `firebase-admin/auth` so the package stays
179
+ * decoupled from a specific firebase-admin version.
180
+ */
181
+ interface FirebaseAdminAuthLike {
182
+ verifyIdToken(idToken: string, checkRevoked?: boolean): Promise<DecodedIdTokenLike>;
183
+ verifySessionCookie(sessionCookie: string, checkRevoked?: boolean): Promise<DecodedIdTokenLike>;
184
+ createSessionCookie(idToken: string, sessionCookieOptions: {
185
+ expiresIn: number;
186
+ }): Promise<string>;
187
+ revokeRefreshTokens(uid: string): Promise<void>;
188
+ }
189
+ interface DecodedIdTokenLike {
190
+ uid: string;
191
+ email?: string;
192
+ email_verified?: boolean;
193
+ [claim: string]: any;
194
+ }
195
+ /** Identity attached to every authenticated request as `req.user`. */
196
+ interface AuthUser<TContext = unknown> {
197
+ uid: string;
198
+ email: string | null;
199
+ emailVerified: boolean;
200
+ claims: Record<string, any>;
201
+ /** Result of the user-supplied `allow()` callback. */
202
+ context: TContext;
203
+ }
204
+ /** A route descriptor mounted by `firebaseAuth` before the protected chain. */
205
+ interface AuthRoute {
206
+ method: "GET" | "POST";
207
+ path: string;
208
+ handler: RouteHandler;
209
+ }
210
+ /**
211
+ * Returned by {@link firebaseAuth}. Servers detect this shape (vs.
212
+ * `BasicAuthConfig` / raw `Middleware`) and mount the routes before pushing
213
+ * the middleware onto the chain.
214
+ */
215
+ interface AuthExtension {
216
+ readonly __authExtension: true;
217
+ middleware: Middleware;
218
+ /** Auxiliary routes (login page, session, logout). Empty in pure bearer mode. */
219
+ routes: AuthRoute[];
220
+ /** Path used to redirect unauthenticated browser requests. */
221
+ loginPath: string;
222
+ }
223
+ type FirebaseAuthMode = "cookie" | "bearer" | "both";
224
+ /** Provider configuration for the bundled login page. */
225
+ interface FirebaseAuthLoginPageConfig {
226
+ /** Page title. Default: "Admin sign-in". */
227
+ title?: string;
228
+ /**
229
+ * Providers shown on the login page.
230
+ * Default: `["password", "google"]`.
231
+ */
232
+ providers?: ("password" | "google")[];
233
+ }
234
+ interface FirebaseAuthConfig<TContext = unknown> {
235
+ /** Lazy getter for the Firebase Admin Auth instance. */
236
+ getAuth: () => FirebaseAdminAuthLike;
237
+ /** Transport mode. Default: `"cookie"`. */
238
+ mode?: FirebaseAuthMode;
239
+ /**
240
+ * Authorization callback. Receives the verified token claims and returns:
241
+ * - a context object → request is allowed, exposed as `req.user.context`,
242
+ * - `null` → request is rejected (401 / redirect to login).
243
+ *
244
+ * If omitted, the default policy allows any authenticated user with
245
+ * `context = null`.
246
+ */
247
+ allow?: (user: Omit<AuthUser, "context">) => TContext | null | Promise<TContext | null>;
248
+ /**
249
+ * Whether to mount the bundled `/__login`, `/__session`, `/__logout`
250
+ * routes. Default: `true` for `cookie`/`both`, `false` for `bearer`.
251
+ */
252
+ loginPage?: boolean | FirebaseAuthLoginPageConfig;
253
+ /**
254
+ * Firebase Web API key required by the JS SDK on the login page.
255
+ * Mandatory when `loginPage` is enabled. Find it in your Firebase Console
256
+ * under Project Settings → General → Web app config.
257
+ */
258
+ apiKey?: string;
259
+ /**
260
+ * Firebase Auth domain (e.g. `my-project.firebaseapp.com`).
261
+ * Mandatory when `loginPage` is enabled.
262
+ */
263
+ authDomain?: string;
264
+ /** Cookie name. Default: `__admin_session`. */
265
+ cookieName?: string;
266
+ /** Session cookie TTL in days. Default: `5` (Firebase max is 14). */
267
+ sessionTtlDays?: number;
268
+ /**
269
+ * Cookie `Secure` flag. Default: `true`. Set to `false` only for local
270
+ * development over HTTP.
271
+ */
272
+ secureCookie?: boolean;
273
+ /** Cookie `SameSite`. Default: `"Lax"`. */
274
+ sameSite?: "Strict" | "Lax" | "None";
275
+ /**
276
+ * Behaviour when authentication fails or `allow()` returns `null`.
277
+ * - `"redirect"` (default in cookie mode) → 302 to the login page,
278
+ * - `"401"` (default in bearer mode) → JSON 401 response.
279
+ */
280
+ onUnauthenticated?: "redirect" | "401";
281
+ /**
282
+ * Routes that should bypass the auth middleware (matched against the path
283
+ * after the basePath stripping). The auxiliary login routes are always
284
+ * public regardless of this option.
285
+ */
286
+ publicPaths?: (string | RegExp)[];
287
+ }
288
+ /**
289
+ * Build a Firebase Auth extension for use with `servers.admin()` or
290
+ * `servers.crud()`. See module-level docs for the full design and examples.
291
+ */
292
+ declare function firebaseAuth<TContext = unknown>(config: FirebaseAuthConfig<TContext>): AuthExtension;
293
+ /**
294
+ * Type guard: detect an {@link AuthExtension} (vs. legacy
295
+ * `BasicAuthConfig` / `Middleware`).
296
+ */
297
+ declare function isAuthExtension(value: unknown): value is AuthExtension;
298
+ /**
299
+ * Helper for explicitly opening a CRUD operation when the server has
300
+ * `auth` defined (bypasses the default-deny policy).
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * rules: { list: allowAll, get: allowAll }
305
+ * ```
306
+ */
307
+ declare const allowAll: () => true;
308
+
309
+ export { type AuthExtension as A, type DecodedIdTokenLike as D, type FirebaseAdminAuthLike as F, MiniRouter as M, type RouteHandler as R, type Middleware as a, type AuthUser as b, allowAll as c, type AuthRoute as d, type FirebaseAuthConfig as e, firebaseAuth as f, type FirebaseAuthLoginPageConfig as g, type FirebaseAuthMode as h, isAuthExtension as i };
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Minimal zero-dependency HTTP router for Firebase Functions.
3
+ * Compatible with any Express-like (req, res) handler.
4
+ *
5
+ * Supports:
6
+ * - Named path parameters (e.g. "/repos/:name/:id")
7
+ * - GET, POST, DELETE methods
8
+ * - Global middleware (before each route)
9
+ * - 404 / error fallbacks
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { MiniRouter } from "@lpdjs/firestore-repo-service/servers/admin";
14
+ *
15
+ * // Create router
16
+ * const router = new MiniRouter();
17
+ *
18
+ * // Add global middleware (executed before every route)
19
+ * router.use(async (req, res, next) => {
20
+ * console.log(`${req.method} ${req.url}`);
21
+ * await next();
22
+ * });
23
+ *
24
+ * // Auth middleware
25
+ * router.use((req, res, next) => {
26
+ * if (!req.headers?.authorization) {
27
+ * res.status(401).send("Unauthorized");
28
+ * return;
29
+ * }
30
+ * next();
31
+ * });
32
+ *
33
+ * // Define routes with path parameters
34
+ * router.get("/users", async (req, res) => {
35
+ * res.json({ users: await getAllUsers() });
36
+ * });
37
+ *
38
+ * router.get("/users/:id", async (req, res) => {
39
+ * const user = await getUser(req.params.id); // Access path params
40
+ * if (!user) {
41
+ * res.status(404).send("User not found");
42
+ * return;
43
+ * }
44
+ * res.json(user);
45
+ * });
46
+ *
47
+ * router.post("/users", async (req, res) => {
48
+ * const user = await createUser(req.body);
49
+ * res.status(201).json(user);
50
+ * });
51
+ *
52
+ * router.delete("/users/:id", async (req, res) => {
53
+ * await deleteUser(req.params.id);
54
+ * res.status(204).end();
55
+ * });
56
+ *
57
+ * // Custom 404 handler
58
+ * router.onNotFound((req, res) => {
59
+ * res.status(404).json({ error: "Route not found", path: req.url });
60
+ * });
61
+ *
62
+ * // Custom error handler
63
+ * router.onError((err, req, res) => {
64
+ * console.error("Error:", err);
65
+ * res.status(500).json({ error: "Internal server error" });
66
+ * });
67
+ *
68
+ * // Use with Firebase Functions
69
+ * export const api = onRequest(async (req, res) => {
70
+ * await router.handle(req, res);
71
+ * });
72
+ * ```
73
+ */
74
+ type AnyReq = {
75
+ method?: string;
76
+ url?: string;
77
+ /** Express originalUrl — preserved before any router stripping, contains the full path including the Firebase Functions prefix */
78
+ originalUrl?: string;
79
+ path?: string;
80
+ headers?: Record<string, string | string[] | undefined>;
81
+ body?: unknown;
82
+ query?: Record<string, string | string[] | undefined>;
83
+ };
84
+ type AnyRes = {
85
+ status: (code: number) => AnyRes;
86
+ set: (key: string, value: string) => AnyRes;
87
+ send: (body: string) => void;
88
+ json: (body: unknown) => void;
89
+ end: () => void;
90
+ };
91
+ type RouteParams = Record<string, string>;
92
+ type RouteHandler = (req: AnyReq & {
93
+ params: RouteParams;
94
+ }, res: AnyRes) => void | Promise<void>;
95
+ type Middleware = (req: AnyReq & {
96
+ params: RouteParams;
97
+ }, res: AnyRes, next: () => void | Promise<void>) => void | Promise<void>;
98
+ declare class MiniRouter {
99
+ private routes;
100
+ private middlewares;
101
+ private notFoundHandler;
102
+ private errorHandler;
103
+ use(middleware: Middleware): this;
104
+ get(path: string, handler: RouteHandler): this;
105
+ post(path: string, handler: RouteHandler): this;
106
+ put(path: string, handler: RouteHandler): this;
107
+ patch(path: string, handler: RouteHandler): this;
108
+ delete(path: string, handler: RouteHandler): this;
109
+ onNotFound(handler: RouteHandler): this;
110
+ onError(handler: (err: unknown, req: AnyReq, res: AnyRes) => void): this;
111
+ private addRoute;
112
+ handle(req: AnyReq, res: AnyRes): Promise<void>;
113
+ private runMiddlewareChain;
114
+ }
115
+
116
+ /**
117
+ * Firebase Auth helper for the admin & CRUD servers.
118
+ *
119
+ * Returns an {@link AuthExtension} ready to plug into `servers.admin()` or
120
+ * `servers.crud()`. Supports two transport modes:
121
+ *
122
+ * - **`cookie`** — session cookie pattern (default for admin UIs). Mounts
123
+ * `/__login`, `/__session`, `/__logout` routes; the page lets the user sign
124
+ * in client-side with the Firebase JS SDK and exchanges the resulting ID
125
+ * token for an HttpOnly session cookie via Firebase Admin SDK.
126
+ * - **`bearer`** — verifies `Authorization: Bearer <idToken>` on every request
127
+ * (default for REST APIs). No login routes mounted.
128
+ * - **`both`** — accept either cookie or bearer.
129
+ *
130
+ * The helper is **agnostic** about authorization: pass an `allow` callback
131
+ * returning whatever role/context shape you need. The result is exposed as
132
+ * `req.user.context` to downstream middlewares and route handlers.
133
+ *
134
+ * @example Admin (cookie + role trio)
135
+ * ```ts
136
+ * import { firebaseAuth } from "@lpdjs/firestore-repo-service/servers/auth";
137
+ * import { getAuth } from "firebase-admin/auth";
138
+ *
139
+ * servers.admin({
140
+ * auth: firebaseAuth({
141
+ * getAuth,
142
+ * mode: "cookie",
143
+ * apiKey: process.env.FIREBASE_WEB_API_KEY!,
144
+ * authDomain: process.env.FIREBASE_AUTH_DOMAIN!,
145
+ * allow: ({ email, claims }) => {
146
+ * if (claims.superAdmin) return { role: "superAdmin" };
147
+ * if (email?.endsWith("@solarpush.io")) return { role: "admin" };
148
+ * if (email) return { role: "viewer" };
149
+ * return null;
150
+ * },
151
+ * }),
152
+ * repos: { ... },
153
+ * });
154
+ * ```
155
+ *
156
+ * @example CRUD (bearer + business rules per repo)
157
+ * ```ts
158
+ * servers.crud({
159
+ * auth: firebaseAuth({ getAuth, mode: "bearer", allow: (u) => u }),
160
+ * repos: {
161
+ * comments: {
162
+ * repo: repos.comments,
163
+ * rules: {
164
+ * list: () => true,
165
+ * get: ({ user, doc }) => doc.public || doc.authorId === user.uid,
166
+ * create: ({ user }) => !!user.uid,
167
+ * update: ({ user, doc }) => user.uid === doc.authorId,
168
+ * delete: ({ user, doc }) => user.claims.role === "moderator",
169
+ * },
170
+ * },
171
+ * },
172
+ * });
173
+ * ```
174
+ */
175
+
176
+ /**
177
+ * Minimal Firebase Admin Auth surface needed by this helper.
178
+ * Avoids a hard import of `firebase-admin/auth` so the package stays
179
+ * decoupled from a specific firebase-admin version.
180
+ */
181
+ interface FirebaseAdminAuthLike {
182
+ verifyIdToken(idToken: string, checkRevoked?: boolean): Promise<DecodedIdTokenLike>;
183
+ verifySessionCookie(sessionCookie: string, checkRevoked?: boolean): Promise<DecodedIdTokenLike>;
184
+ createSessionCookie(idToken: string, sessionCookieOptions: {
185
+ expiresIn: number;
186
+ }): Promise<string>;
187
+ revokeRefreshTokens(uid: string): Promise<void>;
188
+ }
189
+ interface DecodedIdTokenLike {
190
+ uid: string;
191
+ email?: string;
192
+ email_verified?: boolean;
193
+ [claim: string]: any;
194
+ }
195
+ /** Identity attached to every authenticated request as `req.user`. */
196
+ interface AuthUser<TContext = unknown> {
197
+ uid: string;
198
+ email: string | null;
199
+ emailVerified: boolean;
200
+ claims: Record<string, any>;
201
+ /** Result of the user-supplied `allow()` callback. */
202
+ context: TContext;
203
+ }
204
+ /** A route descriptor mounted by `firebaseAuth` before the protected chain. */
205
+ interface AuthRoute {
206
+ method: "GET" | "POST";
207
+ path: string;
208
+ handler: RouteHandler;
209
+ }
210
+ /**
211
+ * Returned by {@link firebaseAuth}. Servers detect this shape (vs.
212
+ * `BasicAuthConfig` / raw `Middleware`) and mount the routes before pushing
213
+ * the middleware onto the chain.
214
+ */
215
+ interface AuthExtension {
216
+ readonly __authExtension: true;
217
+ middleware: Middleware;
218
+ /** Auxiliary routes (login page, session, logout). Empty in pure bearer mode. */
219
+ routes: AuthRoute[];
220
+ /** Path used to redirect unauthenticated browser requests. */
221
+ loginPath: string;
222
+ }
223
+ type FirebaseAuthMode = "cookie" | "bearer" | "both";
224
+ /** Provider configuration for the bundled login page. */
225
+ interface FirebaseAuthLoginPageConfig {
226
+ /** Page title. Default: "Admin sign-in". */
227
+ title?: string;
228
+ /**
229
+ * Providers shown on the login page.
230
+ * Default: `["password", "google"]`.
231
+ */
232
+ providers?: ("password" | "google")[];
233
+ }
234
+ interface FirebaseAuthConfig<TContext = unknown> {
235
+ /** Lazy getter for the Firebase Admin Auth instance. */
236
+ getAuth: () => FirebaseAdminAuthLike;
237
+ /** Transport mode. Default: `"cookie"`. */
238
+ mode?: FirebaseAuthMode;
239
+ /**
240
+ * Authorization callback. Receives the verified token claims and returns:
241
+ * - a context object → request is allowed, exposed as `req.user.context`,
242
+ * - `null` → request is rejected (401 / redirect to login).
243
+ *
244
+ * If omitted, the default policy allows any authenticated user with
245
+ * `context = null`.
246
+ */
247
+ allow?: (user: Omit<AuthUser, "context">) => TContext | null | Promise<TContext | null>;
248
+ /**
249
+ * Whether to mount the bundled `/__login`, `/__session`, `/__logout`
250
+ * routes. Default: `true` for `cookie`/`both`, `false` for `bearer`.
251
+ */
252
+ loginPage?: boolean | FirebaseAuthLoginPageConfig;
253
+ /**
254
+ * Firebase Web API key required by the JS SDK on the login page.
255
+ * Mandatory when `loginPage` is enabled. Find it in your Firebase Console
256
+ * under Project Settings → General → Web app config.
257
+ */
258
+ apiKey?: string;
259
+ /**
260
+ * Firebase Auth domain (e.g. `my-project.firebaseapp.com`).
261
+ * Mandatory when `loginPage` is enabled.
262
+ */
263
+ authDomain?: string;
264
+ /** Cookie name. Default: `__admin_session`. */
265
+ cookieName?: string;
266
+ /** Session cookie TTL in days. Default: `5` (Firebase max is 14). */
267
+ sessionTtlDays?: number;
268
+ /**
269
+ * Cookie `Secure` flag. Default: `true`. Set to `false` only for local
270
+ * development over HTTP.
271
+ */
272
+ secureCookie?: boolean;
273
+ /** Cookie `SameSite`. Default: `"Lax"`. */
274
+ sameSite?: "Strict" | "Lax" | "None";
275
+ /**
276
+ * Behaviour when authentication fails or `allow()` returns `null`.
277
+ * - `"redirect"` (default in cookie mode) → 302 to the login page,
278
+ * - `"401"` (default in bearer mode) → JSON 401 response.
279
+ */
280
+ onUnauthenticated?: "redirect" | "401";
281
+ /**
282
+ * Routes that should bypass the auth middleware (matched against the path
283
+ * after the basePath stripping). The auxiliary login routes are always
284
+ * public regardless of this option.
285
+ */
286
+ publicPaths?: (string | RegExp)[];
287
+ }
288
+ /**
289
+ * Build a Firebase Auth extension for use with `servers.admin()` or
290
+ * `servers.crud()`. See module-level docs for the full design and examples.
291
+ */
292
+ declare function firebaseAuth<TContext = unknown>(config: FirebaseAuthConfig<TContext>): AuthExtension;
293
+ /**
294
+ * Type guard: detect an {@link AuthExtension} (vs. legacy
295
+ * `BasicAuthConfig` / `Middleware`).
296
+ */
297
+ declare function isAuthExtension(value: unknown): value is AuthExtension;
298
+ /**
299
+ * Helper for explicitly opening a CRUD operation when the server has
300
+ * `auth` defined (bypasses the default-deny policy).
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * rules: { list: allowAll, get: allowAll }
305
+ * ```
306
+ */
307
+ declare const allowAll: () => true;
308
+
309
+ export { type AuthExtension as A, type DecodedIdTokenLike as D, type FirebaseAdminAuthLike as F, MiniRouter as M, type RouteHandler as R, type Middleware as a, type AuthUser as b, allowAll as c, type AuthRoute as d, type FirebaseAuthConfig as e, firebaseAuth as f, type FirebaseAuthLoginPageConfig as g, type FirebaseAuthMode as h, isAuthExtension as i };