@ddd-ts/event-sourcing-firestore 0.0.0-compute-timeout-on-process.8 → 0.0.0-compute-timeout-on-process.9

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 (84) hide show
  1. package/dist/firestore.event-lake.aggregate-store.d.ts +30 -36
  2. package/dist/firestore.event-lake.aggregate-store.d.ts.map +1 -1
  3. package/dist/firestore.event-lake.aggregate-store.js +0 -1
  4. package/dist/firestore.event-lake.aggregate-store.mjs +4 -6
  5. package/dist/firestore.event-lake.aggregate-store.spec.d.ts +2 -0
  6. package/dist/firestore.event-lake.aggregate-store.spec.d.ts.map +1 -0
  7. package/dist/firestore.event-lake.storage-layer.d.ts +10 -13
  8. package/dist/firestore.event-lake.storage-layer.d.ts.map +1 -1
  9. package/dist/firestore.event-lake.storage-layer.mjs +3 -4
  10. package/dist/firestore.event-lake.store.d.ts +3 -7
  11. package/dist/firestore.event-lake.store.d.ts.map +1 -1
  12. package/dist/firestore.event-lake.store.mjs +3 -4
  13. package/dist/firestore.event-lake.store.spec.d.ts +2 -0
  14. package/dist/firestore.event-lake.store.spec.d.ts.map +1 -0
  15. package/dist/firestore.event-stream-store.spec.d.ts +2 -0
  16. package/dist/firestore.event-stream-store.spec.d.ts.map +1 -0
  17. package/dist/firestore.event-stream.aggregate-store.d.ts +25 -30
  18. package/dist/firestore.event-stream.aggregate-store.d.ts.map +1 -1
  19. package/dist/firestore.event-stream.aggregate-store.js +0 -1
  20. package/dist/firestore.event-stream.aggregate-store.mjs +3 -5
  21. package/dist/firestore.event-stream.aggregate-store.spec.d.ts +2 -0
  22. package/dist/firestore.event-stream.aggregate-store.spec.d.ts.map +1 -0
  23. package/dist/firestore.event-stream.storage-layer.d.ts +11 -14
  24. package/dist/firestore.event-stream.storage-layer.d.ts.map +1 -1
  25. package/dist/firestore.event-stream.storage-layer.js +0 -1
  26. package/dist/firestore.event-stream.storage-layer.mjs +3 -5
  27. package/dist/firestore.event-stream.store.d.ts +3 -7
  28. package/dist/firestore.event-stream.store.d.ts.map +1 -1
  29. package/dist/firestore.event-stream.store.mjs +2 -3
  30. package/dist/firestore.projected-stream.reader.d.ts +10 -14
  31. package/dist/firestore.projected-stream.reader.d.ts.map +1 -1
  32. package/dist/firestore.projected-stream.reader.mjs +3 -4
  33. package/dist/firestore.projected-stream.reader.spec.d.ts +2 -0
  34. package/dist/firestore.projected-stream.reader.spec.d.ts.map +1 -0
  35. package/dist/firestore.projected-stream.storage-layer.d.ts +24 -28
  36. package/dist/firestore.projected-stream.storage-layer.d.ts.map +1 -1
  37. package/dist/firestore.projected-stream.storage-layer.mjs +3 -4
  38. package/dist/firestore.snapshotter.d.ts +3 -7
  39. package/dist/firestore.snapshotter.d.ts.map +1 -1
  40. package/dist/firestore.snapshotter.mjs +1 -2
  41. package/dist/index.d.ts +11 -11
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/projection/cases/attempts.spec.d.ts +2 -0
  44. package/dist/projection/cases/attempts.spec.d.ts.map +1 -0
  45. package/dist/projection/cases/batchlast.spec.d.ts +2 -0
  46. package/dist/projection/cases/batchlast.spec.d.ts.map +1 -0
  47. package/dist/projection/cases/bigshuffle.spec.d.ts +2 -0
  48. package/dist/projection/cases/bigshuffle.spec.d.ts.map +1 -0
  49. package/dist/projection/cases/burst.spec.d.ts +2 -0
  50. package/dist/projection/cases/burst.spec.d.ts.map +1 -0
  51. package/dist/projection/cases/claimtimeout.spec.d.ts +2 -0
  52. package/dist/projection/cases/claimtimeout.spec.d.ts.map +1 -0
  53. package/dist/projection/cases/concurrency.spec.d.ts +2 -0
  54. package/dist/projection/cases/concurrency.spec.d.ts.map +1 -0
  55. package/dist/projection/cases/deduplicate.spec.d.ts +2 -0
  56. package/dist/projection/cases/deduplicate.spec.d.ts.map +1 -0
  57. package/dist/projection/cases/defer.spec.d.ts +2 -0
  58. package/dist/projection/cases/defer.spec.d.ts.map +1 -0
  59. package/dist/projection/cases/lock.spec.d.ts +2 -0
  60. package/dist/projection/cases/lock.spec.d.ts.map +1 -0
  61. package/dist/projection/cases/skip.spec.d.ts +2 -0
  62. package/dist/projection/cases/skip.spec.d.ts.map +1 -0
  63. package/dist/projection/cases/stress.spec.d.ts +2 -0
  64. package/dist/projection/cases/stress.spec.d.ts.map +1 -0
  65. package/dist/projection/firestore.projector.d.ts +122 -125
  66. package/dist/projection/firestore.projector.d.ts.map +1 -1
  67. package/dist/projection/firestore.projector.mjs +4 -5
  68. package/dist/projection/testkit/case-fixture.d.ts +610 -0
  69. package/dist/projection/testkit/case-fixture.d.ts.map +1 -0
  70. package/dist/projection/testkit.d.ts +44 -0
  71. package/dist/projection/testkit.d.ts.map +1 -0
  72. package/dist/projection/trace.decorator.d.ts +2 -0
  73. package/dist/projection/trace.decorator.d.ts.map +1 -0
  74. package/package.json +13 -13
  75. package/dist/firestore.event-lake.aggregate-store.mjs.map +0 -1
  76. package/dist/firestore.event-lake.storage-layer.mjs.map +0 -1
  77. package/dist/firestore.event-lake.store.mjs.map +0 -1
  78. package/dist/firestore.event-stream.aggregate-store.mjs.map +0 -1
  79. package/dist/firestore.event-stream.storage-layer.mjs.map +0 -1
  80. package/dist/firestore.event-stream.store.mjs.map +0 -1
  81. package/dist/firestore.projected-stream.reader.mjs.map +0 -1
  82. package/dist/firestore.projected-stream.storage-layer.mjs.map +0 -1
  83. package/dist/firestore.snapshotter.mjs.map +0 -1
  84. package/dist/projection/firestore.projector.mjs.map +0 -1
@@ -0,0 +1,44 @@
1
+ import * as fb from "firebase-admin";
2
+ import type { Firestore } from "firebase-admin/firestore";
3
+ import { FirestoreProjector, FirestoreQueueStore } from "./firestore.projector";
4
+ import { FirestoreProjectedStreamReader } from "../firestore.projected-stream.reader";
5
+ import { type IEventBus } from "@ddd-ts/core";
6
+ import { FirestoreTransactionPerformer } from "@ddd-ts/store-firestore";
7
+ export declare const registry: import("@ddd-ts/core").SerializerRegistry<[[import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened, import("@ddd-ts/core/dist/components/auto-serializer").AutoSerializerV1<typeof import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened>], [import("@ddd-ts/core/dist/projections/spec/account/account").Deposited, import("@ddd-ts/core/dist/components/auto-serializer").AutoSerializerV1<typeof import("@ddd-ts/core/dist/projections/spec/account/account").Deposited>], [import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn, import("@ddd-ts/core/dist/components/auto-serializer").AutoSerializerV1<typeof import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn>], [import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed, import("@ddd-ts/core/dist/components/auto-serializer").AutoSerializerV1<typeof import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed>], [import("@ddd-ts/core/dist/projections/spec/account/account").Account, import("@ddd-ts/core/dist/components/auto-serializer").AutoSerializerV1<typeof import("@ddd-ts/core/dist/projections/spec/account/account").Account>]], import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened | import("@ddd-ts/core/dist/projections/spec/account/account").Deposited | import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn | import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed | import("@ddd-ts/core/dist/projections/spec/account/account").Account>;
8
+ export type DbHandle = {
9
+ app: fb.app.App;
10
+ db: Firestore;
11
+ };
12
+ export declare function getDb(projectId?: string): DbHandle;
13
+ declare const AccountStore_base: {
14
+ new (firestore: Firestore, serializer: import("@ddd-ts/core").ISerializer<import("@ddd-ts/core/dist/projections/spec/account/account").Account, {}> & import("@ddd-ts/core").ISerializer<(import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened | import("@ddd-ts/core/dist/projections/spec/account/account").Deposited | import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn | import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed) & import("@ddd-ts/core").INamed<string>, {}>, eventBus?: IEventBus): {
15
+ loadFirst(event: import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened | import("@ddd-ts/core/dist/projections/spec/account/account").Deposited | import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn | import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed): import("@ddd-ts/core/dist/projections/spec/account/account").Account;
16
+ getStreamId(id: import("@ddd-ts/core").Identifier<string> & import("@ddd-ts/core/dist/projections/spec/account/account").AccountId): import("@ddd-ts/core").StreamId;
17
+ readonly firestore: Firestore;
18
+ readonly serializer: import("@ddd-ts/core").ISerializer<(import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened | import("@ddd-ts/core/dist/projections/spec/account/account").Deposited | import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn | import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed) & import("@ddd-ts/core").INamed<string>, {}>;
19
+ readonly snapshotter: import("..").FirestoreSnapshotter<import("@ddd-ts/core/dist/projections/spec/account/account").Account>;
20
+ readonly eventBus?: IEventBus | undefined;
21
+ readonly streamStore: import("@ddd-ts/core").EventStreamStore<(import("@ddd-ts/core/dist/projections/spec/account/account").AccountOpened | import("@ddd-ts/core/dist/projections/spec/account/account").Deposited | import("@ddd-ts/core/dist/projections/spec/account/account").Withdrawn | import("@ddd-ts/core/dist/projections/spec/account/account").AccountRenamed) & import("@ddd-ts/core").INamed<string>>;
22
+ readonly transaction: import("@ddd-ts/core").TransactionPerformer;
23
+ loadFromSnapshot(snapshot: import("@ddd-ts/core/dist/projections/spec/account/account").Account): Promise<import("@ddd-ts/core/dist/projections/spec/account/account").Account>;
24
+ loadFromScratch(id: import("@ddd-ts/core").Identifier<string> & import("@ddd-ts/core/dist/projections/spec/account/account").AccountId): Promise<import("@ddd-ts/core/dist/projections/spec/account/account").Account | undefined>;
25
+ loadForce(id: import("@ddd-ts/core").Identifier<string> & import("@ddd-ts/core/dist/projections/spec/account/account").AccountId): Promise<import("@ddd-ts/core/dist/projections/spec/account/account").Account | undefined>;
26
+ load(id: import("@ddd-ts/core").Identifier<string> & import("@ddd-ts/core/dist/projections/spec/account/account").AccountId): Promise<import("@ddd-ts/core/dist/projections/spec/account/account").Account | undefined>;
27
+ saveAll(aggregates: import("@ddd-ts/core/dist/projections/spec/account/account").Account[], parentTrx?: import("@ddd-ts/core").Transaction, attempts?: number): Promise<void>;
28
+ save(aggregate: import("@ddd-ts/core/dist/projections/spec/account/account").Account, trx?: import("@ddd-ts/core").Transaction, attempts?: number): Promise<void>;
29
+ };
30
+ };
31
+ export declare class AccountStore extends AccountStore_base {
32
+ constructor(firestore: Firestore, eventBus?: IEventBus);
33
+ }
34
+ export declare function makeProjector(params: {
35
+ db: Firestore;
36
+ projection: any;
37
+ }): {
38
+ reader: FirestoreProjectedStreamReader<any>;
39
+ queueStore: FirestoreQueueStore;
40
+ projector: FirestoreProjector;
41
+ };
42
+ export declare function makeTransaction(db: Firestore): FirestoreTransactionPerformer;
43
+ export {};
44
+ //# sourceMappingURL=testkit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testkit.d.ts","sourceRoot":"","sources":["../../src/projection/testkit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAEtF,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAGxE,eAAO,MAAM,QAAQ,whDAA4B,CAAC;AAElD,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;IAChB,EAAE,EAAE,SAAS,CAAC;CACf,CAAC;AAEF,wBAAgB,KAAK,CAAC,SAAS,SAAY,GAAG,QAAQ,CAGrD;;;;;;;;;;;;;;;;;;;AAGD,qBAAa,YAAa,SAAQ,iBAEjC;gBACa,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,SAAS;CAGvD;AAGD,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,EAAE,EAAE,SAAS,CAAC;IACd,UAAU,EAAE,GAAG,CAAC;CACjB;;;;EAYA;AAGD,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,iCAE5C"}
@@ -0,0 +1,2 @@
1
+ export declare function Trace<U, const Fn extends (...args: any[]) => Promise<any>>(spanName: string, attributes?: (target: U, ...args: Parameters<Fn>) => Record<string, any>): (_target: U, _propertyKey: string, descriptor: TypedPropertyDescriptor<Fn>) => TypedPropertyDescriptor<Fn>;
2
+ //# sourceMappingURL=trace.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.decorator.d.ts","sourceRoot":"","sources":["../../src/projection/trace.decorator.ts"],"names":[],"mappings":"AAEA,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EACxE,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,aAG7D,CAAC,gBACI,MAAM,cACR,uBAAuB,CAAC,EAAE,CAAC,iCA+B1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddd-ts/event-sourcing-firestore",
3
- "version": "0.0.0-compute-timeout-on-process.8",
3
+ "version": "0.0.0-compute-timeout-on-process.9",
4
4
  "types": "dist/index.d.ts",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -10,30 +10,30 @@
10
10
  "dist"
11
11
  ],
12
12
  "dependencies": {
13
- "@ddd-ts/core": "0.0.0-compute-timeout-on-process.8",
14
- "@ddd-ts/shape": "0.0.0-compute-timeout-on-process.8",
15
- "@ddd-ts/store-firestore": "0.0.0-compute-timeout-on-process.8",
16
- "@ddd-ts/traits": "0.0.0-compute-timeout-on-process.8",
17
- "@ddd-ts/types": "0.0.0-compute-timeout-on-process.8",
13
+ "@ddd-ts/core": "0.0.0-compute-timeout-on-process.9",
14
+ "@ddd-ts/shape": "0.0.0-compute-timeout-on-process.9",
15
+ "@ddd-ts/store-firestore": "0.0.0-compute-timeout-on-process.9",
16
+ "@ddd-ts/traits": "0.0.0-compute-timeout-on-process.9",
17
+ "@ddd-ts/types": "0.0.0-compute-timeout-on-process.9",
18
18
  "@opentelemetry/api": "1.6.0",
19
19
  "firebase-admin": "^13.2.0"
20
20
  },
21
21
  "devDependencies": {
22
- "@ddd-ts/shape": "0.0.0-compute-timeout-on-process.8",
23
- "@ddd-ts/tests": "0.0.0-compute-timeout-on-process.8",
24
- "@ddd-ts/tools": "0.0.0-compute-timeout-on-process.8",
25
- "@ddd-ts/types": "0.0.0-compute-timeout-on-process.8",
22
+ "@ddd-ts/shape": "0.0.0-compute-timeout-on-process.9",
23
+ "@ddd-ts/tests": "0.0.0-compute-timeout-on-process.9",
24
+ "@ddd-ts/tools": "0.0.0-compute-timeout-on-process.9",
25
+ "@ddd-ts/types": "0.0.0-compute-timeout-on-process.9",
26
26
  "@types/jest": "^29.5.1"
27
27
  },
28
28
  "exports": {
29
29
  ".": {
30
- "default": "./dist/index.js",
31
30
  "import": "./dist/index.mjs",
32
- "require": "./dist/index.js",
33
- "types": "./dist/index.d.ts"
31
+ "require": "./dist/index.js"
34
32
  },
35
33
  "./package.json": "./package.json"
36
34
  },
35
+ "main": "./dist/index.js",
36
+ "module": "./dist/index.mjs",
37
37
  "scripts": {
38
38
  "pretest": "docker compose -f docker/docker-compose.yml up -d",
39
39
  "wait": "sh -c 'until curl --silent localhost:8080; do sleep 1; done;'",
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.event-lake.aggregate-store.mjs","names":[],"sources":["../src/firestore.event-lake.aggregate-store.ts"],"sourcesContent":["import {\n ISerializer,\n IEventSourced,\n IIdentifiable,\n EventOf,\n EventLakeStore,\n LakeId,\n IChange,\n IEventBus,\n EventSourced,\n Identifiable,\n} from \"@ddd-ts/core\";\nimport {\n FirestoreStore,\n FirestoreTransaction,\n FirestoreTransactionPerformer,\n} from \"@ddd-ts/store-firestore\";\n\nimport { CollectionReference } from \"firebase-admin/firestore\";\nimport { FirestoreEventLakeStorageLayer } from \"./firestore.event-lake.storage-layer\";\nimport { HasTrait } from \"@ddd-ts/traits\";\n\nexport const MakeFirestoreEventLakeAggregateStore = <\n A extends HasTrait<typeof EventSourced> & HasTrait<typeof Identifiable>,\n>(\n AGGREGATE: A,\n) => {\n abstract class $FirestoreEventLakeAggregateStore extends FirestoreEventLakeAggregateStore<\n InstanceType<A>\n > {\n constructor(\n collection: CollectionReference,\n serializer: ISerializer<InstanceType<A>> &\n ISerializer<EventOf<InstanceType<A>>>,\n eventBus?: IEventBus,\n ) {\n super(collection, serializer, eventBus, AGGREGATE.name);\n }\n\n abstract getLakeId(instance: InstanceType<A>): LakeId;\n }\n\n return $FirestoreEventLakeAggregateStore;\n};\n\nexport abstract class FirestoreEventLakeAggregateStore<\n A extends IEventSourced & IIdentifiable,\n> extends FirestoreStore<A> {\n transaction: FirestoreTransactionPerformer;\n lakeStore: EventLakeStore<EventOf<A>>;\n constructor(\n collection: CollectionReference,\n serializer: ISerializer<EventOf<A>> & ISerializer<A>,\n eventBus?: IEventBus,\n $name?: string,\n ) {\n super(collection, serializer, $name);\n const storageLayer = new FirestoreEventLakeStorageLayer(\n collection.firestore,\n );\n this.transaction = new FirestoreTransactionPerformer(collection.firestore);\n this.lakeStore = new EventLakeStore<EventOf<A>>(\n storageLayer,\n serializer,\n eventBus,\n );\n }\n\n abstract getLakeId(instance: A): LakeId;\n\n override async save(aggregate: A, trx?: FirestoreTransaction) {\n const changes = [...aggregate.changes] as IChange<EventOf<A>>[];\n\n await this.transaction.performWith(trx, async (trx) => {\n const lakeId = this.getLakeId(aggregate);\n\n await super.save(aggregate, trx);\n await this.lakeStore.append(lakeId, changes, trx);\n aggregate.clearChanges();\n });\n }\n}\n"],"mappings":";;;;;;;AAsBA,MAAa,wCAGX,cACG;CACH,MAAe,0CAA0C,iCAEvD;EACA,YACE,YACA,YAEA,UACA;AACA,SAAM,YAAY,YAAY,UAAU,UAAU,KAAK;;;AAM3D,QAAO;;AAGT,IAAsB,mCAAtB,cAEU,eAAkB;CAC1B;CACA;CACA,YACE,YACA,YACA,UACA,OACA;AACA,QAAM,YAAY,YAAY,MAAM;EACpC,MAAM,eAAe,IAAI,+BACvB,WAAW,UACZ;AACD,OAAK,cAAc,IAAI,8BAA8B,WAAW,UAAU;AAC1E,OAAK,YAAY,IAAI,eACnB,cACA,YACA,SACD;;CAKH,MAAe,KAAK,WAAc,KAA4B;EAC5D,MAAM,UAAU,CAAC,GAAG,UAAU,QAAQ;AAEtC,QAAM,KAAK,YAAY,YAAY,KAAK,OAAO,QAAQ;GACrD,MAAM,SAAS,KAAK,UAAU,UAAU;AAExC,SAAM,MAAM,KAAK,WAAW,IAAI;AAChC,SAAM,KAAK,UAAU,OAAO,QAAQ,SAAS,IAAI;AACjD,aAAU,cAAc;IACxB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.event-lake.storage-layer.mjs","names":[],"sources":["../src/firestore.event-lake.storage-layer.ts"],"sourcesContent":["import {\n LakeId,\n EventId,\n type ISerializedChange,\n type ISerializedFact,\n type EventLakeStorageLayer,\n} from \"@ddd-ts/core\";\nimport type { ISerializedSavedChange } from \"@ddd-ts/core/dist/interfaces/es-event\";\n\nimport {\n DefaultConverter,\n FirestoreTransaction,\n} from \"@ddd-ts/store-firestore\";\nimport * as fb from \"firebase-admin\";\n\nexport const serverTimestamp = fb.firestore.FieldValue.serverTimestamp;\n\nexport class FirestoreEventLakeStorageLayer implements EventLakeStorageLayer {\n constructor(\n public readonly firestore: fb.firestore.Firestore,\n public readonly converter = new DefaultConverter(),\n ) {}\n\n getCollection(lakeId: LakeId) {\n return this.firestore\n .collection(\"event-store\")\n .doc(\"Lakes\")\n .collection(lakeId.shardType)\n .doc(lakeId.shardId)\n .collection(\"events\");\n }\n\n async append(\n lakeId: LakeId,\n changes: ISerializedChange[],\n trx: FirestoreTransaction,\n ) {\n const collection = this.getCollection(lakeId);\n\n const result: ISerializedSavedChange[] = [];\n\n for (const change of changes) {\n const revision = trx.increment();\n const storageChange = {\n eventId: change.id,\n name: change.name,\n payload: change.payload,\n occurredAt: serverTimestamp(),\n version: change.version,\n revision,\n };\n\n const ref = collection.doc(change.id);\n trx.transaction.create(ref, this.converter.toFirestore(storageChange));\n\n result.push({\n ...change,\n ref: ref.path,\n revision,\n occurredAt: undefined,\n });\n }\n\n return result;\n }\n\n async *read(\n lakeId: LakeId,\n startAfter?: EventId,\n endAt?: EventId,\n ): AsyncIterable<ISerializedFact> {\n const collection = this.getCollection(lakeId);\n\n const [start, end] = await Promise.all([\n startAfter ? collection.doc(startAfter.serialize()).get() : null,\n endAt ? collection.doc(endAt.serialize()).get() : null,\n ]);\n\n if (startAfter && !start?.exists) {\n throw new Error(`StartAfter event not found: ${startAfter}`);\n }\n\n if (endAt && !end?.exists) {\n throw new Error(`EndAt event not found: ${endAt}`);\n }\n\n let query = collection\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\");\n\n if (start) {\n query = query.startAfter(start);\n }\n\n if (endAt) {\n query = query.endAt(end);\n }\n\n for await (const event of query.stream()) {\n const e = event as any as fb.firestore.QueryDocumentSnapshot<any>;\n const data = this.converter.fromFirestore(e);\n yield {\n id: data.eventId,\n ref: e.ref.path,\n revision: data.revision,\n name: data.name,\n $name: data.name,\n payload: data.payload,\n occurredAt: data.occurredAt,\n version: data.version ?? 1,\n } as ISerializedFact;\n }\n }\n}\n"],"mappings":";;;;;AAeA,MAAa,kBAAkB,GAAG,UAAU,WAAW;AAEvD,IAAa,iCAAb,MAA6E;CAC3E,YACE,AAAgB,WAChB,AAAgB,YAAY,IAAI,kBAAkB,EAClD;EAFgB;EACA;;CAGlB,cAAc,QAAgB;AAC5B,SAAO,KAAK,UACT,WAAW,cAAc,CACzB,IAAI,QAAQ,CACZ,WAAW,OAAO,UAAU,CAC5B,IAAI,OAAO,QAAQ,CACnB,WAAW,SAAS;;CAGzB,MAAM,OACJ,QACA,SACA,KACA;EACA,MAAM,aAAa,KAAK,cAAc,OAAO;EAE7C,MAAM,SAAmC,EAAE;AAE3C,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,WAAW,IAAI,WAAW;GAChC,MAAM,gBAAgB;IACpB,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,SAAS,OAAO;IAChB,YAAY,iBAAiB;IAC7B,SAAS,OAAO;IAChB;IACD;GAED,MAAM,MAAM,WAAW,IAAI,OAAO,GAAG;AACrC,OAAI,YAAY,OAAO,KAAK,KAAK,UAAU,YAAY,cAAc,CAAC;AAEtE,UAAO,KAAK;IACV,GAAG;IACH,KAAK,IAAI;IACT;IACA,YAAY;IACb,CAAC;;AAGJ,SAAO;;CAGT,OAAO,KACL,QACA,YACA,OACgC;EAChC,MAAM,aAAa,KAAK,cAAc,OAAO;EAE7C,MAAM,CAAC,OAAO,OAAO,MAAM,QAAQ,IAAI,CACrC,aAAa,WAAW,IAAI,WAAW,WAAW,CAAC,CAAC,KAAK,GAAG,MAC5D,QAAQ,WAAW,IAAI,MAAM,WAAW,CAAC,CAAC,KAAK,GAAG,KACnD,CAAC;AAEF,MAAI,cAAc,CAAC,OAAO,OACxB,OAAM,IAAI,MAAM,+BAA+B,aAAa;AAG9D,MAAI,SAAS,CAAC,KAAK,OACjB,OAAM,IAAI,MAAM,0BAA0B,QAAQ;EAGpD,IAAI,QAAQ,WACT,QAAQ,cAAc,MAAM,CAC5B,QAAQ,YAAY,MAAM;AAE7B,MAAI,MACF,SAAQ,MAAM,WAAW,MAAM;AAGjC,MAAI,MACF,SAAQ,MAAM,MAAM,IAAI;AAG1B,aAAW,MAAM,SAAS,MAAM,QAAQ,EAAE;GACxC,MAAM,IAAI;GACV,MAAM,OAAO,KAAK,UAAU,cAAc,EAAE;AAC5C,SAAM;IACJ,IAAI,KAAK;IACT,KAAK,EAAE,IAAI;IACX,UAAU,KAAK;IACf,MAAM,KAAK;IACX,OAAO,KAAK;IACZ,SAAS,KAAK;IACd,YAAY,KAAK;IACjB,SAAS,KAAK,WAAW;IAC1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.event-lake.store.mjs","names":[],"sources":["../src/firestore.event-lake.store.ts"],"sourcesContent":["import { EventLakeStore, IEsEvent, IEventBus, ISerializer } from \"@ddd-ts/core\";\nimport { FirestoreEventLakeStorageLayer } from \"./firestore.event-lake.storage-layer\";\nimport { Firestore } from \"firebase-admin/firestore\";\n\nexport class FirestoreEventLakeStore<\n Event extends IEsEvent,\n> extends EventLakeStore<Event> {\n constructor(\n firestore: Firestore,\n serializer: ISerializer<Event>,\n eventBus?: IEventBus,\n ) {\n super(new FirestoreEventLakeStorageLayer(firestore), serializer, eventBus);\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,0BAAb,cAEU,eAAsB;CAC9B,YACE,WACA,YACA,UACA;AACA,QAAM,IAAI,+BAA+B,UAAU,EAAE,YAAY,SAAS"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.event-stream.aggregate-store.mjs","names":[],"sources":["../src/firestore.event-stream.aggregate-store.ts"],"sourcesContent":["import { HasTrait } from \"@ddd-ts/traits\";\nimport {\n StreamId,\n EventsOf,\n EventSourced,\n type Identifiable,\n type IEventBus,\n EventStreamStore,\n EventStreamAggregateStore,\n ISerializer,\n IEventSourced,\n IIdentifiable,\n EventOf,\n} from \"@ddd-ts/core\";\nimport { FirestoreTransactionPerformer } from \"@ddd-ts/store-firestore\";\n\nimport { FirestoreSnapshotter } from \"./firestore.snapshotter\";\nimport { FirestoreEventStreamStorageLayer } from \"./firestore.event-stream.storage-layer\";\nimport { Firestore } from \"firebase-admin/firestore\";\n\nexport const MakeFirestoreEventStreamAggregateStore = <\n A extends HasTrait<typeof EventSourced> & HasTrait<typeof Identifiable>,\n>(\n AGGREGATE: A,\n) => {\n return class $FirestoreEventStreamAggregateStore extends FirestoreEventStreamAggregateStore<\n InstanceType<A>\n > {\n constructor(\n firestore: Firestore,\n serializer: ISerializer<InstanceType<A>> &\n ISerializer<EventOf<InstanceType<A>>>,\n eventBus?: IEventBus,\n ) {\n const snapshotter = new FirestoreSnapshotter<InstanceType<A>>(\n AGGREGATE.name,\n firestore,\n serializer,\n );\n super(firestore, serializer, snapshotter, eventBus);\n }\n\n loadFirst(event: EventsOf<A>[number]): InstanceType<A> {\n return AGGREGATE.loadFirst(event);\n }\n\n getStreamId(id: InstanceType<A>[\"id\"]): StreamId {\n return StreamId.from(AGGREGATE.name, id.serialize());\n }\n };\n};\n\nexport abstract class FirestoreEventStreamAggregateStore<\n A extends IEventSourced & IIdentifiable,\n> extends EventStreamAggregateStore<A> {\n constructor(\n public readonly firestore: Firestore,\n public readonly serializer: ISerializer<EventOf<A>>,\n public readonly snapshotter: FirestoreSnapshotter<A>,\n public readonly eventBus?: IEventBus,\n ) {\n const storageLayer = new FirestoreEventStreamStorageLayer(firestore);\n const transaction = new FirestoreTransactionPerformer(firestore);\n const streamStore = new EventStreamStore<EventOf<A>>(\n storageLayer,\n serializer,\n eventBus,\n );\n super(streamStore, transaction, snapshotter);\n }\n}\n"],"mappings":";;;;;;;;AAoBA,MAAa,0CAGX,cACG;AACH,QAAO,MAAM,4CAA4C,mCAEvD;EACA,YACE,WACA,YAEA,UACA;GACA,MAAM,cAAc,IAAI,qBACtB,UAAU,MACV,WACA,WACD;AACD,SAAM,WAAW,YAAY,aAAa,SAAS;;EAGrD,UAAU,OAA6C;AACrD,UAAO,UAAU,UAAU,MAAM;;EAGnC,YAAY,IAAqC;AAC/C,UAAO,SAAS,KAAK,UAAU,MAAM,GAAG,WAAW,CAAC;;;;AAK1D,IAAsB,qCAAtB,cAEU,0BAA6B;CACrC,YACE,AAAgB,WAChB,AAAgB,YAChB,AAAgB,aAChB,AAAgB,UAChB;EACA,MAAM,eAAe,IAAI,iCAAiC,UAAU;EACpE,MAAM,cAAc,IAAI,8BAA8B,UAAU;EAChE,MAAM,cAAc,IAAI,iBACtB,cACA,YACA,SACD;AACD,QAAM,aAAa,aAAa,YAAY;EAZ5B;EACA;EACA;EACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.event-stream.storage-layer.mjs","names":[],"sources":["../src/firestore.event-stream.storage-layer.ts"],"sourcesContent":["import {\n StreamId,\n type ISerializedChange,\n type ISerializedFact,\n EventStreamStorageLayer,\n} from \"@ddd-ts/core\";\nimport { ISerializedSavedChange } from \"@ddd-ts/core/dist/interfaces/es-event\";\n\nimport {\n DefaultConverter,\n FirestoreTransaction,\n} from \"@ddd-ts/store-firestore\";\nimport * as fb from \"firebase-admin\";\n\nexport const serverTimestamp = fb.firestore.FieldValue.serverTimestamp;\n\nexport class FirestoreEventStreamStorageLayer\n implements EventStreamStorageLayer\n{\n constructor(\n public readonly firestore: fb.firestore.Firestore,\n public readonly converter = new DefaultConverter(),\n ) {}\n\n isLocalRevisionOutdatedError(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === 6\n );\n }\n\n getCollection(streamId: StreamId) {\n return this.firestore\n .collection(\"event-store\")\n .doc(streamId.aggregateType)\n .collection(\"streams\")\n .doc(streamId.aggregateId)\n .collection(\"events\");\n }\n\n async append(\n streamId: StreamId,\n changes: ISerializedChange[],\n expectedRevision: number,\n trx: FirestoreTransaction,\n ) {\n const collection = this.getCollection(streamId);\n const result: ISerializedSavedChange[] = [];\n\n let revision = expectedRevision + 1;\n for (const change of changes) {\n const storageChange = {\n aggregateType: streamId.aggregateType,\n eventId: change.id,\n aggregateId: streamId.aggregateId,\n revision: revision,\n name: change.name,\n payload: change.payload,\n occurredAt: serverTimestamp(),\n version: change.version,\n };\n\n const ref = collection.doc(`${revision}`);\n\n result.push({\n ...change,\n ref: ref.path,\n revision: revision,\n occurredAt: undefined,\n });\n\n trx.transaction.create(ref, this.converter.toFirestore(storageChange));\n revision++;\n }\n\n return result;\n }\n\n async *read(\n streamId: StreamId,\n startAt?: number,\n ): AsyncIterable<ISerializedFact> {\n const collection = this.getCollection(streamId);\n\n const query = collection\n .where(\"revision\", \">=\", startAt || 0)\n .orderBy(\"revision\", \"asc\");\n\n for await (const event of query.stream()) {\n const e = event as any as fb.firestore.QueryDocumentSnapshot<any>;\n const data = this.converter.fromFirestore(e);\n yield {\n id: data.eventId,\n ref: e.ref.path,\n revision: data.revision,\n name: data.name,\n $name: data.name,\n payload: data.payload,\n occurredAt: data.occurredAt,\n version: data.version ?? 1,\n };\n }\n }\n}\n"],"mappings":";;;;;;AAcA,MAAa,kBAAkB,GAAG,UAAU,WAAW;AAEvD,IAAa,mCAAb,MAEA;CACE,YACE,AAAgB,WAChB,AAAgB,YAAY,IAAI,kBAAkB,EAClD;EAFgB;EACA;;CAGlB,6BAA6B,OAAyB;AACpD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS;;CAInB,cAAc,UAAoB;AAChC,SAAO,KAAK,UACT,WAAW,cAAc,CACzB,IAAI,SAAS,cAAc,CAC3B,WAAW,UAAU,CACrB,IAAI,SAAS,YAAY,CACzB,WAAW,SAAS;;CAGzB,MAAM,OACJ,UACA,SACA,kBACA,KACA;EACA,MAAM,aAAa,KAAK,cAAc,SAAS;EAC/C,MAAM,SAAmC,EAAE;EAE3C,IAAI,WAAW,mBAAmB;AAClC,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,gBAAgB;IACpB,eAAe,SAAS;IACxB,SAAS,OAAO;IAChB,aAAa,SAAS;IACZ;IACV,MAAM,OAAO;IACb,SAAS,OAAO;IAChB,YAAY,iBAAiB;IAC7B,SAAS,OAAO;IACjB;GAED,MAAM,MAAM,WAAW,IAAI,GAAG,WAAW;AAEzC,UAAO,KAAK;IACV,GAAG;IACH,KAAK,IAAI;IACC;IACV,YAAY;IACb,CAAC;AAEF,OAAI,YAAY,OAAO,KAAK,KAAK,UAAU,YAAY,cAAc,CAAC;AACtE;;AAGF,SAAO;;CAGT,OAAO,KACL,UACA,SACgC;EAGhC,MAAM,QAFa,KAAK,cAAc,SAAS,CAG5C,MAAM,YAAY,MAAM,WAAW,EAAE,CACrC,QAAQ,YAAY,MAAM;AAE7B,aAAW,MAAM,SAAS,MAAM,QAAQ,EAAE;GACxC,MAAM,IAAI;GACV,MAAM,OAAO,KAAK,UAAU,cAAc,EAAE;AAC5C,SAAM;IACJ,IAAI,KAAK;IACT,KAAK,EAAE,IAAI;IACX,UAAU,KAAK;IACf,MAAM,KAAK;IACX,OAAO,KAAK;IACZ,SAAS,KAAK;IACd,YAAY,KAAK;IACjB,SAAS,KAAK,WAAW;IAC1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.event-stream.store.mjs","names":[],"sources":["../src/firestore.event-stream.store.ts"],"sourcesContent":["import {\n EventStreamStore,\n type IEsEvent,\n type IEventBus,\n type ISerializer,\n} from \"@ddd-ts/core\";\nimport { FirestoreEventStreamStorageLayer } from \"./firestore.event-stream.storage-layer\";\nimport { Firestore } from \"firebase-admin/firestore\";\n\nexport class FirestoreEventStreamStore<\n Event extends IEsEvent,\n> extends EventStreamStore<Event> {\n constructor(\n firestore: Firestore,\n serializer: ISerializer<Event>,\n eventBus?: IEventBus,\n ) {\n super(\n new FirestoreEventStreamStorageLayer(firestore),\n serializer,\n eventBus,\n );\n }\n}\n"],"mappings":";;;;;AASA,IAAa,4BAAb,cAEU,iBAAwB;CAChC,YACE,WACA,YACA,UACA;AACA,QACE,IAAI,iCAAiC,UAAU,EAC/C,YACA,SACD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.projected-stream.reader.mjs","names":[],"sources":["../src/firestore.projected-stream.reader.ts"],"sourcesContent":["import {\n Cursor,\n IEsEvent,\n IFact,\n ISavedChange,\n ISerializedSavedChange,\n ISerializer,\n ProjectedStream,\n ProjectedStreamReader,\n} from \"@ddd-ts/core\";\nimport { Firestore } from \"firebase-admin/firestore\";\nimport { FirestoreProjectedStreamStorageLayer } from \"./firestore.projected-stream.storage-layer\";\n\nexport class FirestoreProjectedStreamReader<\n Event extends IEsEvent,\n> extends ProjectedStreamReader<Event> {\n storage: FirestoreProjectedStreamStorageLayer;\n serializer: ISerializer<Event>;\n constructor(firestore: Firestore, serializer: ISerializer<Event>) {\n const storage = new FirestoreProjectedStreamStorageLayer(firestore);\n super(storage, serializer);\n this.storage = storage;\n this.serializer = serializer;\n }\n\n async getCursor(savedChange: ISavedChange<Event>) {\n const serialized = await this.serializer.serialize(savedChange);\n return this.storage.getCursor(serialized as ISerializedSavedChange);\n }\n\n async get(cursor: Cursor) {\n const serialized = await this.storage.get(cursor);\n if (!serialized) {\n return undefined;\n }\n return this.serializer.deserialize(serialized) as unknown as Promise<\n IFact<Event>\n >;\n }\n\n async slice(\n projectedStream: ProjectedStream,\n shard: string,\n startAfter?: Cursor,\n endAt?: Cursor,\n limit?: number,\n ) {\n const serialized = await this.storage.slice(\n projectedStream,\n shard,\n startAfter,\n endAt,\n limit,\n );\n return Promise.all(\n serialized.map((s) => this.serializer.deserialize(s)),\n ) as any;\n }\n\n async *read(\n projectedStream: ProjectedStream,\n shard: string,\n startAfter?: Cursor,\n endAt?: Cursor,\n ) {\n for await (const serialized of this.storage.read(\n projectedStream,\n shard,\n startAfter,\n endAt,\n )) {\n yield this.serializer.deserialize(serialized) as unknown as IFact<Event>;\n }\n }\n}\n"],"mappings":";;;;;AAaA,IAAa,iCAAb,cAEU,sBAA6B;CACrC;CACA;CACA,YAAY,WAAsB,YAAgC;EAChE,MAAM,UAAU,IAAI,qCAAqC,UAAU;AACnE,QAAM,SAAS,WAAW;AAC1B,OAAK,UAAU;AACf,OAAK,aAAa;;CAGpB,MAAM,UAAU,aAAkC;EAChD,MAAM,aAAa,MAAM,KAAK,WAAW,UAAU,YAAY;AAC/D,SAAO,KAAK,QAAQ,UAAU,WAAqC;;CAGrE,MAAM,IAAI,QAAgB;EACxB,MAAM,aAAa,MAAM,KAAK,QAAQ,IAAI,OAAO;AACjD,MAAI,CAAC,WACH;AAEF,SAAO,KAAK,WAAW,YAAY,WAAW;;CAKhD,MAAM,MACJ,iBACA,OACA,YACA,OACA,OACA;EACA,MAAM,aAAa,MAAM,KAAK,QAAQ,MACpC,iBACA,OACA,YACA,OACA,MACD;AACD,SAAO,QAAQ,IACb,WAAW,KAAK,MAAM,KAAK,WAAW,YAAY,EAAE,CAAC,CACtD;;CAGH,OAAO,KACL,iBACA,OACA,YACA,OACA;AACA,aAAW,MAAM,cAAc,KAAK,QAAQ,KAC1C,iBACA,OACA,YACA,MACD,CACC,OAAM,KAAK,WAAW,YAAY,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.projected-stream.storage-layer.mjs","names":["Cursor"],"sources":["../src/firestore.projected-stream.storage-layer.ts"],"sourcesContent":["import {\n ISerializedFact,\n ISerializedSavedChange,\n LakeSource,\n ProjectedStream,\n ProjectedStreamStorageLayer,\n StreamSource,\n} from \"@ddd-ts/core\";\nimport { DefaultConverter } from \"@ddd-ts/store-firestore\";\nimport {\n Filter,\n Firestore,\n QueryDocumentSnapshot,\n Timestamp,\n} from \"firebase-admin/firestore\";\nimport { MicrosecondTimestamp } from \"@ddd-ts/shape\";\nimport { Cursor } from \"@ddd-ts/core/dist/components/cursor\";\n\nexport class FirestoreLakeSourceFilter {\n filter(shard: string, lakeSource: LakeSource) {\n return Filter.and(\n Filter.where(`payload.${lakeSource.shardKey}`, \"==\", shard),\n Filter.where(\"name\", \"in\", lakeSource.events),\n );\n }\n}\n\nexport class FirestoreStreamSourceFilter {\n filter(shard: string, streamSource: StreamSource) {\n return Filter.and(\n Filter.where(\"aggregateType\", \"==\", streamSource.aggregateType),\n Filter.where(`payload.${streamSource.shardKey}`, \"==\", shard),\n Filter.where(\"name\", \"in\", streamSource.events),\n );\n }\n}\n\nexport class FirestoreProjectedStreamStorageLayer\n implements ProjectedStreamStorageLayer\n{\n constructor(\n private readonly firestore: Firestore,\n public readonly converter = new DefaultConverter(),\n ) {}\n\n async *read(\n projectedStream: ProjectedStream,\n shard: string,\n startAfter?: Cursor,\n endAt?: Cursor,\n ) {\n let query = this.firestore\n .collectionGroup(\"events\")\n .orderBy(\"occurredAt\")\n .orderBy(\"revision\");\n\n const filters = projectedStream.sources.map((source) => {\n if (source instanceof LakeSource) {\n return new FirestoreLakeSourceFilter().filter(shard, source);\n }\n if (source instanceof StreamSource) {\n return new FirestoreStreamSourceFilter().filter(shard, source);\n }\n throw new Error(\"Unknown source type\");\n });\n\n query = query.where(Filter.or(...filters));\n\n if (startAfter) {\n const ts = this.microsecondToTimestamp(startAfter.occurredAt);\n query = query.startAfter(ts, startAfter.revision);\n }\n\n if (endAt) {\n const ts = this.microsecondToTimestamp(endAt.occurredAt);\n query = query.endAt(ts, endAt.revision);\n }\n\n for await (const doc of query.stream() as AsyncIterable<QueryDocumentSnapshot>) {\n const data = this.converter.fromFirestore(doc);\n yield {\n id: data.eventId,\n ref: doc.ref.path,\n revision: data.revision,\n name: data.name,\n $name: data.name,\n payload: data.payload,\n occurredAt: data.occurredAt,\n version: data.version ?? 1,\n };\n }\n }\n public microsecondToTimestamp(microseconds: MicrosecondTimestamp) {\n const seconds = BigInt(microseconds.micros) / 1_000_000n;\n const nanoseconds = (BigInt(microseconds.micros) % 1_000_000n) * 1000n; // Convert to nanoseconds\n return new Timestamp(Number(seconds), Number(nanoseconds));\n }\n\n async get(cursor: Cursor) {\n const doc = await this.firestore.doc(cursor.ref).get();\n if (!doc.exists) {\n return undefined;\n }\n const data = this.converter.fromFirestoreSnapshot(doc) as any;\n return {\n id: data.eventId,\n ref: doc.ref.path,\n revision: data.revision,\n name: data.name,\n $name: data.name,\n payload: data.payload,\n occurredAt: data.occurredAt,\n version: data.version ?? 1,\n } as ISerializedFact;\n }\n\n async getCursor(\n savedChange: ISerializedSavedChange,\n ): Promise<Cursor | undefined> {\n const doc = await this.firestore.doc(savedChange.ref).get();\n if (!doc.exists) {\n return undefined;\n }\n const data = this.converter.fromFirestoreSnapshot(doc) as any;\n return Cursor.deserialize({\n eventId: data.eventId,\n ref: doc.ref.path,\n occurredAt: data.occurredAt,\n revision: data.revision,\n });\n }\n\n async slice(\n projectedStream: ProjectedStream,\n shard: string,\n startAfter?: Cursor,\n endAt?: Cursor,\n limit?: number,\n ) {\n let query = this.firestore\n .collectionGroup(\"events\")\n .orderBy(\"occurredAt\")\n .orderBy(\"revision\");\n\n const filters = projectedStream.sources.map((source) => {\n if (source instanceof LakeSource) {\n return new FirestoreLakeSourceFilter().filter(shard, source);\n }\n if (source instanceof StreamSource) {\n return new FirestoreStreamSourceFilter().filter(shard, source);\n }\n throw new Error(\"Unknown source type\");\n });\n\n query = query.where(Filter.or(...filters));\n\n if (startAfter) {\n const ts = this.microsecondToTimestamp(startAfter.occurredAt);\n query = query.startAfter(ts, startAfter.revision);\n }\n\n if (endAt) {\n const ts = this.microsecondToTimestamp(endAt.occurredAt);\n query = query.endAt(ts, endAt.revision);\n }\n\n if (limit) {\n query = query.limit(limit);\n }\n\n const all = await query.get();\n\n return all.docs.map((doc) => {\n const data = this.converter.fromFirestore(doc);\n return {\n id: data.eventId,\n ref: doc.ref.path,\n revision: data.revision,\n name: data.name,\n $name: data.name,\n payload: data.payload,\n occurredAt: data.occurredAt,\n version: data.version ?? 1,\n } as ISerializedFact;\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.snapshotter.mjs","names":[],"sources":["../src/firestore.snapshotter.ts"],"sourcesContent":["import {\n type IEventSourced,\n type IIdentifiable,\n type ISerializer,\n} from \"@ddd-ts/core\";\nimport { FirestoreStore } from \"@ddd-ts/store-firestore\";\n\nclass SnapshotSerializer<A extends IEventSourced & IIdentifiable> {\n constructor(\n private readonly serializer: ISerializer<A>,\n private readonly aggregateType: string,\n ) {}\n\n async serialize(instance: A) {\n const serialized = await this.serializer.serialize(instance);\n return {\n ...serialized,\n $name: this.aggregateType,\n revision: instance.acknowledgedRevision,\n };\n }\n\n async deserialize(serialized: any) {\n const { revision, ...content } = serialized;\n const instance = await this.serializer.deserialize({\n $name: this.aggregateType,\n ...content,\n });\n instance.acknowledgedRevision = Number(revision);\n return instance;\n }\n}\n\nexport class FirestoreSnapshotter<\n A extends IEventSourced & IIdentifiable,\n> extends FirestoreStore<A> {\n constructor(\n aggregateType: string,\n database: FirebaseFirestore.Firestore,\n serializer: ISerializer<A>,\n ) {\n const collection = database\n .collection(\"event-store\")\n .doc(aggregateType)\n .collection(\"streams\");\n super(\n collection,\n new SnapshotSerializer(serializer, aggregateType),\n aggregateType,\n );\n }\n}\n"],"mappings":";;;;AAOA,IAAM,qBAAN,MAAkE;CAChE,YACE,AAAiB,YACjB,AAAiB,eACjB;EAFiB;EACA;;CAGnB,MAAM,UAAU,UAAa;AAE3B,SAAO;GACL,GAFiB,MAAM,KAAK,WAAW,UAAU,SAAS;GAG1D,OAAO,KAAK;GACZ,UAAU,SAAS;GACpB;;CAGH,MAAM,YAAY,YAAiB;EACjC,MAAM,EAAE,UAAU,GAAG,YAAY;EACjC,MAAM,WAAW,MAAM,KAAK,WAAW,YAAY;GACjD,OAAO,KAAK;GACZ,GAAG;GACJ,CAAC;AACF,WAAS,uBAAuB,OAAO,SAAS;AAChD,SAAO;;;AAIX,IAAa,uBAAb,cAEU,eAAkB;CAC1B,YACE,eACA,UACA,YACA;EACA,MAAM,aAAa,SAChB,WAAW,cAAc,CACzB,IAAI,cAAc,CAClB,WAAW,UAAU;AACxB,QACE,YACA,IAAI,mBAAmB,YAAY,cAAc,EACjD,cACD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"firestore.projector.mjs","names":[],"sources":["../../src/projection/firestore.projector.ts"],"sourcesContent":["import {\n FieldValue,\n Firestore,\n Timestamp,\n WriteBatch,\n} from \"firebase-admin/firestore\";\nimport {\n IEsEvent,\n ISavedChange,\n EventId,\n ProjectedStreamReader,\n Cursor,\n CheckpointId,\n ESProjection,\n ProjectedStream,\n IFact,\n Serialized,\n Lock,\n} from \"@ddd-ts/core\";\nimport { Mapping, MicrosecondTimestamp, Optional, Shape } from \"@ddd-ts/shape\";\nimport {\n DefaultConverter,\n FirestoreTransaction,\n} from \"@ddd-ts/store-firestore\";\n\nconst Status = {\n SUCCESS: \"OK\",\n FAILURE: \"FAILURE\",\n DEFERRED: \"DEFERRED\",\n} as const;\ntype Status = (typeof Status)[keyof typeof Status];\n\nconst TaskState = {\n ENQUEUED: \"ENQUEUED\",\n PROCESSED: \"PROCESSED\",\n MISSING: \"MISSING\",\n} as const;\ntype TaskState = (typeof TaskState)[keyof typeof TaskState];\n\nconst wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst RETENTION = MicrosecondTimestamp.MONTH;\n\ninterface FirestoreProjectorConfig {\n retry: {\n attempts: number;\n minDelay: number;\n maxDelay: number;\n backoff: number;\n };\n enqueue: {\n batchSize: number;\n };\n onProcessError: (error: Error) => void;\n onEnqueueError: (error: Error) => void;\n debounce?: {\n delayMs: number;\n };\n}\n\nexport class FirestoreProjector {\n _unclaim = true;\n\n constructor(\n public readonly projection: ESProjection<IEsEvent>,\n public readonly reader: ProjectedStreamReader<IEsEvent>,\n public readonly queue: FirestoreQueueStore,\n public config: FirestoreProjectorConfig = {\n retry: { attempts: 10, minDelay: 10, maxDelay: 200, backoff: 1.5 },\n debounce: { delayMs: 0 },\n enqueue: { batchSize: 100 },\n onProcessError: (error: Error) => {\n console.error(\"Error processing event:\", error);\n },\n onEnqueueError: (error: Error) => {\n console.error(\"Error enqueuing tasks:\", error);\n },\n },\n ) {}\n\n async *breathe() {\n const { attempts, minDelay, maxDelay, backoff } = this.config.retry;\n\n for (let i = 0; i < attempts; i++) {\n const reset = () => {\n i--;\n };\n\n yield [i, reset] as const;\n\n const margin = maxDelay - minDelay;\n const jitter = Math.random() * margin;\n\n const backedoff = (backoff * i + 1) * minDelay;\n const jitteredDelay = backedoff + jitter;\n\n await wait(jitteredDelay);\n }\n }\n\n private checkpointTsTriggered: Map<string, BigInt> = new Map();\n private async shouldProceedAfterDebounce(event: ISavedChange<IEsEvent>) {\n const debounceDelay = this.config.debounce?.delayMs;\n if (!debounceDelay) return true;\n\n const checkpointId = this.projection.getCheckpointId(event).serialize();\n\n const currentTs = process.hrtime.bigint();\n this.checkpointTsTriggered.set(checkpointId, currentTs);\n\n await wait(debounceDelay);\n\n const latestTs = this.checkpointTsTriggered.get(checkpointId);\n\n const shouldContinue = latestTs === currentTs;\n return shouldContinue;\n }\n\n // @Trace(\"projector.handle\", ($, e) => ({\n // eventId: e.id.serialize(),\n // eventName: e.name,\n // eventRevision: e.revision,\n // eventReference: e.ref,\n // projectionName: $.projection.constructor.name,\n // checkpointId: $.projection.getCheckpointId(e).serialize(),\n // }))\n async handle(savedChange: ISavedChange<IEsEvent>) {\n if (await this.shouldProceedAfterDebounce(savedChange) === false) return;\n\n const checkpointId = this.projection.getCheckpointId(savedChange);\n const target = await this.getCursor(savedChange);\n\n if (!target) {\n throw new Error(\n `Cursor not found for event ${savedChange.id.serialize()}`,\n );\n }\n\n const errors = [];\n\n for await (const [attempt, reset] of this.breathe()) {\n // console.log(`Attempt ${attempt} for event ${savedChange.id.serialize()}`);\n const source = this.projection.getSource(savedChange);\n const [status, message] = await this.attempt(\n source,\n checkpointId,\n target,\n );\n\n if (status === Status.DEFERRED) {\n reset();\n continue;\n }\n\n if (status === Status.SUCCESS) {\n await this.queue.cleanup(checkpointId);\n return;\n }\n\n errors.push(message);\n }\n\n throw new Error(\n `Failed to handle event ${savedChange.id.serialize()}: ${errors.join(\", \")}`,\n );\n }\n\n // @Trace(\"projector.reader.getCursor\")\n private async getCursor(savedChange: ISavedChange<IEsEvent>) {\n return await this.reader.getCursor(savedChange);\n }\n\n // @Trace(\"projector.attempt\")\n private async attempt(\n source: ProjectedStream,\n checkpointId: CheckpointId,\n target: Cursor,\n ) {\n const headCursor = await this.getQueueHead(checkpointId);\n\n const isTargetAfterHead = !headCursor || target.isAfter(headCursor);\n\n if (isTargetAfterHead) {\n const [status, message] = await this.enqueue(\n source,\n headCursor,\n checkpointId,\n target,\n );\n if (status === Status.DEFERRED) {\n return [Status.DEFERRED, message] as const;\n }\n }\n\n if (!isTargetAfterHead) {\n const processed = await this.checkIsProcessed(checkpointId, target);\n if (processed === TaskState.PROCESSED) {\n return [Status.SUCCESS, \"Target event already processed\"] as const;\n }\n\n if (processed === TaskState.MISSING) {\n const [status, message] = await this.enqueueOne(checkpointId, target);\n if (status === Status.DEFERRED) {\n return [Status.FAILURE, message] as const;\n }\n }\n }\n\n const unprocessed = await this.getUnprocessed(checkpointId);\n\n if (!unprocessed.length) {\n return [Status.FAILURE, \"No unprocessed tasks found\"] as const;\n }\n\n const batch = Task.batch(unprocessed);\n\n if (!batch.length) {\n return [\n Status.DEFERRED,\n \"No tasks available to claim, deferring\",\n ] as const;\n }\n\n const claimer = ClaimerId.generate();\n\n const [status, message] = await this.claimTasks(\n checkpointId,\n claimer,\n batch,\n );\n\n if (status === Status.FAILURE) {\n return [Status.DEFERRED, message] as const;\n }\n\n return await this.processEvents(checkpointId, claimer, target.eventId);\n }\n\n // @Trace(\"projector.queue.head\")\n private async getQueueHead(checkpointId: CheckpointId) {\n return await this.queue.head(checkpointId);\n }\n\n // @Trace(\"projector.readSourceStream\")\n private async readSourceStream(\n source: ProjectedStream,\n head: Cursor | undefined,\n checkpointId: CheckpointId,\n target: Cursor,\n ) {\n const shard = checkpointId.shard();\n const headCursor = head;\n const limit = this.config.enqueue.batchSize;\n return this.reader.slice(source, shard, headCursor, target, limit);\n }\n\n // @Trace(\"projector.enqueue\")\n private async enqueue(\n source: ProjectedStream,\n head: Cursor | undefined,\n checkpointId: CheckpointId,\n target: Cursor,\n ) {\n const events = await this.readSourceStream(\n source,\n head,\n checkpointId,\n target,\n );\n\n const tasks = events.map((e) => {\n const settings = this.projection.getTaskSettings(e);\n return Task.new(e, settings);\n });\n\n return await this.queue.enqueue(checkpointId, tasks);\n }\n\n private async enqueueOne(checkpointId: CheckpointId, target: Cursor) {\n const event = await this.reader.get(target);\n if (!event) {\n throw new Error(`Event not found for cursor ${target.ref}`);\n }\n\n const settings = this.projection.getTaskSettings(event);\n const task = Task.new(event, settings);\n return await this.queue.enqueue(checkpointId, [task]);\n }\n\n // @Trace(\"projector.queue.isProcessed\")\n private async checkIsProcessed(checkpointId: CheckpointId, cursor: Cursor) {\n return await this.queue.isProcessed(checkpointId, cursor);\n }\n\n // @Trace(\"projector.queue.unprocessed\")\n private async getUnprocessed(checkpointId: CheckpointId) {\n return await this.queue.unprocessed(checkpointId);\n }\n\n // @Trace(\"projector.queue.claim\")\n private async claimTasks(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n batch: Task<true>[],\n ) {\n try {\n await this.queue.claim(checkpointId, claimer, batch);\n return [Status.SUCCESS, \"Tasks claimed successfully\"] as const;\n } catch (e) {\n return [Status.FAILURE, e] as const;\n }\n }\n\n // @Trace(\"projector.processEvents\")\n private async processEvents(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n targetEventId: EventId,\n ) {\n const tasks = await this.queue.claimed(checkpointId, claimer);\n const todo = await Promise.all(tasks.map((t) => this.reader.get(t.cursor)));\n const filtered = todo.filter((t) => !!t) as IEsEvent[];\n\n if (!filtered.length) {\n // Nothing to process, possibly all tasks were for events that no longer exist\n return [\n Status.DEFERRED,\n \"No events to process in claimed tasks\",\n ] as const;\n }\n\n const onProcessed = this.queue.processed.bind(this.queue, claimer);\n const assertBeforeInsert = this.assertBeforeInsert.bind(this, checkpointId, claimer, filtered);\n const context = { onProcessed, checkpointId, assertBeforeInsert };\n\n const hasTarget = tasks.some((t) => t.id.equals(targetEventId));\n\n try {\n const processed = await this.projection.process(filtered, context);\n\n if (processed.some((id) => id?.equals(targetEventId))) {\n return [Status.SUCCESS, \"Target event processed successfully\"] as const;\n }\n\n return [Status.DEFERRED, \"Target event not processed yet\"] as const;\n } catch (e) {\n this.config.onProcessError(e as Error);\n if (this._unclaim) {\n await this.queue.unclaim(checkpointId, tasks);\n }\n\n if (!hasTarget) {\n return [\n Status.DEFERRED,\n \"Target event not in claimed batch, deferring\",\n ] as const;\n }\n\n return [Status.FAILURE, e] as const;\n }\n }\n\n private async assertBeforeInsert(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n events: IEsEvent[],\n ) {\n const claimedTasks = await this.queue.claimed(checkpointId, claimer);\n const claimedTasksMap = new Map(claimedTasks.map((t) => [t.id.serialize(), t]));\n\n for (const event of events) {\n const task = claimedTasksMap.get(event.id.serialize());\n if (!task) {\n throw new Error(`Task not found for event ${event.id.serialize()} in claimer ${claimer.serialize()}`);\n }\n\n if (task.claimIds?.[0] !== claimer.serialize()) {\n throw new Error(`Task ${task.id.serialize()} claimer mismatch: expected ${claimer.serialize()}, found ${task.claimIds?.[0]}`);\n }\n }\n }\n}\n\nexport class AlreadyEnqueuedError extends Error {\n constructor() {\n super(\"Tasks already enqueued\");\n this.name = \"AlreadyEnqueuedError\";\n }\n}\n\nexport class FirestoreQueueStore {\n converter = new DefaultConverter();\n collection: FirebaseFirestore.CollectionReference;\n\n constructor(public db: Firestore) {\n this.collection = db.collection(\n \"checkpoints\",\n ) as FirebaseFirestore.CollectionReference;\n }\n\n private timestampToMicroseconds(timestamp: Timestamp): MicrosecondTimestamp {\n const microseconds =\n BigInt(timestamp.seconds) * BigInt(1_000_000) +\n BigInt(timestamp.nanoseconds) / BigInt(1_000);\n return new MicrosecondTimestamp(microseconds);\n }\n\n private microsecondsToTimestamp(\n microseconds: MicrosecondTimestamp,\n ): Timestamp {\n const seconds = BigInt(microseconds.micros) / 1_000_000n;\n const nanoseconds = (BigInt(microseconds.micros) % 1_000_000n) * 1000n; // Convert to nanoseconds\n return new Timestamp(Number(seconds), Number(nanoseconds));\n }\n\n async enqueue(checkpointId: CheckpointId, tasks: Task<false>[]) {\n // console.log(\n // `Enqueuing ${tasks.length} tasks for checkpoint ${checkpointId.serialize()}`,\n // );\n\n const batch = this.collection.firestore.batch();\n\n for (const task of tasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.create(ref, {\n ref: this.db.doc(task.cursor.ref),\n ...this.converter.toFirestore(task.serialize()),\n });\n }\n\n try {\n await batch.commit();\n return [Status.SUCCESS, \"Tasks enqueued successfully\"] as const;\n } catch (err: any) {\n if (err.code === 6) {\n return [Status.DEFERRED, new AlreadyEnqueuedError()] as const;\n }\n return [Status.DEFERRED, err] as const;\n }\n }\n\n async claim(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n tasks: Task<true>[],\n ) {\n const batch = this.collection.firestore.batch();\n\n for (const task of tasks) {\n if (task.claimIds.length > 0) {\n throw new Error(\n `Task ${task.id.serialize()} is already claimed by ${task.claimIds.join(\", \")}`,\n );\n }\n const ref = this.queued(checkpointId, task.id);\n batch.update(\n ref,\n {\n /** @deprecated */ claimer: claimer.serialize(),\n /** @deprecated */ claimedAt: FieldValue.serverTimestamp(),\n [`claimsMetadata.${claimer.serialize()}`]: {\n claimedAt: FieldValue.serverTimestamp(),\n },\n claimIds: FieldValue.arrayUnion(claimer.serialize()),\n attempts: FieldValue.increment(1),\n remaining: FieldValue.increment(-1),\n },\n { lastUpdateTime: this.microsecondsToTimestamp(task.lastUpdateTime) },\n );\n }\n\n await batch.commit();\n }\n\n async head(checkpointId: CheckpointId) {\n const head = this.queue(checkpointId)\n .orderBy(\"occurredAt\", \"desc\")\n .orderBy(\"revision\", \"desc\")\n .limit(1);\n const headDoc = (await head.get()).docs[0];\n if (!headDoc) {\n return undefined;\n }\n\n const headData = this.converter.fromFirestoreSnapshot(headDoc);\n if (!headData) {\n return undefined;\n }\n const headCursor = headDoc\n ? Cursor.deserialize({\n ref: headData.ref,\n occurredAt: headData.occurredAt,\n revision: headData.revision,\n eventId: headData.id,\n })\n : undefined;\n return headCursor;\n }\n\n async unprocessed(checkpointId: CheckpointId) {\n const query = this.queue(checkpointId)\n .where(\"processed\", \"==\", false)\n .where(\"remaining\", \">\", 0)\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\")\n .limit(100);\n\n const snapshot = await query.get();\n const tasks = snapshot.docs.map((doc) => {\n const data = this.converter.fromFirestoreSnapshot(doc);\n const timestamp = doc.updateTime\n ? this.timestampToMicroseconds(doc.updateTime)\n : undefined;\n return Task.deserializeWithLastUpdateTime(data as any, timestamp);\n });\n\n // Check for timeouts and unclaim expired tasks\n const expiredTasks: Task<true>[] = [];\n for (const task of tasks) {\n const originalClaimIds = task.claimIds;\n task.checkTimeout();\n\n // If timeout cleared the claimer, we need to update the database\n if (originalClaimIds.length > task.claimIds.length) {\n expiredTasks.push(task);\n }\n }\n\n // Update expired tasks in the database\n if (expiredTasks.length > 0) {\n const batch = this.collection.firestore.batch();\n for (const task of expiredTasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.update(\n ref,\n {\n /** @deprecated */ claimer: FieldValue.delete(),\n /** @deprecated */ claimedAt: FieldValue.delete(),\n claimIds: task.claimIds,\n },\n { lastUpdateTime: this.microsecondsToTimestamp(task.lastUpdateTime) },\n );\n }\n\n await batch.commit();\n }\n\n return tasks;\n }\n\n async claimed(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n ): Promise<Task<true>[]> {\n const query = this.queue(checkpointId)\n .where(\"claimIds\", \"array-contains\", claimer.serialize())\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\");\n\n const snapshot = await query.get();\n return snapshot.docs\n .map((doc) => {\n const data = this.converter.fromFirestoreSnapshot(doc);\n const timestamp = doc.updateTime\n ? this.timestampToMicroseconds(doc.updateTime as Timestamp)\n : undefined;\n return Task.deserializeWithLastUpdateTime(data as any, timestamp);\n })\n .filter((task) => task.claimIds[0] === claimer.serialize());\n }\n\n async unclaim(checkpointId: CheckpointId, tasks: Task<true>[]) {\n const batch = this.collection.firestore.batch();\n\n for (const task of tasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.update(\n ref,\n {\n /** @deprecated */ claimer: FieldValue.delete(),\n /** @deprecated */ claimedAt: FieldValue.delete(),\n claimIds: FieldValue.arrayRemove(task.currentClaimId),\n },\n { lastUpdateTime: this.microsecondsToTimestamp(task.lastUpdateTime) },\n );\n }\n\n await batch.commit();\n }\n\n /**\n * If the task exists, then looks for a processed flag.\n * If not found, check if the cursor is older than the retention time for processed event.\n * If so, consider it processed.\n * Otherwise, consider it missing.\n */\n async isProcessed(checkpointId: CheckpointId, cursor: Cursor) {\n const doc = await this.queued(checkpointId, cursor.eventId).get();\n if (doc.exists) {\n const data = doc.data();\n if (!data) throw new Error(\"No data in queued document\");\n if (data.processed === true) return TaskState.PROCESSED;\n return TaskState.ENQUEUED;\n }\n\n const lastRetention = MicrosecondTimestamp.now().sub(RETENTION);\n if (cursor.isOlderThan(lastRetention)) {\n return TaskState.PROCESSED;\n }\n return TaskState.MISSING;\n }\n\n checkpoint(id: CheckpointId) {\n return this.collection.doc(id.name).collection(\"shards\");\n }\n\n queue(id: CheckpointId) {\n return this.checkpoint(id).doc(id.shard()).collection(\"queue\");\n }\n\n queued(id: CheckpointId, eventId: EventId) {\n return this.queue(id).doc(eventId.serialize());\n }\n\n async processed(\n claimerId: ClaimerId,\n id: CheckpointId,\n eventIds: EventId[],\n context: {\n transaction?: FirestoreTransaction;\n batchWriter?: WriteBatch;\n } = {},\n ) {\n const { transaction: trx, batchWriter } = context;\n\n if (trx) {\n for (const eventId of eventIds) {\n const ref = this.queued(id, eventId);\n trx.transaction.update(ref, {\n processed: true,\n [`claimsMetadata.${claimerId.serialize()}.processedAt`]: FieldValue.serverTimestamp(),\n });\n }\n return;\n }\n\n await Promise.all(\n eventIds.map((eventId) =>\n this.queued(id, eventId).update({\n processed: true,\n [`claimsMetadata.${claimerId.serialize()}.processedAt`]: FieldValue.serverTimestamp(),\n }),\n ),\n );\n\n return;\n }\n\n async getTailCursor(id: CheckpointId) {\n const tail = this.queue(id)\n .where(\"remaining\", \">\", 0)\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\")\n .limit(1);\n const tailDoc = (await tail.get()).docs[0];\n if (!tailDoc) {\n return undefined;\n }\n\n const tailData = this.converter.fromFirestoreSnapshot(tailDoc);\n if (!tailData) {\n return undefined;\n }\n const tailCursor = tailDoc\n ? Cursor.deserialize({\n ref: tailData.ref,\n occurredAt: tailData.occurredAt,\n revision: tailData.revision,\n eventId: tailData.id,\n })\n : undefined;\n return tailCursor;\n }\n\n async cleanup(id: CheckpointId) {\n const aMonthAgo = MicrosecondTimestamp.now().sub(\n MicrosecondTimestamp.WEEK.mult(4),\n );\n\n const query = this.queue(id)\n .where(\"remaining\", \">\", 0)\n .where(\"occurredAt\", \"<\", aMonthAgo.serialize()) // Only consider events older than 4 weeks\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\");\n\n const MIN_TRAIL = 1; // Keep at least one processed document to maintain the tail cursor\n const TRAIL = MIN_TRAIL + 0; // Extra buffer to optimize isProcessed checks\n\n const snapshot = await query.get();\n\n if (snapshot.size < TRAIL) return;\n\n const stopper = snapshot.docs.findIndex((doc) => !doc.data().processed);\n const cleanable = snapshot.docs.slice(0, stopper);\n\n const cleaning = cleanable.slice(0, cleanable.length - TRAIL);\n if (cleaning.length === 0) return;\n\n const batch = this.collection.firestore.batch();\n for (const queued of cleaning) batch.delete(queued.ref);\n await batch.commit();\n }\n\n async flush(id: CheckpointId) {\n const stream = this.queue(id).stream() as AsyncIterable<any>;\n const writer = this.collection.firestore.bulkWriter();\n for await (const queued of stream) writer.delete(queued.ref);\n await writer.close();\n }\n\n /**\n * This method adds a fake processed event to the queue.\n * It is useful for initializing the tail cursor of a new projection, at the\n * same time as the projection's initial state is created, reset, or updated.\n * By default, it will use the current time as the occurredAt timestamp.\n * You can override this by providing a specific timestamp.\n *\n * This ensures that the projection can start processing new events from the\n * correct point in time, avoiding reprocessing of old events.\n */\n async seed(checkpointId: CheckpointId) {\n const cursor = new Cursor({\n ref: \"seed\",\n occurredAt: MicrosecondTimestamp.now(),\n revision: 0,\n eventId: EventId.generate(),\n });\n\n const task = new Task<false>({\n id: EventId.generate(),\n ref: \"Seed\",\n occurredAt: cursor.occurredAt,\n revision: cursor.revision,\n attempts: 0,\n processed: true,\n /** @deprecated */ claimer: undefined,\n /** @deprecated */ claimedAt: undefined,\n claimsMetadata: {},\n claimIds: [],\n lock: new Lock({}),\n remaining: 1,\n claimTimeout: 0,\n skipAfter: 0,\n isolateAfter: 0,\n lastUpdateTime: undefined,\n });\n\n try {\n const serialized = task.serialize();\n const converted = this.converter.toFirestore(serialized);\n await this.queued(checkpointId, task.cursor.eventId).create(converted);\n } catch (e) {\n // Ignore if already exists\n if (!(e instanceof AlreadyEnqueuedError)) {\n throw e;\n }\n }\n }\n}\n\nexport class ClaimerId extends EventId {}\nexport class Task<Stored extends boolean> extends Shape({\n id: EventId,\n ref: String,\n occurredAt: MicrosecondTimestamp,\n revision: Number,\n attempts: Number,\n processed: Boolean,\n /** @deprecated */ claimer: Optional(String),\n /** @deprecated */ claimedAt: Optional(MicrosecondTimestamp),\n claimsMetadata: Mapping([{\n claimedAt: MicrosecondTimestamp,\n processedAt: Optional(MicrosecondTimestamp),\n }]),\n claimIds: [String],\n lock: Lock,\n skipAfter: Number,\n remaining: Number,\n isolateAfter: Number,\n claimTimeout: Number,\n lastUpdateTime: Optional(MicrosecondTimestamp),\n}) {\n declare lastUpdateTime: Stored extends true\n ? MicrosecondTimestamp\n : undefined;\n\n get cursor() {\n return new Cursor({\n ref: this.ref,\n occurredAt: this.occurredAt,\n revision: this.revision,\n eventId: this.id,\n });\n }\n\n static new(\n fact: IFact,\n config: {\n lock: Lock;\n claimTimeout: number;\n skipAfter: number;\n isolateAfter: number;\n },\n ): Task<false> {\n return new Task<false>({\n id: fact.id,\n attempts: 0,\n /** @deprecated */ claimer: undefined,\n processed: false,\n /** @deprecated */ claimedAt: undefined,\n claimsMetadata: {},\n claimIds: [],\n lock: config.lock,\n claimTimeout: config.claimTimeout,\n skipAfter: config.skipAfter,\n isolateAfter: config.isolateAfter,\n remaining: config.skipAfter,\n ref: fact.ref,\n revision: fact.revision,\n occurredAt: fact.occurredAt,\n lastUpdateTime: undefined,\n });\n }\n\n get currentClaimId() {\n // TODO: we should check the claim metadata for the latest valid claim\n return this.claimIds.at(-1);\n }\n\n get isProcessing() {\n return this.currentClaimId !== undefined;\n }\n\n get isProcessed() {\n return !!this.processed;\n }\n\n get shouldSkip() {\n return this.attempts > this.skipAfter;\n }\n\n get shouldIsolate() {\n return this.attempts > this.isolateAfter;\n }\n\n checkTimeout() {\n const claimer = this.currentClaimId;\n if (!claimer) return;\n\n const claimInfo = this.claimsMetadata[claimer];\n if (!claimInfo || !claimInfo.claimedAt) return;\n\n const now = MicrosecondTimestamp.now();\n const elapsedMicros = now.micros - claimInfo.claimedAt.micros;\n const timeoutMicros = BigInt(this.claimTimeout) * 1000n; // Convert ms to microseconds\n\n if (elapsedMicros > timeoutMicros) {\n this.claimIds = this.claimIds.filter((id) => id !== claimer);\n }\n }\n\n static deserializeWithLastUpdateTime(\n data: Omit<Serialized<Task<true>>, \"lastUpdateTime\">,\n timestamp?: MicrosecondTimestamp,\n ): Task<true> {\n const task = Task.deserialize({\n ...data,\n lastUpdateTime: timestamp as any,\n }) as Task<true>;\n\n return task;\n }\n\n static batch(tasks: Task<true>[]) {\n // console.log(\n // JSON.stringify(\n // tasks.map((t) => t.serialize()),\n // null,\n // 2,\n // ),\n // );\n\n const locks: Lock[] = [];\n const batchLocks: Lock[] = [];\n\n const batch: Task<true>[] = [];\n\n for (const task of tasks) {\n if (task.shouldSkip) {\n // console.log(\n // `Skipping task ${task.id.serialize()} due to skipAfter limit`,\n // );\n continue;\n }\n\n if (task.shouldIsolate) {\n if (batch.length > 0) {\n return batch;\n }\n // console.log(\n // `Isolating task ${task.id.serialize()} due to isolateAfter limit`,\n // );\n batch.push(task);\n return batch;\n }\n\n if (locks.some((l) => l.restrains(task.lock))) {\n locks.push(task.lock);\n continue;\n }\n\n if (batchLocks.some((l) => l.restrains(task.lock, false))) {\n locks.push(task.lock);\n continue;\n }\n\n if (task.isProcessed) {\n continue;\n }\n\n if (task.isProcessing) {\n locks.push(task.lock);\n continue;\n }\n\n batch.push(task);\n batchLocks.push(task.lock);\n }\n\n return batch;\n }\n}\n"],"mappings":"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"}