@lpdjs/firestore-repo-service 2.4.3 → 2.6.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +92 -50
  2. package/dist/{create-servers-B9dTUhvR.d.cts → create-servers-B4GrBqdA.d.cts} +6 -6
  3. package/dist/{create-servers-BFhdPPeo.d.ts → create-servers-CVudVM8e.d.ts} +6 -6
  4. package/dist/{firebase-auth-D1APf9PA.d.cts → firebase-auth-Dpvrd8MP.d.cts} +13 -0
  5. package/dist/{firebase-auth-D1APf9PA.d.ts → firebase-auth-Dpvrd8MP.d.ts} +13 -0
  6. package/dist/history/index.cjs +1 -1
  7. package/dist/history/index.cjs.map +1 -1
  8. package/dist/history/index.d.cts +10 -4
  9. package/dist/history/index.d.ts +10 -4
  10. package/dist/history/index.js +1 -1
  11. package/dist/history/index.js.map +1 -1
  12. package/dist/{index-BxurOEz1.d.ts → index-DzO9MfNI.d.cts} +9 -2
  13. package/dist/{index-BmagC7uw.d.cts → index-oFhGCBrY.d.ts} +9 -2
  14. package/dist/index.cjs +84 -84
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +9 -9
  17. package/dist/index.d.ts +9 -9
  18. package/dist/index.js +84 -84
  19. package/dist/index.js.map +1 -1
  20. package/dist/{openapi-ML_1hTx2.d.cts → openapi-B2w5tVRR.d.cts} +1 -1
  21. package/dist/{openapi-DIoQV_yQ.d.ts → openapi-DB8bXZB-.d.ts} +1 -1
  22. package/dist/{queue-xMOZxY0M.d.cts → queue-B8YUTnBT.d.cts} +20 -5
  23. package/dist/{queue-CVchaGAh.d.ts → queue-DYmbVDu5.d.ts} +20 -5
  24. package/dist/{read-BSyLao3I.d.cts → read-CTWZjxyh.d.cts} +1 -1
  25. package/dist/{read-BSyLao3I.d.ts → read-CTWZjxyh.d.ts} +1 -1
  26. package/dist/servers/admin/index.cjs +48 -48
  27. package/dist/servers/admin/index.cjs.map +1 -1
  28. package/dist/servers/admin/index.d.cts +4 -4
  29. package/dist/servers/admin/index.d.ts +4 -4
  30. package/dist/servers/admin/index.js +48 -48
  31. package/dist/servers/admin/index.js.map +1 -1
  32. package/dist/servers/auth/index.cjs +25 -12
  33. package/dist/servers/auth/index.cjs.map +1 -1
  34. package/dist/servers/auth/index.d.cts +1 -1
  35. package/dist/servers/auth/index.d.ts +1 -1
  36. package/dist/servers/auth/index.js +25 -12
  37. package/dist/servers/auth/index.js.map +1 -1
  38. package/dist/servers/crud/index.cjs +2 -2
  39. package/dist/servers/crud/index.cjs.map +1 -1
  40. package/dist/servers/crud/index.d.cts +6 -6
  41. package/dist/servers/crud/index.d.ts +6 -6
  42. package/dist/servers/crud/index.js +2 -2
  43. package/dist/servers/crud/index.js.map +1 -1
  44. package/dist/servers/hono/cli.cjs +142 -53
  45. package/dist/servers/hono/cli.cjs.map +1 -1
  46. package/dist/servers/hono/cli.js +142 -53
  47. package/dist/servers/hono/cli.js.map +1 -1
  48. package/dist/servers/hono/index.cjs +5 -5
  49. package/dist/servers/hono/index.cjs.map +1 -1
  50. package/dist/servers/hono/index.d.cts +241 -24
  51. package/dist/servers/hono/index.d.ts +241 -24
  52. package/dist/servers/hono/index.js +5 -5
  53. package/dist/servers/hono/index.js.map +1 -1
  54. package/dist/servers/index.cjs +98 -98
  55. package/dist/servers/index.cjs.map +1 -1
  56. package/dist/servers/index.d.cts +9 -9
  57. package/dist/servers/index.d.ts +9 -9
  58. package/dist/servers/index.js +98 -98
  59. package/dist/servers/index.js.map +1 -1
  60. package/dist/sync/bigquery.cjs +3 -3
  61. package/dist/sync/bigquery.cjs.map +1 -1
  62. package/dist/sync/bigquery.d.cts +18 -2
  63. package/dist/sync/bigquery.d.ts +18 -2
  64. package/dist/sync/bigquery.js +3 -3
  65. package/dist/sync/bigquery.js.map +1 -1
  66. package/dist/sync/index.cjs +37 -37
  67. package/dist/sync/index.cjs.map +1 -1
  68. package/dist/sync/index.d.cts +5 -5
  69. package/dist/sync/index.d.ts +5 -5
  70. package/dist/sync/index.js +37 -37
  71. package/dist/sync/index.js.map +1 -1
  72. package/dist/{types-5vgXdUM2.d.ts → types-BHZ-Gk-s.d.ts} +71 -4
  73. package/dist/{types-ChzVPw4k.d.ts → types-FLGn8CAI.d.ts} +15 -1
  74. package/dist/{types-BtdC0Qhu.d.cts → types-GvexCqrq.d.cts} +71 -4
  75. package/dist/{types-BgIGWlR1.d.cts → types-wcX7xfdo.d.cts} +15 -1
  76. package/package.json +2 -2
package/README.md CHANGED
@@ -102,20 +102,23 @@ if (post) {
102
102
  }
103
103
 
104
104
  // Update
105
- const updated = await repos.users.update("user123", { name: "New name", age: 31 });
105
+ const updated = await repos.users.update("user123", {
106
+ name: "New name",
107
+ age: 31,
108
+ });
106
109
  ```
107
110
 
108
111
  ## API reference
109
112
 
110
113
  ### `createRepositoryConfig()`
111
114
 
112
- | Option | Description |
113
- | ------------- | ------------------------------------------------------- |
114
- | `path` | Collection path in Firestore |
115
- | `isGroup` | `true` for collection group, `false` for simple |
116
- | `foreignKeys` | Keys for `get.by*` methods (single document lookup) |
117
- | `queryKeys` | Keys for `query.by*` methods (multi-document query) |
118
- | `refCb` | Function that returns the document reference |
115
+ | Option | Description |
116
+ | ------------- | --------------------------------------------------- |
117
+ | `path` | Collection path in Firestore |
118
+ | `isGroup` | `true` for collection group, `false` for simple |
119
+ | `foreignKeys` | Keys for `get.by*` methods (single document lookup) |
120
+ | `queryKeys` | Keys for `query.by*` methods (multi-document query) |
121
+ | `refCb` | Function that returns the document reference |
119
122
 
120
123
  **Sub-collection example:**
121
124
 
@@ -134,8 +137,8 @@ comments: createRepositoryConfig<CommentModel>()({
134
137
 
135
138
  ```typescript
136
139
  interface QueryOptions<T> {
137
- where?: [keyof T, WhereFilterOp, any][]; // AND conditions
138
- orWhere?: [keyof T, WhereFilterOp, any][][]; // OR conditions
140
+ where?: [keyof T, WhereFilterOp, any][]; // AND conditions
141
+ orWhere?: [keyof T, WhereFilterOp, any][][]; // OR conditions
139
142
  orderBy?: { field: keyof T; direction?: "asc" | "desc" }[];
140
143
  limit?: number;
141
144
  offset?: number;
@@ -226,7 +229,9 @@ const page = await repos.posts.query.paginate({
226
229
  ```typescript
227
230
  import { count, sum, average } from "@lpdjs/firestore-repo-service";
228
231
 
229
- const activeCount = await repos.users.aggregate.count({ where: [["isActive", "==", true]] });
232
+ const activeCount = await repos.users.aggregate.count({
233
+ where: [["isActive", "==", true]],
234
+ });
230
235
  const totalViews = await repos.posts.aggregate.sum("views");
231
236
  const avgAge = await repos.users.aggregate.average("age");
232
237
  ```
@@ -237,7 +242,9 @@ const avgAge = await repos.users.aggregate.average("age");
237
242
  const result = await repos.users.transaction.run(async (txn) => {
238
243
  const user = await txn.get(repos.users.documentRef("user123"));
239
244
  if (user.exists()) {
240
- txn.update(repos.users.documentRef("user123"), { age: user.data().age + 1 });
245
+ txn.update(repos.users.documentRef("user123"), {
246
+ age: user.data().age + 1,
247
+ });
241
248
  }
242
249
  return { success: true };
243
250
  });
@@ -249,8 +256,14 @@ const result = await repos.users.transaction.run(async (txn) => {
249
256
  // (status = 'active' AND age >= 18) OR (status = 'pending' AND verified = true)
250
257
  const users = await repos.users.query.by({
251
258
  orWhere: [
252
- [["status", "==", "active"], ["age", ">=", 18]],
253
- [["status", "==", "pending"], ["verified", "==", true]],
259
+ [
260
+ ["status", "==", "active"],
261
+ ["age", ">=", 18],
262
+ ],
263
+ [
264
+ ["status", "==", "pending"],
265
+ ["verified", "==", true],
266
+ ],
254
267
  ],
255
268
  });
256
269
  ```
@@ -304,9 +317,9 @@ npm i -D @asteasolutions/zod-to-openapi
304
317
  ### Bootstrap
305
318
 
306
319
  ```bash
307
- npx frs-hono init # interactive — creates apis.ts + manifest stub
308
- npx frs-hono new createPost --domain posts --method post --api v1
309
- npx frs-hono gen --root src/domains # refresh manifest (run before each build)
320
+ npx frs init # interactive — creates apis.ts + manifest stub
321
+ npx frs new createPost --domain posts --method post --api v1
322
+ npx frs gen --root src/domains # refresh manifest (run before each build)
310
323
  ```
311
324
 
312
325
  ### Configure your APIs (`apis.ts`)
@@ -337,7 +350,7 @@ import { z } from "zod";
337
350
  import { defineRoute } from "../../../../apis.js";
338
351
 
339
352
  export default defineRoute({
340
- api: "v1", // typed: only registered tags accepted
353
+ api: "v1", // typed: only registered tags accepted
341
354
  method: "post",
342
355
  input: z.object({ title: z.string() }),
343
356
  output: z.object({ id: z.string() }),
@@ -365,16 +378,16 @@ export const { v1 } = apis.toFunctions(routes, onRequest, {
365
378
 
366
379
  ### Key features
367
380
 
368
- | Feature | Details |
369
- | --- | --- |
370
- | **File-based routing** | `routes.ts` next to each useCase, scanned at build time |
371
- | **Multi-API registry** | One Cloud Function per tag, typed `api` field |
372
- | **Zod validation** | Body / query / path params + optional response validation |
373
- | **OpenAPI 3.1** | Auto-generated from Zod schemas; Scalar UI at `/docs` |
374
- | **Interceptor** | Around-style hook for envelopes, error mapping, tracing |
375
- | **Middlewares** | Per-API and per-route Hono middlewares |
376
- | **Typed context** | Augment `ContextVariableMap` once, `c.get("user")` typed everywhere |
377
- | **CLI** | `init` / `new` (interactive) / `gen` |
381
+ | Feature | Details |
382
+ | ---------------------- | ------------------------------------------------------------------- |
383
+ | **File-based routing** | `routes.ts` next to each useCase, scanned at build time |
384
+ | **Multi-API registry** | One Cloud Function per tag, typed `api` field |
385
+ | **Zod validation** | Body / query / path params + optional response validation |
386
+ | **OpenAPI 3.1** | Auto-generated from Zod schemas; Scalar UI at `/docs` |
387
+ | **Interceptor** | Around-style hook for envelopes, error mapping, tracing |
388
+ | **Middlewares** | Per-API and per-route Hono middlewares |
389
+ | **Typed context** | Augment `ContextVariableMap` once, `c.get("user")` typed everywhere |
390
+ | **CLI** | `init` / `new` (interactive) / `gen` |
378
391
 
379
392
  Full documentation: [frs.lpdjs.fr/guide/hono](https://frs.lpdjs.fr/guide/hono)
380
393
 
@@ -446,9 +459,18 @@ export const { functions } = servers.sync({
446
459
 
447
460
  // Spread Cloud Functions into your exports
448
461
  export const {
449
- users_onCreate, users_onUpdate, users_onDelete, sync_users,
450
- posts_onCreate, posts_onUpdate, posts_onDelete, sync_posts,
451
- comments_onCreate, comments_onUpdate, comments_onDelete, sync_comments,
462
+ users_onCreate,
463
+ users_onUpdate,
464
+ users_onDelete,
465
+ sync_users,
466
+ posts_onCreate,
467
+ posts_onUpdate,
468
+ posts_onDelete,
469
+ sync_posts,
470
+ comments_onCreate,
471
+ comments_onUpdate,
472
+ comments_onDelete,
473
+ sync_comments,
452
474
  adminsync,
453
475
  } = functions;
454
476
  ```
@@ -488,7 +510,6 @@ Firestore emulator runs on `localhost:8080`, UI on `http://localhost:4000`.
488
510
 
489
511
  MIT
490
512
 
491
-
492
513
  ## Installation
493
514
 
494
515
  ```bash
@@ -579,20 +600,23 @@ if (post) {
579
600
  }
580
601
 
581
602
  // Update
582
- const updated = await repos.users.update("user123", { name: "New name", age: 31 });
603
+ const updated = await repos.users.update("user123", {
604
+ name: "New name",
605
+ age: 31,
606
+ });
583
607
  ```
584
608
 
585
609
  ## API reference
586
610
 
587
611
  ### `createRepositoryConfig()`
588
612
 
589
- | Option | Description |
590
- | ------------- | ------------------------------------------------------- |
591
- | `path` | Collection path in Firestore |
592
- | `isGroup` | `true` for collection group, `false` for simple |
593
- | `foreignKeys` | Keys for `get.by*` methods (single document lookup) |
594
- | `queryKeys` | Keys for `query.by*` methods (multi-document query) |
595
- | `refCb` | Function that returns the document reference |
613
+ | Option | Description |
614
+ | ------------- | --------------------------------------------------- |
615
+ | `path` | Collection path in Firestore |
616
+ | `isGroup` | `true` for collection group, `false` for simple |
617
+ | `foreignKeys` | Keys for `get.by*` methods (single document lookup) |
618
+ | `queryKeys` | Keys for `query.by*` methods (multi-document query) |
619
+ | `refCb` | Function that returns the document reference |
596
620
 
597
621
  **Sub-collection example:**
598
622
 
@@ -611,8 +635,8 @@ comments: createRepositoryConfig<CommentModel>()({
611
635
 
612
636
  ```typescript
613
637
  interface QueryOptions<T> {
614
- where?: [keyof T, WhereFilterOp, any][]; // AND conditions
615
- orWhere?: [keyof T, WhereFilterOp, any][][]; // OR conditions
638
+ where?: [keyof T, WhereFilterOp, any][]; // AND conditions
639
+ orWhere?: [keyof T, WhereFilterOp, any][][]; // OR conditions
616
640
  orderBy?: { field: keyof T; direction?: "asc" | "desc" }[];
617
641
  limit?: number;
618
642
  offset?: number;
@@ -703,7 +727,9 @@ const page = await repos.posts.query.paginate({
703
727
  ```typescript
704
728
  import { count, sum, average } from "@lpdjs/firestore-repo-service";
705
729
 
706
- const activeCount = await repos.users.aggregate.count({ where: [["isActive", "==", true]] });
730
+ const activeCount = await repos.users.aggregate.count({
731
+ where: [["isActive", "==", true]],
732
+ });
707
733
  const totalViews = await repos.posts.aggregate.sum("views");
708
734
  const avgAge = await repos.users.aggregate.average("age");
709
735
  ```
@@ -714,7 +740,9 @@ const avgAge = await repos.users.aggregate.average("age");
714
740
  const result = await repos.users.transaction.run(async (txn) => {
715
741
  const user = await txn.get(repos.users.documentRef("user123"));
716
742
  if (user.exists()) {
717
- txn.update(repos.users.documentRef("user123"), { age: user.data().age + 1 });
743
+ txn.update(repos.users.documentRef("user123"), {
744
+ age: user.data().age + 1,
745
+ });
718
746
  }
719
747
  return { success: true };
720
748
  });
@@ -726,8 +754,14 @@ const result = await repos.users.transaction.run(async (txn) => {
726
754
  // (status = 'active' AND age >= 18) OR (status = 'pending' AND verified = true)
727
755
  const users = await repos.users.query.by({
728
756
  orWhere: [
729
- [["status", "==", "active"], ["age", ">=", 18]],
730
- [["status", "==", "pending"], ["verified", "==", true]],
757
+ [
758
+ ["status", "==", "active"],
759
+ ["age", ">=", 18],
760
+ ],
761
+ [
762
+ ["status", "==", "pending"],
763
+ ["verified", "==", true],
764
+ ],
731
765
  ],
732
766
  });
733
767
  ```
@@ -798,9 +832,18 @@ export const { functions } = servers.sync({
798
832
 
799
833
  // Spread Cloud Functions into your exports
800
834
  export const {
801
- users_onCreate, users_onUpdate, users_onDelete, sync_users,
802
- posts_onCreate, posts_onUpdate, posts_onDelete, sync_posts,
803
- comments_onCreate, comments_onUpdate, comments_onDelete, sync_comments,
835
+ users_onCreate,
836
+ users_onUpdate,
837
+ users_onDelete,
838
+ sync_users,
839
+ posts_onCreate,
840
+ posts_onUpdate,
841
+ posts_onDelete,
842
+ sync_posts,
843
+ comments_onCreate,
844
+ comments_onUpdate,
845
+ comments_onDelete,
846
+ sync_comments,
804
847
  adminsync,
805
848
  } = functions;
806
849
  ```
@@ -837,4 +880,3 @@ Firestore emulator runs on `localhost:8080`, UI on `http://localhost:4000`.
837
880
  ## License
838
881
 
839
882
  MIT
840
-
@@ -1,11 +1,11 @@
1
- import { S as SyncQueue } from './queue-xMOZxY0M.cjs';
2
- import { F as FirestoreSyncConfig, S as SyncEvent } from './types-BgIGWlR1.cjs';
3
- import { O as OpenAPIDocument } from './openapi-ML_1hTx2.cjs';
1
+ import { S as SyncQueue } from './queue-B8YUTnBT.cjs';
2
+ import { F as FirestoreSyncConfig, S as SyncEvent } from './types-wcX7xfdo.cjs';
3
+ import { O as OpenAPIDocument } from './openapi-B2w5tVRR.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
- import { H as HistoryTriggersConfig } from './read-BSyLao3I.cjs';
7
- import { C as ConfiguredRepository, a as CrudServerOptions, b as CrudRepoConfig } from './types-BtdC0Qhu.cjs';
8
- import { A as AdminServerOptions, a as AdminRepoConfig } from './index-BmagC7uw.cjs';
6
+ import { H as HistoryTriggersConfig } from './read-CTWZjxyh.cjs';
7
+ import { C as ConfiguredRepository, a as CrudServerOptions, b as CrudRepoConfig } from './types-GvexCqrq.cjs';
8
+ import { A as AdminServerOptions, a as AdminRepoConfig } from './index-DzO9MfNI.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">;
@@ -1,11 +1,11 @@
1
- import { S as SyncQueue } from './queue-CVchaGAh.js';
2
- import { F as FirestoreSyncConfig, S as SyncEvent } from './types-ChzVPw4k.js';
3
- import { O as OpenAPIDocument } from './openapi-DIoQV_yQ.js';
1
+ import { S as SyncQueue } from './queue-DYmbVDu5.js';
2
+ import { F as FirestoreSyncConfig, S as SyncEvent } from './types-FLGn8CAI.js';
3
+ import { O as OpenAPIDocument } from './openapi-DB8bXZB-.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
- import { H as HistoryTriggersConfig } from './read-BSyLao3I.js';
7
- import { C as ConfiguredRepository, a as CrudServerOptions, b as CrudRepoConfig } from './types-5vgXdUM2.js';
8
- import { A as AdminServerOptions, a as AdminRepoConfig } from './index-BxurOEz1.js';
6
+ import { H as HistoryTriggersConfig } from './read-CTWZjxyh.js';
7
+ import { C as ConfiguredRepository, a as CrudServerOptions, b as CrudRepoConfig } from './types-BHZ-Gk-s.js';
8
+ import { A as AdminServerOptions, a as AdminRepoConfig } from './index-oFhGCBrY.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">;
@@ -272,6 +272,19 @@ interface FirebaseAuthConfig<TContext = unknown> {
272
272
  secureCookie?: boolean;
273
273
  /** Cookie `SameSite`. Default: `"Lax"`. */
274
274
  sameSite?: "Strict" | "Lax" | "None";
275
+ /**
276
+ * Enable the built-in CSRF defense: state-changing requests
277
+ * (`POST`/`PUT`/`PATCH`/`DELETE`) authenticated by **session cookie** must
278
+ * carry an `Origin`/`Referer` header matching the request host. Bearer
279
+ * requests are exempt (a browser never auto-attaches an `Authorization`
280
+ * header, so they are not CSRF-able).
281
+ *
282
+ * Defaults to `true` for `cookie`/`both` modes and `false` for `bearer`.
283
+ * This is the primary protection when `sameSite: "None"` is used and a
284
+ * defense-in-depth layer otherwise. Disable only if you implement your own
285
+ * CSRF protection upstream.
286
+ */
287
+ csrfProtection?: boolean;
275
288
  /**
276
289
  * Behaviour when authentication fails or `allow()` returns `null`.
277
290
  * - `"redirect"` (default in cookie mode) → 302 to the login page,
@@ -272,6 +272,19 @@ interface FirebaseAuthConfig<TContext = unknown> {
272
272
  secureCookie?: boolean;
273
273
  /** Cookie `SameSite`. Default: `"Lax"`. */
274
274
  sameSite?: "Strict" | "Lax" | "None";
275
+ /**
276
+ * Enable the built-in CSRF defense: state-changing requests
277
+ * (`POST`/`PUT`/`PATCH`/`DELETE`) authenticated by **session cookie** must
278
+ * carry an `Origin`/`Referer` header matching the request host. Bearer
279
+ * requests are exempt (a browser never auto-attaches an `Authorization`
280
+ * header, so they are not CSRF-able).
281
+ *
282
+ * Defaults to `true` for `cookie`/`both` modes and `false` for `bearer`.
283
+ * This is the primary protection when `sameSite: "None"` is used and a
284
+ * defense-in-depth layer otherwise. Disable only if you implement your own
285
+ * CSRF protection upstream.
286
+ */
287
+ csrfProtection?: boolean;
275
288
  /**
276
289
  * Behaviour when authentication fails or `allow()` returns `null`.
277
290
  * - `"redirect"` (default in cookie mode) → 302 to the login page,
@@ -1,2 +1,2 @@
1
- 'use strict';var firestore=require('firebase-admin/firestore'),crypto=require('crypto');function O(t){if(t===null)return "null";if(t===void 0)return "undefined";if(t instanceof firestore.Timestamp)return "timestamp";if(t instanceof Date)return "date";if(Array.isArray(t))return "array";let e=typeof t;return e==="string"||e==="number"||e==="boolean"?e:"object"}function C(t,e){if(t===e)return true;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(t instanceof firestore.Timestamp&&e instanceof firestore.Timestamp)return t.isEqual(e);if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r++)if(!C(t[r],e[r]))return false;return true}if(typeof t=="object"&&typeof e=="object")try{return JSON.stringify(t)===JSON.stringify(e)}catch{return false}return false}function b(t,e,r={}){let o=new Set([...r.exclude??[],...r.metaFields??[],...r.systemKeys??[]]),s=r.include?new Set(r.include):null,l=t??{},a=e??{},y=new Set([...Object.keys(l),...Object.keys(a)]),T={};for(let p of y){if(o.has(p)||s&&!s.has(p))continue;let i=l[p],n=a[p];C(i,n)||(T[p]={oldValue:i===void 0?null:i,newValue:n===void 0?null:n,type:{old:O(i),new:O(n)}});}return T}var U=7e5;function w(t){let e=t.ttlOverride??t.config.ttl,o={schemaVersion:2,historyDocId:crypto.randomUUID(),historyToObjectId:t.entityId,historySetAt:firestore.Timestamp.now(),operation:t.operation,meta:t.meta,changes:t.changes};e&&(o.expiresAt=firestore.Timestamp.fromMillis(Date.now()+e.days*24*60*60*1e3));let s=l=>{try{return Buffer.byteLength(JSON.stringify(l,(a,y)=>y instanceof firestore.Timestamp?y.toMillis():y),"utf8")}catch{return 0}};if(s(o.changes)>U){let l={};for(let[a,y]of Object.entries(o.changes)){let T=s(y.oldValue),p=s(y.newValue);l[a]={oldValue:T>5e4?"[truncated]":y.oldValue,newValue:p>5e4?"[truncated]":y.newValue,type:y.type};}o.changes=l,o._truncated=true;}return o}async function D(t,e,r,o){let s=e;return r.onBeforeWrite&&(s=await r.onBeforeWrite(e,o)),s?(await t.doc(s.historyDocId).set(s),{written:true,entry:s}):{written:false,reason:"dropped-by-onBeforeWrite"}}function M(t,e){let r=t??{},o={},s=e.meta;if(!s)return o;let l=i=>{if(!i)return;let n=r[i];return n===void 0||n===null?null:String(n)},a=l(s.userId);a!==void 0&&(o.userId=a);let y=l(s.userEmail);y!==void 0&&(o.userEmail=y);let T=l(s.reason);T!==void 0&&(o.reason=T);let p=l(s.comment);if(p!==void 0&&(o.comment=p),s.extras&&s.extras.length>0){let i={},n=false;for(let f of s.extras)f in r&&(i[f]=r[f],n=true);n&&(o.extras=i);}return o}function E(t){let e=t.meta;if(!e)return [];let r=[];return e.userId&&r.push(e.userId),e.userEmail&&r.push(e.userEmail),e.reason&&r.push(e.reason),e.comment&&r.push(e.comment),e.extras&&r.push(...e.extras),r}var B="history";function N(t,e){let r=e.ref?.path??void 0;return r?`${r}/{docId}`:(console.warn(`[HistoryTriggers] Cannot determine collection path for "${t}". Skipping.`),null)}function K(t,e){let{onDocumentWritten:r}=e.deps,o={};for(let[s,l]of Object.entries(t)){let a=l._historyConfig??(typeof l.history=="object"&&l.history!==null&&"enabled"in l.history?l.history:void 0);if(!a?.enabled)continue;let y=a.subcollection??B,T=a.ttl??e.defaults?.ttl,p=e.repos?.[s],i;if(l._isGroup){if(!p?.triggerPath){console.warn(`[HistoryTriggers] Skipping collection-group repo "${s}". Provide a triggerPath in the history triggers repos override.`);continue}i=p.triggerPath;}else i=p?.triggerPath??N(s,l);if(!i)continue;let n=l._systemKeys??[],f=n[0]??"docId",c=E(a);o[`${s}_onHistory`]=r(i,async u=>{try{let m=u.data?.before?.data(),d=u.data?.after?.data(),H;if(!m&&d)H="create";else if(m&&!d)H="delete";else if(m&&d)H="update";else return;let h=String(d?.[f]??m?.[f]??u.params?.docId??u.data?.after?.id??u.data?.before?.id??"");if(!h)return;let g=b(m??{},d??{},{include:a.include,exclude:a.exclude,metaFields:c,systemKeys:n});if(H==="update"&&Object.keys(g).length===0)return;let x=M(d??m??null,a),L=w({entityId:h,operation:H,changes:g,meta:x,config:a,ttlOverride:T}),I=u.data?.after?.ref??u.data?.before?.ref;if(!I)return;let P=I.collection(y);await D(P,L,a,{repoName:s,docId:h,before:m??null,after:d??null});}catch(m){console.error(`[HistoryTriggers] Failed to record history for "${s}":`,m);}});}return o}var S=5;function v(t){return t.schemaVersion===2}function F(t){if(typeof t!="string")return "object";switch(t){case "string":case "number":case "boolean":case "object":case "array":case "timestamp":case "date":case "null":case "undefined":return t;default:return "object"}}function W(t){return {historyDocId:t.historyDocId,historyToObjectId:t.historyToObjectId,historySetAt:t.historySetAt,schemaVersion:2,operation:t.operation,meta:t.meta??{},changes:t.changes??{}}}function z(t){let e={};t.historyUserId!==void 0&&(e.userId=t.historyUserId??null),t.historyUserEmail!==void 0&&(e.userEmail=t.historyUserEmail??null);let r=t.extraHistoryDetails??null;r&&(r.reason!==void 0&&(e.reason=r.reason??null),r.comment!==void 0&&(e.comment=r.comment??null));let o={},s=false;if(t.historyDetails&&typeof t.historyDetails=="object")for(let[l,a]of Object.entries(t.historyDetails))o[l]=a,s=true;if(t.extraContentKeys&&typeof t.extraContentKeys=="object")for(let[l,a]of Object.entries(t.extraContentKeys))o[`content.${l}`]=a,s=true;return s&&(e.extras=o),e}function $(t){let e=t.changes?.oldValue??null,r=t.changes?.newValue??null,o=t.types?.oldValue,s=t.types?.newValue;return {oldValue:e,newValue:r,type:{old:o?F(o):O(e),new:s?F(s):O(r)}}}function q(t){let e=$(t),r=z(t);return {historyDocId:t.historyDocId,historyToObjectId:t.historyToObjectId,historySetAt:t.historySetAt,schemaVersion:1,operation:"update",meta:r,changes:{[t.field]:e}}}function G(t,e,r){return Math.abs(t.toMillis()-e.toMillis())<=r}function J(t,e){return (t.userId??null)===(e.userId??null)}function A(t,e={}){let r=e.groupToleranceMs??S,o=[];for(let s of t){if(v(s)){o.push(W(s));continue}let a=q(s),y=o[o.length-1];y&&y.schemaVersion===1&&G(y.historySetAt,a.historySetAt,r)&&J(y.meta,a.meta)?Object.assign(y.changes,a.changes):o.push(a);}return o}var X="history",Y=50;function j(t,e,r){return t(...r).collection(e)}function Q(t){return String(t[t.length-1]??"")}function Z(t,e,r,o){if(!o?.enabled)return null;let s=o.subcollection??X;async function l(...i){let n={},f=i,c=i[i.length-1];c!==null&&typeof c=="object"&&!(c instanceof firestore.Timestamp)&&("limit"in c||"cursor"in c||"direction"in c)&&(n=c,f=i.slice(0,-1));let u=j(t,s,f),m=n.direction??"desc",d=u.orderBy("historySetAt",m);return n.cursor&&(d=d.startAfter(n.cursor)),n.limit&&n.limit>0&&(d=d.limit(n.limit)),(await d.get()).docs.map(h=>({id:h.id,data:h.data()}))}async function a(...i){let n={},f=i,c=i[i.length-1];c!==null&&typeof c=="object"&&!(c instanceof firestore.Timestamp)&&("limit"in c||"cursor"in c||"direction"in c||"fields"in c||"operations"in c)&&(n=c,f=i.slice(0,-1));let u=n.limit??Y,m=Math.max(u,Math.min(u*8,500)),d=await l(...f,{limit:m,cursor:n.cursor,direction:n.direction??"desc"}),H=A(d.map(h=>h.data));if(n.fields&&n.fields.length>0){let h=new Set(n.fields);H=H.filter(g=>Object.keys(g.changes).some(x=>h.has(x)));}if(n.operations&&n.operations.length>0){let h=new Set(n.operations);H=H.filter(g=>h.has(g.operation));}return H.slice(0,u)}async function y(...i){let n=i[i.length-1],f={},c,u;return n!==null&&typeof n=="object"&&!(n instanceof firestore.Timestamp)?(f=n,c=i[i.length-2],u=i.slice(0,-2)):(c=n,u=i.slice(0,-1)),a(...u,{...f,fields:[c]})}async function T(...i){let n=i[i.length-1],f={},c,u;return n!==null&&typeof n=="object"&&!(n instanceof firestore.Timestamp)?(f=n,c=i[i.length-2],u=i.slice(0,-2)):(c=n,u=i.slice(0,-1)),a(...u,{...f,operations:[c]})}async function p(...i){let n=i[i.length-1],f=i.slice(0,-1),c=Q(f),u=b(n.before??{},n.after??{},{include:o.include,exclude:o.exclude,metaFields:E(o),systemKeys:e});if(n.operation==="update"&&Object.keys(u).length===0)return null;let d={...M(n.after??n.before??null,o),...n.meta??{}},H=w({entityId:c,operation:n.operation,changes:u,meta:d,config:o}),h=j(t,s,f),g=await D(h,H,o,{repoName:r,docId:c,before:n.before??null,after:n.after??null});return !g.written||!g.entry?null:{historyDocId:g.entry.historyDocId,historyToObjectId:g.entry.historyToObjectId,historySetAt:g.entry.historySetAt,schemaVersion:2,operation:g.entry.operation,meta:g.entry.meta,changes:g.entry.changes}}return {list:a,raw:l,byField:y,byOperation:T,recordManual:p}}exports.DEFAULT_GROUP_TOLERANCE_MS=S;exports.buildHistoryEntry=w;exports.computeDiff=b;exports.createHistoryMethods=Z;exports.createHistoryTriggers=K;exports.extractMeta=M;exports.metaFieldsOf=E;exports.normalizeHistoryDocs=A;exports.valueType=O;exports.valuesEqual=C;exports.writeHistoryEntry=D;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var firestore=require('firebase-admin/firestore'),crypto=require('crypto');function w(t){if(t===null)return "null";if(t===void 0)return "undefined";if(t instanceof firestore.Timestamp)return "timestamp";if(t instanceof Date)return "date";if(Array.isArray(t))return "array";let e=typeof t;return e==="string"||e==="number"||e==="boolean"?e:"object"}function V(t,e,o=new WeakMap){if(t===e)return true;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(t instanceof firestore.Timestamp&&e instanceof firestore.Timestamp)return t.isEqual(e);if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(Array.isArray(t)||Array.isArray(e)){if(!Array.isArray(t)||!Array.isArray(e)||t.length!==e.length)return false;if(o.get(t)===e)return true;o.set(t,e);for(let r=0;r<t.length;r++)if(!V(t[r],e[r],o))return false;return true}if(typeof t=="object"&&typeof e=="object"){if(t instanceof Date||e instanceof Date||t instanceof firestore.Timestamp||e instanceof firestore.Timestamp)return false;if(o.get(t)===e)return true;o.set(t,e);let r=Object.keys(t),i=Object.keys(e);if(r.length!==i.length)return false;for(let a of r)if(!Object.prototype.hasOwnProperty.call(e,a)||!V(t[a],e[a],o))return false;return true}return false}function D(t,e,o={}){let r=new Set([...o.exclude??[],...o.metaFields??[],...o.systemKeys??[]]),i=o.include?new Set(o.include):null,a=t??{},c=e??{},y=new Set([...Object.keys(a),...Object.keys(c)]),T={};for(let p of y){if(r.has(p)||i&&!i.has(p))continue;let s=a[p],n=c[p];V(s,n)||(T[p]={oldValue:s===void 0?null:s,newValue:n===void 0?null:n,type:{old:w(s),new:w(n)}});}return T}var U=7e5;function b(t){let e=t.ttlOverride??t.config.ttl,r={schemaVersion:2,historyDocId:crypto.randomUUID(),historyToObjectId:t.entityId,historySetAt:firestore.Timestamp.now(),operation:t.operation,meta:t.meta,changes:t.changes};e&&(r.expiresAt=firestore.Timestamp.fromMillis(Date.now()+e.days*24*60*60*1e3));let i=a=>{try{return Buffer.byteLength(JSON.stringify(a,(c,y)=>y instanceof firestore.Timestamp?y.toMillis():y),"utf8")}catch{return 0}};if(i(r.changes)>U){let a={};for(let[c,y]of Object.entries(r.changes)){let T=i(y.oldValue),p=i(y.newValue);a[c]={oldValue:T>5e4?"[truncated]":y.oldValue,newValue:p>5e4?"[truncated]":y.newValue,type:y.type};}r.changes=a,r._truncated=true;}return r}async function M(t,e,o,r){let i=e;return o.onBeforeWrite&&(i=await o.onBeforeWrite(e,r)),i?(await t.doc(i.historyDocId).set(i),{written:true,entry:i}):{written:false,reason:"dropped-by-onBeforeWrite"}}function k(t,e){let o=t??{},r={},i=e.meta;if(!i)return r;let a=s=>{if(!s)return;let n=o[s];return n===void 0||n===null?null:String(n)},c=a(i.userId);c!==void 0&&(r.userId=c);let y=a(i.userEmail);y!==void 0&&(r.userEmail=y);let T=a(i.reason);T!==void 0&&(r.reason=T);let p=a(i.comment);if(p!==void 0&&(r.comment=p),i.extras&&i.extras.length>0){let s={},n=false;for(let f of i.extras)f in o&&(s[f]=o[f],n=true);n&&(r.extras=s);}return r}function E(t){let e=t.meta;if(!e)return [];let o=[];return e.userId&&o.push(e.userId),e.userEmail&&o.push(e.userEmail),e.reason&&o.push(e.reason),e.comment&&o.push(e.comment),e.extras&&o.push(...e.extras),o}var B="history";function K(t,e){let o=e.ref?.path??void 0;return o?`${o}/{docId}`:(console.warn(`[HistoryTriggers] Cannot determine collection path for "${t}". Skipping.`),null)}function N(t,e){let{onDocumentWritten:o}=e.deps,r={};for(let[i,a]of Object.entries(t)){let c=a._historyConfig??(typeof a.history=="object"&&a.history!==null&&"enabled"in a.history?a.history:void 0);if(!c?.enabled)continue;let y=c.subcollection??B,T=c.ttl??e.defaults?.ttl,p=e.repos?.[i],s;if(a._isGroup){if(!p?.triggerPath){console.warn(`[HistoryTriggers] Skipping collection-group repo "${i}". Provide a triggerPath in the history triggers repos override.`);continue}s=p.triggerPath;}else s=p?.triggerPath??K(i,a);if(!s)continue;let n=a._systemKeys??[],f=n[0]??"docId",l=E(c);r[`${i}_onHistory`]=o(s,async u=>{try{let m=u.data?.before?.data(),d=u.data?.after?.data(),H;if(!m&&d)H="create";else if(m&&!d)H="delete";else if(m&&d)H="update";else return;let h=String(d?.[f]??m?.[f]??u.params?.docId??u.data?.after?.id??u.data?.before?.id??"");if(!h)return;let g=D(m??{},d??{},{include:c.include,exclude:c.exclude,metaFields:l,systemKeys:n});if(H==="update"&&Object.keys(g).length===0)return;let R=k(d??m??null,c),L=b({entityId:h,operation:H,changes:g,meta:R,config:c,ttlOverride:T}),I=u.data?.after?.ref??u.data?.before?.ref;if(!I)return;let P=I.collection(y);await M(P,L,c,{repoName:i,docId:h,before:m??null,after:d??null});}catch(m){console.error(`[HistoryTriggers] Failed to record history for "${i}":`,m);}});}return r}var j=5;function W(t){return t.schemaVersion===2}function F(t){if(typeof t!="string")return "object";switch(t){case "string":case "number":case "boolean":case "object":case "array":case "timestamp":case "date":case "null":case "undefined":return t;default:return "object"}}function v(t){return {historyDocId:t.historyDocId,historyToObjectId:t.historyToObjectId,historySetAt:t.historySetAt,schemaVersion:2,operation:t.operation,meta:t.meta??{},changes:t.changes??{}}}function z(t){let e={};t.historyUserId!==void 0&&(e.userId=t.historyUserId??null),t.historyUserEmail!==void 0&&(e.userEmail=t.historyUserEmail??null);let o=t.extraHistoryDetails??null;o&&(o.reason!==void 0&&(e.reason=o.reason??null),o.comment!==void 0&&(e.comment=o.comment??null));let r={},i=false;if(t.historyDetails&&typeof t.historyDetails=="object")for(let[a,c]of Object.entries(t.historyDetails))r[a]=c,i=true;if(t.extraContentKeys&&typeof t.extraContentKeys=="object")for(let[a,c]of Object.entries(t.extraContentKeys))r[`content.${a}`]=c,i=true;return i&&(e.extras=r),e}function $(t){let e=t.changes?.oldValue??null,o=t.changes?.newValue??null,r=t.types?.oldValue,i=t.types?.newValue;return {oldValue:e,newValue:o,type:{old:r?F(r):w(e),new:i?F(i):w(o)}}}function q(t){let e=$(t),o=z(t);return {historyDocId:t.historyDocId,historyToObjectId:t.historyToObjectId,historySetAt:t.historySetAt,schemaVersion:1,operation:"update",meta:o,changes:{[t.field]:e}}}function G(t,e,o){return Math.abs(t.toMillis()-e.toMillis())<=o}function J(t,e){return (t.userId??null)===(e.userId??null)}function C(t,e={}){let o=e.groupToleranceMs??j,r=[];for(let i of t){if(W(i)){r.push(v(i));continue}let c=q(i),y=r[r.length-1];y&&y.schemaVersion===1&&G(y.historySetAt,c.historySetAt,o)&&J(y.meta,c.meta)?Object.assign(y.changes,c.changes):r.push(c);}return r}var X="history",Y=50;function S(t,e,o){return t(...o).collection(e)}function Q(t){return String(t[t.length-1]??"")}function Z(t,e,o,r){if(!r?.enabled)return null;let i=r.subcollection??X;async function a(...s){let n={},f=s,l=s[s.length-1];l!==null&&typeof l=="object"&&!(l instanceof firestore.Timestamp)&&("limit"in l||"cursor"in l||"direction"in l)&&(n=l,f=s.slice(0,-1));let u=S(t,i,f),m=n.direction??"desc",d=u.orderBy("historySetAt",m);return n.cursor&&(d=d.startAfter(n.cursor)),n.limit&&n.limit>0&&(d=d.limit(n.limit)),(await d.get()).docs.map(h=>({id:h.id,data:h.data()}))}async function c(...s){let n={},f=s,l=s[s.length-1];l!==null&&typeof l=="object"&&!(l instanceof firestore.Timestamp)&&("limit"in l||"cursor"in l||"direction"in l||"fields"in l||"operations"in l)&&(n=l,f=s.slice(0,-1));let u=n.limit??Y,m=Math.max(u,Math.min(u*8,500)),d=await a(...f,{limit:m,cursor:n.cursor,direction:n.direction??"desc"}),H=C(d.map(h=>h.data));if(n.fields&&n.fields.length>0){let h=new Set(n.fields);H=H.filter(g=>Object.keys(g.changes).some(R=>h.has(R)));}if(n.operations&&n.operations.length>0){let h=new Set(n.operations);H=H.filter(g=>h.has(g.operation));}return H.slice(0,u)}async function y(...s){let n=s[s.length-1],f={},l,u;return n!==null&&typeof n=="object"&&!(n instanceof firestore.Timestamp)?(f=n,l=s[s.length-2],u=s.slice(0,-2)):(l=n,u=s.slice(0,-1)),c(...u,{...f,fields:[l]})}async function T(...s){let n=s[s.length-1],f={},l,u;return n!==null&&typeof n=="object"&&!(n instanceof firestore.Timestamp)?(f=n,l=s[s.length-2],u=s.slice(0,-2)):(l=n,u=s.slice(0,-1)),c(...u,{...f,operations:[l]})}async function p(...s){let n=s[s.length-1],f=s.slice(0,-1),l=Q(f),u=D(n.before??{},n.after??{},{include:r.include,exclude:r.exclude,metaFields:E(r),systemKeys:e});if(n.operation==="update"&&Object.keys(u).length===0)return null;let d={...k(n.after??n.before??null,r),...n.meta??{}},H=b({entityId:l,operation:n.operation,changes:u,meta:d,config:r}),h=S(t,i,f),g=await M(h,H,r,{repoName:o,docId:l,before:n.before??null,after:n.after??null});return !g.written||!g.entry?null:{historyDocId:g.entry.historyDocId,historyToObjectId:g.entry.historyToObjectId,historySetAt:g.entry.historySetAt,schemaVersion:2,operation:g.entry.operation,meta:g.entry.meta,changes:g.entry.changes}}return {list:c,raw:a,byField:y,byOperation:T,recordManual:p}}exports.DEFAULT_GROUP_TOLERANCE_MS=j;exports.buildHistoryEntry=b;exports.computeDiff=D;exports.createHistoryMethods=Z;exports.createHistoryTriggers=N;exports.extractMeta=k;exports.metaFieldsOf=E;exports.normalizeHistoryDocs=C;exports.valueType=w;exports.valuesEqual=V;exports.writeHistoryEntry=M;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map