@causa/runtime-google 0.33.1 → 0.35.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 (35) hide show
  1. package/README.md +14 -2
  2. package/dist/app-check/guard.js +4 -2
  3. package/dist/identity-platform/identity-platform.strategy.js +4 -2
  4. package/dist/pubsub/publisher.d.ts +7 -13
  5. package/dist/pubsub/publisher.js +27 -17
  6. package/dist/pubsub/publisher.module.js +1 -2
  7. package/dist/spanner/entity-manager.d.ts +22 -18
  8. package/dist/spanner/entity-manager.js +6 -6
  9. package/dist/spanner/index.d.ts +1 -1
  10. package/dist/transaction/firestore-pubsub/runner.js +4 -2
  11. package/dist/transaction/index.d.ts +3 -0
  12. package/dist/transaction/index.js +3 -0
  13. package/dist/transaction/spanner-outbox/event.d.ts +27 -0
  14. package/dist/transaction/spanner-outbox/event.js +63 -0
  15. package/dist/transaction/spanner-outbox/index.d.ts +5 -0
  16. package/dist/transaction/spanner-outbox/index.js +4 -0
  17. package/dist/transaction/spanner-outbox/module.d.ts +28 -0
  18. package/dist/transaction/spanner-outbox/module.js +91 -0
  19. package/dist/transaction/spanner-outbox/runner.d.ts +19 -0
  20. package/dist/transaction/spanner-outbox/runner.js +31 -0
  21. package/dist/transaction/spanner-outbox/sender.d.ts +95 -0
  22. package/dist/transaction/spanner-outbox/sender.js +150 -0
  23. package/dist/transaction/spanner-pubsub/index.d.ts +0 -2
  24. package/dist/transaction/spanner-pubsub/index.js +0 -2
  25. package/dist/transaction/spanner-pubsub/runner.d.ts +3 -3
  26. package/dist/transaction/spanner-pubsub/runner.js +12 -27
  27. package/dist/transaction/{spanner-pubsub/state-transaction.d.ts → spanner-state-transaction.d.ts} +4 -5
  28. package/dist/transaction/{spanner-pubsub/state-transaction.js → spanner-state-transaction.js} +2 -3
  29. package/dist/transaction/spanner-transaction.d.ts +16 -0
  30. package/dist/transaction/spanner-transaction.js +20 -0
  31. package/dist/transaction/spanner-utils.d.ts +9 -0
  32. package/dist/transaction/spanner-utils.js +31 -0
  33. package/package.json +11 -11
  34. package/dist/transaction/spanner-pubsub/transaction.d.ts +0 -17
  35. package/dist/transaction/spanner-pubsub/transaction.js +0 -21
package/README.md CHANGED
@@ -102,12 +102,24 @@ For testing, the `createDatabase` utility creates a temporary database, copying
102
102
 
103
103
  ### GCP-based Causa transaction runners
104
104
 
105
- This package provides two `TransactionRunner`s: the `SpannerPubSubTransactionRunner` and the `FirestorePubSubTransactionRunner`. Both use Pub/Sub and a `BufferEventTransaction` to publish events. They only differ by the service used to store the state.
105
+ This package provides the following `TransactionRunner`s:
106
106
 
107
- The `SpannerPubSubTransactionRunner` uses a Spanner transaction as the underlying transaction for the `SpannerPubSubTransaction`, while the `FirestorePubSubTransactionRunner` uses a Firestore transaction for the `FirestorePubSubTransaction`. Both state transactions implement the `FindReplaceStateTransaction` interface, and therefore the runners can be used with the `VersionedEntityManager`.
107
+ - `SpannerPubSubTransactionRunner`
108
+ - `FirestorePubSubTransactionRunner`
109
+ - `SpannerOutboxTransactionRunner`
110
+
111
+ The first two use Pub/Sub and a `BufferEventTransaction` to publish events. They only differ by the service used to store the state.
112
+
113
+ The `SpannerPubSubTransactionRunner` uses a Spanner transaction as the underlying transaction for the `SpannerTransaction`, while the `FirestorePubSubTransactionRunner` uses a Firestore transaction for the `FirestorePubSubTransaction`. Both state transactions implement the `FindReplaceStateTransaction` interface, and therefore the runners can be used with the `VersionedEntityManager`.
108
114
 
109
115
  One feature sets the `FirestorePubSubTransactionRunner` and its `FirestoreStateTransaction` apart: the handling of deleted entities using a separate, "soft-deleted document collection". Entities with a non-null `deletedAt` property are moved to a collection suffixed with `$deleted`, and an `_expirationDate` field is added to them. A TTL is expected to be set on this field. The `@SoftDeletedFirestoreCollection` decorator must be added to document classes that are meant to be handled using the `FirestorePubSubTransactionRunner`.
110
116
 
117
+ > [!CAUTION]
118
+ >
119
+ > `SpannerPubSubTransactionRunner` and `FirestorePubSubTransactionRunner` do not provide atomic guarantees between the state and the events being committed. This could result in events being lost, as they are published once the state transaction successfully committed. Prefer the `SpannerOutboxTransactionRunner` when applicable.
120
+
121
+ `SpannerOutboxTransactionRunner` implements the outbox pattern (from the base runtime's `OutboxTransactionRunner`), and uses the default injected `EventPublisher` (which can be the `PubSubPublisher`, if the corresponding module is imported). It requires an outbox table to be created in each database using the runner. See the documentation of the `SpannerOutboxEvent` for more information.
122
+
111
123
  ### Validation
112
124
 
113
125
  The `@IsValidFirestoreId` validation decorator checks that a property is a string which is not `.` or `..`, and does not contain forward slashes. This ensures the property's value can be used as a Firestore document ID.
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ var AppCheckGuard_1;
10
11
  import { Logger, UnauthenticatedError } from '@causa/runtime/nestjs';
11
12
  import { Injectable, } from '@nestjs/common';
12
13
  import { Reflector } from '@nestjs/core';
@@ -16,7 +17,7 @@ import { APP_CHECK_DISABLED_METADATA_KEY } from './app-check-disabled.decorator.
16
17
  * A NestJS guard that verifies the App Check token in the request.
17
18
  * The token is expected to be in the `X-Firebase-AppCheck` header.
18
19
  */
19
- let AppCheckGuard = class AppCheckGuard {
20
+ let AppCheckGuard = AppCheckGuard_1 = class AppCheckGuard {
20
21
  appCheck;
21
22
  reflector;
22
23
  logger;
@@ -24,6 +25,7 @@ let AppCheckGuard = class AppCheckGuard {
24
25
  this.appCheck = appCheck;
25
26
  this.reflector = reflector;
26
27
  this.logger = logger;
28
+ this.logger.setContext(AppCheckGuard_1.name);
27
29
  }
28
30
  async canActivate(context) {
29
31
  const isDisabled = this.reflector.getAllAndOverride(APP_CHECK_DISABLED_METADATA_KEY, [context.getHandler(), context.getClass()]);
@@ -45,7 +47,7 @@ let AppCheckGuard = class AppCheckGuard {
45
47
  }
46
48
  }
47
49
  };
48
- AppCheckGuard = __decorate([
50
+ AppCheckGuard = AppCheckGuard_1 = __decorate([
49
51
  Injectable(),
50
52
  __metadata("design:paramtypes", [AppCheck,
51
53
  Reflector,
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ var IdentityPlatformStrategy_1;
10
11
  import { Logger, UnauthenticatedError } from '@causa/runtime/nestjs';
11
12
  import { Injectable } from '@nestjs/common';
12
13
  import { ConfigService } from '@nestjs/config';
@@ -19,7 +20,7 @@ import { Strategy } from 'passport-http-bearer';
19
20
  * The `IDENTITY_PLATFORM_STRATEGY_CHECK_REVOKED_TOKEN` configuration key can be set to `true` to also check whether the
20
21
  * token was revoked. This implies a call to the Identity Platform, which will increase latency.
21
22
  */
22
- let IdentityPlatformStrategy = class IdentityPlatformStrategy extends PassportStrategy(Strategy) {
23
+ let IdentityPlatformStrategy = IdentityPlatformStrategy_1 = class IdentityPlatformStrategy extends PassportStrategy(Strategy) {
23
24
  auth;
24
25
  configService;
25
26
  logger;
@@ -32,6 +33,7 @@ let IdentityPlatformStrategy = class IdentityPlatformStrategy extends PassportSt
32
33
  this.auth = auth;
33
34
  this.configService = configService;
34
35
  this.logger = logger;
36
+ this.logger.setContext(IdentityPlatformStrategy_1.name);
35
37
  this.checkRevoked = this.configService.get('IDENTITY_PLATFORM_STRATEGY_CHECK_REVOKED_TOKEN', false);
36
38
  }
37
39
  async validate(token) {
@@ -56,7 +58,7 @@ let IdentityPlatformStrategy = class IdentityPlatformStrategy extends PassportSt
56
58
  return user;
57
59
  }
58
60
  };
59
- IdentityPlatformStrategy = __decorate([
61
+ IdentityPlatformStrategy = IdentityPlatformStrategy_1 = __decorate([
60
62
  Injectable(),
61
63
  __metadata("design:paramtypes", [Auth,
62
64
  ConfigService,
@@ -1,7 +1,7 @@
1
- import { type EventPublisher, type ObjectSerializer, type PublishOptions } from '@causa/runtime';
1
+ import { type EventPublisher, type ObjectSerializer, type PreparedEvent, type PublishOptions } from '@causa/runtime';
2
+ import { Logger } from '@causa/runtime/nestjs';
2
3
  import { PubSub, type PublishOptions as TopicPublishOptions } from '@google-cloud/pubsub';
3
4
  import type { OnApplicationShutdown } from '@nestjs/common';
4
- import type { Logger } from 'pino';
5
5
  /**
6
6
  * Options for the {@link PubSubPublisher}.
7
7
  */
@@ -32,16 +32,12 @@ export type PubSubPublisherOptions = {
32
32
  * This inherits and overrides the {@link PubSubPublisherOptions.publishOptions} for a given topic.
33
33
  */
34
34
  topicPublishOptions?: Record<string, TopicPublishOptions>;
35
- /**
36
- * The logger to use.
37
- * Defaults to {@link getDefaultLogger}.
38
- */
39
- logger?: Logger;
40
35
  };
41
36
  /**
42
37
  * An implementation of the {@link EventPublisher} using Google Pub/Sub as the broker.
43
38
  */
44
39
  export declare class PubSubPublisher implements EventPublisher, OnApplicationShutdown {
40
+ private readonly logger;
45
41
  /**
46
42
  * The {@link PubSub} client to use.
47
43
  */
@@ -59,10 +55,6 @@ export declare class PubSubPublisher implements EventPublisher, OnApplicationShu
59
55
  * A cache of Pub/Sub {@link Topic}s to which messages can be published.
60
56
  */
61
57
  private readonly topicCache;
62
- /**
63
- * The logger to use.
64
- */
65
- readonly logger: Logger;
66
58
  /**
67
59
  * The options to use when publishing messages.
68
60
  * This is used to instantiate the Pub/Sub {@link Topic}s.
@@ -77,9 +69,10 @@ export declare class PubSubPublisher implements EventPublisher, OnApplicationShu
77
69
  /**
78
70
  * Creates a new {@link PubSubPublisher}.
79
71
  *
72
+ * @param logger The logger to use.
80
73
  * @param options Options for the publisher.
81
74
  */
82
- constructor(options?: PubSubPublisherOptions);
75
+ constructor(logger: Logger, options?: PubSubPublisherOptions);
83
76
  /**
84
77
  * Returns the Pub/Sub {@link Topic} to which messages can be published for a given event topic.
85
78
  * If the topic has not been used before, it will be created using the {@link PubSubPublisher.publishOptions}.
@@ -88,7 +81,8 @@ export declare class PubSubPublisher implements EventPublisher, OnApplicationShu
88
81
  * @returns The Pub/Sub {@link Topic} to which messages can be published.
89
82
  */
90
83
  private getTopic;
91
- publish(topic: string, event: object, options?: PublishOptions): Promise<void>;
84
+ prepare(topic: string, event: object, options?: PublishOptions): Promise<PreparedEvent>;
85
+ publish(topicOrPreparedEvent: string | PreparedEvent, event?: object, options?: PublishOptions): Promise<void>;
92
86
  flush(): Promise<void>;
93
87
  onApplicationShutdown(): Promise<void>;
94
88
  }
@@ -1,4 +1,5 @@
1
- import { JsonObjectSerializer, getDefaultLogger, } from '@causa/runtime';
1
+ import { JsonObjectSerializer, } from '@causa/runtime';
2
+ import { Logger } from '@causa/runtime/nestjs';
2
3
  import { PubSub, Topic, } from '@google-cloud/pubsub';
3
4
  import { getConfigurationKeyForTopic } from './configuration.js';
4
5
  import { PubSubTopicNotConfiguredError } from './errors.js';
@@ -14,6 +15,7 @@ const DEFAULT_PUBLISH_OPTIONS = {
14
15
  * An implementation of the {@link EventPublisher} using Google Pub/Sub as the broker.
15
16
  */
16
17
  export class PubSubPublisher {
18
+ logger;
17
19
  /**
18
20
  * The {@link PubSub} client to use.
19
21
  */
@@ -31,10 +33,6 @@ export class PubSubPublisher {
31
33
  * A cache of Pub/Sub {@link Topic}s to which messages can be published.
32
34
  */
33
35
  topicCache = {};
34
- /**
35
- * The logger to use.
36
- */
37
- logger;
38
36
  /**
39
37
  * The options to use when publishing messages.
40
38
  * This is used to instantiate the Pub/Sub {@link Topic}s.
@@ -49,16 +47,18 @@ export class PubSubPublisher {
49
47
  /**
50
48
  * Creates a new {@link PubSubPublisher}.
51
49
  *
50
+ * @param logger The logger to use.
52
51
  * @param options Options for the publisher.
53
52
  */
54
- constructor(options = {}) {
53
+ constructor(logger, options = {}) {
54
+ this.logger = logger;
55
+ this.logger.setContext(PubSubPublisher.name);
55
56
  this.pubSub = options.pubSub ?? new PubSub();
56
57
  this.serializer = options.serializer ?? new JsonObjectSerializer();
57
58
  this.getConfiguration =
58
59
  options.configurationGetter ?? ((key) => process.env[key]);
59
60
  this.publishOptions = options.publishOptions ?? DEFAULT_PUBLISH_OPTIONS;
60
61
  this.topicPublishOptions = options.topicPublishOptions ?? {};
61
- this.logger = options.logger ?? getDefaultLogger();
62
62
  }
63
63
  /**
64
64
  * Returns the Pub/Sub {@link Topic} to which messages can be published for a given event topic.
@@ -85,17 +85,11 @@ export class PubSubPublisher {
85
85
  this.topicCache[topicName] = topic;
86
86
  return topic;
87
87
  }
88
- async publish(topic, event, options = {}) {
88
+ async prepare(topic, event, options = {}) {
89
89
  const data = await this.serializer.serialize(event);
90
- const pubSubTopic = this.getTopic(topic);
91
90
  const defaultAttributes = {};
92
- const baseLogData = {
93
- topic,
94
- pubSubTopic: pubSubTopic.name,
95
- };
96
91
  if ('id' in event && typeof event.id === 'string') {
97
92
  defaultAttributes.eventId = event.id;
98
- baseLogData.eventId = event.id;
99
93
  }
100
94
  if ('producedAt' in event && event.producedAt instanceof Date) {
101
95
  defaultAttributes.producedAt = event.producedAt.toISOString();
@@ -105,14 +99,29 @@ export class PubSubPublisher {
105
99
  }
106
100
  const attributes = {
107
101
  ...defaultAttributes,
108
- ...options.attributes,
102
+ ...options?.attributes,
103
+ };
104
+ const key = options?.key;
105
+ return { topic, data, attributes, key };
106
+ }
107
+ async publish(topicOrPreparedEvent, event, options = {}) {
108
+ const isPrepared = typeof topicOrPreparedEvent !== 'string';
109
+ const { topic, data, attributes, key } = isPrepared
110
+ ? topicOrPreparedEvent
111
+ : await this.prepare(topicOrPreparedEvent, event, options);
112
+ const pubSubTopic = this.getTopic(topic);
113
+ const baseLogData = {
114
+ topic,
115
+ pubSubTopic: pubSubTopic.name,
109
116
  };
110
- const orderingKey = options.key;
117
+ if (attributes && 'eventId' in attributes) {
118
+ baseLogData.eventId = attributes.eventId;
119
+ }
111
120
  try {
112
121
  const pubSubMessageId = await pubSubTopic.publishMessage({
113
122
  data,
114
123
  attributes,
115
- orderingKey,
124
+ orderingKey: key,
116
125
  });
117
126
  this.logger.info({ ...baseLogData, pubSubMessageId }, 'Published message to Pub/Sub.');
118
127
  }
@@ -121,6 +130,7 @@ export class PubSubPublisher {
121
130
  ...baseLogData,
122
131
  pubSubMessage: data.toString('base64'),
123
132
  pubSubAttributes: attributes,
133
+ pubSubOrderingKey: key,
124
134
  errorMessage: error.message,
125
135
  errorStack: error.stack,
126
136
  }, 'Failed to publish message to Pub/Sub.');
@@ -28,11 +28,10 @@ export class PubSubPublisherModule {
28
28
  },
29
29
  {
30
30
  provide: PubSubPublisher,
31
- useFactory: (pubSub, configurationGetter, { logger }) => new PubSubPublisher({
31
+ useFactory: (pubSub, configurationGetter, logger) => new PubSubPublisher(logger, {
32
32
  ...options,
33
33
  pubSub,
34
34
  configurationGetter,
35
- logger,
36
35
  }),
37
36
  inject: [
38
37
  PubSub,
@@ -8,6 +8,10 @@ import type { RecursivePartialEntity } from './types.js';
8
8
  * Any Spanner transaction that can be used for reading.
9
9
  */
10
10
  export type SpannerReadOnlyTransaction = Snapshot | Transaction;
11
+ /**
12
+ * A Spanner transaction that can be used for reading and writing.
13
+ */
14
+ export type SpannerReadWriteTransaction = Transaction;
11
15
  /**
12
16
  * A key for a Spanner row.
13
17
  */
@@ -17,16 +21,16 @@ export type SpannerKey = (string | null)[];
17
21
  */
18
22
  type WriteOperationOptions = {
19
23
  /**
20
- * The {@link Transaction} to use.
24
+ * The {@link SpannerReadWriteTransaction} to use.
21
25
  */
22
- transaction?: Transaction;
26
+ transaction?: SpannerReadWriteTransaction;
23
27
  };
24
28
  /**
25
29
  * Base options for all read operations.
26
30
  */
27
31
  type ReadOperationOptions = {
28
32
  /**
29
- * The {@link Transaction} or {@link Snapshot} to use.
33
+ * The {@link SpannerReadOnlyTransaction} to use.
30
34
  */
31
35
  transaction?: SpannerReadOnlyTransaction;
32
36
  };
@@ -42,7 +46,7 @@ type SnapshotOptions = {
42
46
  /**
43
47
  * A function that can be passed to the {@link SpannerEntityManager.snapshot} method.
44
48
  */
45
- export type SnapshotFunction<T> = (snapshot: Snapshot) => Promise<T>;
49
+ export type SnapshotFunction<T> = (snapshot: SpannerReadOnlyTransaction) => Promise<T>;
46
50
  /**
47
51
  * A SQL statement run using {@link SpannerEntityManager.query}.
48
52
  */
@@ -196,16 +200,16 @@ export declare class SpannerEntityManager {
196
200
  */
197
201
  findOneByKeyOrFail<T>(entityType: Type<T>, key: SpannerKey | SpannerKey[number], options?: FindOptions): Promise<T>;
198
202
  /**
199
- * Runs the provided function in a (read write) {@link Transaction}.
203
+ * Runs the provided function in a (read write) {@link SpannerReadWriteTransaction}.
200
204
  * The function itself should not commit or rollback the transaction.
201
205
  * If the function throws an error, the transaction will be rolled back.
202
206
  *
203
207
  * @param runFn The function to run in the transaction.
204
208
  * @returns The return value of the function.
205
209
  */
206
- transaction<T>(runFn: (transaction: Transaction) => Promise<T>): Promise<T>;
210
+ transaction<T>(runFn: (transaction: SpannerReadWriteTransaction) => Promise<T>): Promise<T>;
207
211
  /**
208
- * Runs the provided function in a read-only transaction ({@link Snapshot}).
212
+ * Runs the provided function in a {@link SpannerReadOnlyTransaction}.
209
213
  * The snapshot will be automatically released when the function returns.
210
214
  *
211
215
  * @param runFn The function to run in the transaction.
@@ -213,7 +217,7 @@ export declare class SpannerEntityManager {
213
217
  */
214
218
  snapshot<T>(runFn: SnapshotFunction<T>): Promise<T>;
215
219
  /**
216
- * Runs the provided function in a read-only transaction ({@link Snapshot}).
220
+ * Runs the provided function in a {@link SpannerReadOnlyTransaction}.
217
221
  * The snapshot will be automatically released when the function returns.
218
222
  *
219
223
  * @param options The options to use when creating the snapshot.
@@ -230,8 +234,8 @@ export declare class SpannerEntityManager {
230
234
  clear(entityType: Type, options?: WriteOperationOptions): Promise<void>;
231
235
  /**
232
236
  * Runs the given SQL statement in the database.
233
- * By default, the statement is run in a read-only transaction ({@link Snapshot}). To perform a write operation, pass
234
- * a {@link Transaction} in the options.
237
+ * By default, the statement is run in a {@link SpannerReadOnlyTransaction}. To perform a write operation, pass a
238
+ * {@link SpannerReadWriteTransaction} in the options.
235
239
  *
236
240
  * @param options Options for the operation.
237
241
  * @param statement The SQL statement to run.
@@ -241,7 +245,7 @@ export declare class SpannerEntityManager {
241
245
  query<T>(options: QueryOptions<T>, statement: SqlStatement): Promise<T[]>;
242
246
  /**
243
247
  * Runs the given SQL statement in the database.
244
- * The statement is run in a read-only transaction ({@link Snapshot}).
248
+ * The statement is run in a {@link SpannerReadOnlyTransaction}.
245
249
  *
246
250
  * @param statement The SQL statement to run.
247
251
  * @returns The rows returned by the query.
@@ -331,22 +335,22 @@ export declare class SpannerEntityManager {
331
335
  validateFn?: (entity: T) => void;
332
336
  }): Promise<T>;
333
337
  /**
334
- * Runs the given "read-write" function on a transaction. If a transaction is not passed, a new {@link Transaction} is
335
- * created instead.
338
+ * Runs the given "read-write" function on a transaction. If a transaction is not passed, a new
339
+ * {@link SpannerReadWriteTransaction} is created instead.
336
340
  *
337
341
  * @param transaction The transaction to use. If `undefined`, a new transaction is created.
338
342
  * @param fn The function to run on the transaction.
339
343
  * @returns The result of the function.
340
344
  */
341
- runInExistingOrNewTransaction<T>(transaction: Transaction | undefined, fn: (transaction: Transaction) => Promise<T>): Promise<T>;
345
+ runInExistingOrNewTransaction<T>(transaction: SpannerReadWriteTransaction | undefined, fn: (transaction: SpannerReadWriteTransaction) => Promise<T>): Promise<T>;
342
346
  /**
343
- * Runs the given "read-only" function on a transaction. If a transaction is not passed, a new {@link Snapshot} is
344
- * created instead.
347
+ * Runs the given "read-only" function on a transaction. If a transaction is not passed, a new
348
+ * {@link SpannerReadOnlyTransaction} is created instead.
345
349
  *
346
- * @param transaction The transaction to use. If `undefined`, a new {@link Snapshot} is created.
350
+ * @param transaction The transaction to use. If `undefined`, a new {@link SpannerReadOnlyTransaction} is created.
347
351
  * @param fn The function to run on the transaction.
348
352
  * @returns The result of the function.
349
353
  */
350
- runInExistingOrNewReadOnlyTransaction<T>(transaction: SpannerReadOnlyTransaction | undefined, fn: (transaction: SpannerReadOnlyTransaction) => Promise<T>): Promise<T>;
354
+ runInExistingOrNewReadOnlyTransaction<T>(transaction: SpannerReadOnlyTransaction | undefined, fn: SnapshotFunction<T>): Promise<T>;
351
355
  }
352
356
  export {};
@@ -202,7 +202,7 @@ let SpannerEntityManager = class SpannerEntityManager {
202
202
  return entity;
203
203
  }
204
204
  /**
205
- * Runs the provided function in a (read write) {@link Transaction}.
205
+ * Runs the provided function in a (read write) {@link SpannerReadWriteTransaction}.
206
206
  * The function itself should not commit or rollback the transaction.
207
207
  * If the function throws an error, the transaction will be rolled back.
208
208
  *
@@ -440,8 +440,8 @@ let SpannerEntityManager = class SpannerEntityManager {
440
440
  });
441
441
  }
442
442
  /**
443
- * Runs the given "read-write" function on a transaction. If a transaction is not passed, a new {@link Transaction} is
444
- * created instead.
443
+ * Runs the given "read-write" function on a transaction. If a transaction is not passed, a new
444
+ * {@link SpannerReadWriteTransaction} is created instead.
445
445
  *
446
446
  * @param transaction The transaction to use. If `undefined`, a new transaction is created.
447
447
  * @param fn The function to run on the transaction.
@@ -459,10 +459,10 @@ let SpannerEntityManager = class SpannerEntityManager {
459
459
  return this.transaction(fn);
460
460
  }
461
461
  /**
462
- * Runs the given "read-only" function on a transaction. If a transaction is not passed, a new {@link Snapshot} is
463
- * created instead.
462
+ * Runs the given "read-only" function on a transaction. If a transaction is not passed, a new
463
+ * {@link SpannerReadOnlyTransaction} is created instead.
464
464
  *
465
- * @param transaction The transaction to use. If `undefined`, a new {@link Snapshot} is created.
465
+ * @param transaction The transaction to use. If `undefined`, a new {@link SpannerReadOnlyTransaction} is created.
466
466
  * @param fn The function to run on the transaction.
467
467
  * @returns The result of the function.
468
468
  */
@@ -1,7 +1,7 @@
1
1
  export { SpannerColumn } from './column.decorator.js';
2
2
  export { SPANNER_SESSION_POOL_OPTIONS_FOR_CLOUD_FUNCTIONS, SPANNER_SESSION_POOL_OPTIONS_FOR_SERVICE, catchSpannerDatabaseErrors, getDefaultSpannerDatabaseForCloudFunction, } from './database.js';
3
3
  export { SpannerEntityManager } from './entity-manager.js';
4
- export type { SpannerKey, SpannerReadOnlyTransaction, } from './entity-manager.js';
4
+ export type { SpannerKey, SpannerReadOnlyTransaction, SpannerReadWriteTransaction, } from './entity-manager.js';
5
5
  export * from './errors.js';
6
6
  export { SpannerHealthIndicator } from './healthcheck.js';
7
7
  export { SpannerModule } from './module.js';
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ var FirestorePubSubTransactionRunner_1;
10
11
  import { BufferEventTransaction, TransactionRunner } from '@causa/runtime';
11
12
  import { Logger } from '@causa/runtime/nestjs';
12
13
  import { Firestore } from '@google-cloud/firestore';
@@ -22,7 +23,7 @@ import { FirestorePubSubTransaction } from './transaction.js';
22
23
  * This runner and the transaction use the {@link FirestoreStateTransaction}, which handles soft-deleted documents. All
23
24
  * entities that are written to the state should be decorated with the `SoftDeletedFirestoreCollection` decorator.
24
25
  */
25
- let FirestorePubSubTransactionRunner = class FirestorePubSubTransactionRunner extends TransactionRunner {
26
+ let FirestorePubSubTransactionRunner = FirestorePubSubTransactionRunner_1 = class FirestorePubSubTransactionRunner extends TransactionRunner {
26
27
  firestore;
27
28
  pubSubPublisher;
28
29
  collectionResolver;
@@ -33,6 +34,7 @@ let FirestorePubSubTransactionRunner = class FirestorePubSubTransactionRunner ex
33
34
  this.pubSubPublisher = pubSubPublisher;
34
35
  this.collectionResolver = collectionResolver;
35
36
  this.logger = logger;
37
+ this.logger.setContext(FirestorePubSubTransactionRunner_1.name);
36
38
  }
37
39
  async run(runFn) {
38
40
  this.logger.info('Creating a Firestore Pub/Sub transaction.');
@@ -49,7 +51,7 @@ let FirestorePubSubTransactionRunner = class FirestorePubSubTransactionRunner ex
49
51
  return [result];
50
52
  }
51
53
  };
52
- FirestorePubSubTransactionRunner = __decorate([
54
+ FirestorePubSubTransactionRunner = FirestorePubSubTransactionRunner_1 = __decorate([
53
55
  Injectable(),
54
56
  __metadata("design:paramtypes", [Firestore,
55
57
  PubSubPublisher, Object, Logger])
@@ -1,2 +1,5 @@
1
1
  export * from './firestore-pubsub/index.js';
2
+ export * from './spanner-outbox/index.js';
2
3
  export * from './spanner-pubsub/index.js';
4
+ export { SpannerStateTransaction } from './spanner-state-transaction.js';
5
+ export { SpannerTransaction } from './spanner-transaction.js';
@@ -1,2 +1,5 @@
1
1
  export * from './firestore-pubsub/index.js';
2
+ export * from './spanner-outbox/index.js';
2
3
  export * from './spanner-pubsub/index.js';
4
+ export { SpannerStateTransaction } from './spanner-state-transaction.js';
5
+ export { SpannerTransaction } from './spanner-transaction.js';
@@ -0,0 +1,27 @@
1
+ import type { EventAttributes, OutboxEvent } from '@causa/runtime';
2
+ /**
3
+ * A Spanner table that implements the {@link OutboxEvent} interface, such that it can be used to store outbox event.
4
+ *
5
+ * The full DDL for the table to be used by the outbox transaction runner is:
6
+ *
7
+ * ```sql
8
+ * CREATE TABLE OutboxEvent (
9
+ * id STRING(36) NOT NULL,
10
+ * topic STRING(MAX) NOT NULL,
11
+ * data BYTES(MAX) NOT NULL,
12
+ * attributes JSON NOT NULL,
13
+ * leaseExpiration TIMESTAMP,
14
+ * -- 20 is the number of shards.
15
+ * shard INT64 AS (MOD(ABS(FARM_FINGERPRINT(id)), 20)),
16
+ * ) PRIMARY KEY (id);
17
+ * CREATE INDEX OutboxEventsByShardAndLeaseExpiration ON OutboxEvent(shard, leaseExpiration)
18
+ * ```
19
+ */
20
+ export declare class SpannerOutboxEvent implements OutboxEvent {
21
+ constructor(init: SpannerOutboxEvent);
22
+ readonly id: string;
23
+ readonly topic: string;
24
+ readonly data: Buffer;
25
+ readonly attributes: EventAttributes;
26
+ readonly leaseExpiration: Date | null;
27
+ }
@@ -0,0 +1,63 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { SpannerColumn, SpannerTable } from '../../spanner/index.js';
11
+ /**
12
+ * A Spanner table that implements the {@link OutboxEvent} interface, such that it can be used to store outbox event.
13
+ *
14
+ * The full DDL for the table to be used by the outbox transaction runner is:
15
+ *
16
+ * ```sql
17
+ * CREATE TABLE OutboxEvent (
18
+ * id STRING(36) NOT NULL,
19
+ * topic STRING(MAX) NOT NULL,
20
+ * data BYTES(MAX) NOT NULL,
21
+ * attributes JSON NOT NULL,
22
+ * leaseExpiration TIMESTAMP,
23
+ * -- 20 is the number of shards.
24
+ * shard INT64 AS (MOD(ABS(FARM_FINGERPRINT(id)), 20)),
25
+ * ) PRIMARY KEY (id);
26
+ * CREATE INDEX OutboxEventsByShardAndLeaseExpiration ON OutboxEvent(shard, leaseExpiration)
27
+ * ```
28
+ */
29
+ let SpannerOutboxEvent = class SpannerOutboxEvent {
30
+ constructor(init) {
31
+ Object.assign(this, init);
32
+ }
33
+ id;
34
+ topic;
35
+ data;
36
+ attributes;
37
+ leaseExpiration;
38
+ };
39
+ __decorate([
40
+ SpannerColumn(),
41
+ __metadata("design:type", String)
42
+ ], SpannerOutboxEvent.prototype, "id", void 0);
43
+ __decorate([
44
+ SpannerColumn(),
45
+ __metadata("design:type", String)
46
+ ], SpannerOutboxEvent.prototype, "topic", void 0);
47
+ __decorate([
48
+ SpannerColumn(),
49
+ __metadata("design:type", Buffer)
50
+ ], SpannerOutboxEvent.prototype, "data", void 0);
51
+ __decorate([
52
+ SpannerColumn({ isJson: true }),
53
+ __metadata("design:type", Object)
54
+ ], SpannerOutboxEvent.prototype, "attributes", void 0);
55
+ __decorate([
56
+ SpannerColumn(),
57
+ __metadata("design:type", Object)
58
+ ], SpannerOutboxEvent.prototype, "leaseExpiration", void 0);
59
+ SpannerOutboxEvent = __decorate([
60
+ SpannerTable({ name: 'OutboxEvent', primaryKey: ['id'] }),
61
+ __metadata("design:paramtypes", [SpannerOutboxEvent])
62
+ ], SpannerOutboxEvent);
63
+ export { SpannerOutboxEvent };
@@ -0,0 +1,5 @@
1
+ export { SpannerOutboxEvent } from './event.js';
2
+ export { SpannerOutboxTransactionModule } from './module.js';
3
+ export { SpannerOutboxTransactionRunner } from './runner.js';
4
+ export type { SpannerOutboxTransaction } from './runner.js';
5
+ export { SpannerOutboxSender } from './sender.js';
@@ -0,0 +1,4 @@
1
+ export { SpannerOutboxEvent } from './event.js';
2
+ export { SpannerOutboxTransactionModule } from './module.js';
3
+ export { SpannerOutboxTransactionRunner } from './runner.js';
4
+ export { SpannerOutboxSender } from './sender.js';
@@ -0,0 +1,28 @@
1
+ import type { OutboxEvent } from '@causa/runtime';
2
+ import type { DynamicModule, Type } from '@nestjs/common';
3
+ import { type SpannerOutboxSenderOptions } from './sender.js';
4
+ /**
5
+ * Options for the {@link SpannerOutboxTransactionModule}.
6
+ */
7
+ export type SpannerOutboxTransactionModuleOptions = SpannerOutboxSenderOptions & {
8
+ /**
9
+ * The type of {@link OutboxEvent} used by the {@link SpannerOutboxTransactionRunner}.
10
+ * This should be a valid class decorated with `@SpannerTable`.
11
+ * Defaults to {@link SpannerOutboxEvent}.
12
+ */
13
+ outboxEventType?: Type<OutboxEvent>;
14
+ };
15
+ /**
16
+ * The module providing the {@link SpannerOutboxTransactionRunner}.
17
+ * This assumes the `SpannerModule` and an {@link EventPublisher} are available (as well as the `LoggerModule`).
18
+ */
19
+ export declare class SpannerOutboxTransactionModule {
20
+ /**
21
+ * Initializes the {@link SpannerOutboxTransactionModule} with the given options.
22
+ * The returned module is always global.
23
+ *
24
+ * @param options Options for the {@link SpannerOutboxTransactionModule}.
25
+ * @returns The module.
26
+ */
27
+ static forRoot(options?: SpannerOutboxTransactionModuleOptions): DynamicModule;
28
+ }