@causa/runtime-google 1.2.0 → 1.3.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.
@@ -1,7 +1,7 @@
1
1
  export { SpannerColumn } from './column.decorator.js';
2
- export { SPANNER_SESSION_POOL_OPTIONS_FOR_CLOUD_FUNCTIONS, SPANNER_SESSION_POOL_OPTIONS_FOR_SERVICE, catchSpannerDatabaseErrors, getDefaultSpannerDatabaseForCloudFunction, } from './database.js';
2
+ export { catchSpannerDatabaseErrors, getDefaultSpannerDatabaseForCloudFunction, SPANNER_SESSION_POOL_OPTIONS_FOR_CLOUD_FUNCTIONS, SPANNER_SESSION_POOL_OPTIONS_FOR_SERVICE, } from './database.js';
3
3
  export { SpannerEntityManager } from './entity-manager.js';
4
- export type { SpannerKey, SpannerReadOnlyTransaction, SpannerReadWriteTransaction, } from './entity-manager.js';
4
+ export type { SpannerKey, SpannerReadOnlyTransaction, SpannerReadWriteTransaction, SqlStatement, } from './entity-manager.js';
5
5
  export * from './errors.js';
6
6
  export { SpannerHealthIndicator } from './healthcheck.js';
7
7
  export { SpannerModule } from './module.js';
@@ -1,5 +1,5 @@
1
1
  export { SpannerColumn } from './column.decorator.js';
2
- export { SPANNER_SESSION_POOL_OPTIONS_FOR_CLOUD_FUNCTIONS, SPANNER_SESSION_POOL_OPTIONS_FOR_SERVICE, catchSpannerDatabaseErrors, getDefaultSpannerDatabaseForCloudFunction, } from './database.js';
2
+ export { catchSpannerDatabaseErrors, getDefaultSpannerDatabaseForCloudFunction, SPANNER_SESSION_POOL_OPTIONS_FOR_CLOUD_FUNCTIONS, SPANNER_SESSION_POOL_OPTIONS_FOR_SERVICE, } from './database.js';
3
3
  export { SpannerEntityManager } from './entity-manager.js';
4
4
  export * from './errors.js';
5
5
  export { SpannerHealthIndicator } from './healthcheck.js';
@@ -1,11 +1,21 @@
1
1
  import type { ReadOnlyStateTransaction, ReadOnlyTransactionOption } from '@causa/runtime';
2
2
  import type { Type } from '@nestjs/common';
3
3
  import { Transaction } from 'firebase-admin/firestore';
4
+ import { type SoftDeletedFirestoreCollectionMetadata } from './soft-deleted-collection.decorator.js';
4
5
  import type { FirestoreCollectionResolver } from './types.js';
5
6
  /**
6
7
  * Option for a function that accepts a {@link FirestoreReadOnlyStateTransaction}.
7
8
  */
8
9
  export type FirestoreReadOnlyStateTransactionOption = ReadOnlyTransactionOption<FirestoreReadOnlyStateTransaction>;
10
+ /**
11
+ * Information about soft-deletion for a given document.
12
+ */
13
+ export type SoftDeleteInfo<T extends object> = Omit<SoftDeletedFirestoreCollectionMetadata, 'deletedDocumentsCollectionSuffix'> & {
14
+ /**
15
+ * The reference to the soft-deleted document.
16
+ */
17
+ ref: FirebaseFirestore.DocumentReference<T>;
18
+ };
9
19
  /**
10
20
  * A {@link ReadOnlyStateTransaction} that uses Firestore for state storage.
11
21
  *
@@ -33,5 +43,14 @@ export declare class FirestoreReadOnlyStateTransaction implements ReadOnlyStateT
33
43
  * The resolver that provides the Firestore collections for a given document type.
34
44
  */
35
45
  collectionResolver: FirestoreCollectionResolver);
46
+ /**
47
+ * Returns the soft-delete information for a given document, if the document type supports soft-deletion.
48
+ *
49
+ * @param activeDocRef The reference to the active document.
50
+ * @param type The type of the document.
51
+ * @returns The soft-delete information for the document, or `null` if the document type does not support
52
+ * soft-deletion.
53
+ */
54
+ protected getSoftDeleteInfo<T extends object>(activeDocRef: FirebaseFirestore.DocumentReference<T>, type: Type<T>): SoftDeleteInfo<T> | null;
36
55
  get<T extends object>(type: Type<T>, entity: Partial<T>): Promise<T | null>;
37
56
  }
@@ -1,5 +1,6 @@
1
1
  import { Transaction } from 'firebase-admin/firestore';
2
- import { getReferenceForFirestoreDocument } from '../../firestore/index.js';
2
+ import { getReferenceForFirestoreDocument, makeFirestoreDataConverter, } from '../../firestore/index.js';
3
+ import { getSoftDeletedFirestoreCollectionMetadataForType, } from './soft-deleted-collection.decorator.js';
3
4
  /**
4
5
  * A {@link ReadOnlyStateTransaction} that uses Firestore for state storage.
5
6
  *
@@ -24,24 +25,44 @@ export class FirestoreReadOnlyStateTransaction {
24
25
  this.firestoreTransaction = firestoreTransaction;
25
26
  this.collectionResolver = collectionResolver;
26
27
  }
28
+ /**
29
+ * Returns the soft-delete information for a given document, if the document type supports soft-deletion.
30
+ *
31
+ * @param activeDocRef The reference to the active document.
32
+ * @param type The type of the document.
33
+ * @returns The soft-delete information for the document, or `null` if the document type does not support
34
+ * soft-deletion.
35
+ */
36
+ getSoftDeleteInfo(activeDocRef, type) {
37
+ const softDeleteMetadata = getSoftDeletedFirestoreCollectionMetadataForType(type);
38
+ if (!softDeleteMetadata) {
39
+ return null;
40
+ }
41
+ const { deletedDocumentsCollectionSuffix: suffix, ...info } = softDeleteMetadata;
42
+ const softDeleteCollection = activeDocRef.firestore
43
+ .collection(`${activeDocRef.parent.path}${suffix}`)
44
+ .withConverter(makeFirestoreDataConverter(type));
45
+ const ref = softDeleteCollection.doc(activeDocRef.id);
46
+ return { ...info, ref };
47
+ }
27
48
  async get(type, entity) {
28
- const { activeCollection, softDelete } = this.collectionResolver.getCollectionsForType(type);
49
+ const { activeCollection } = this.collectionResolver.getCollectionsForType(type);
29
50
  const activeDocRef = getReferenceForFirestoreDocument(activeCollection, entity, type);
30
51
  const activeSnapshot = await this.firestoreTransaction.get(activeDocRef);
31
52
  const activeDocument = activeSnapshot.data();
32
53
  if (activeDocument) {
33
54
  return activeDocument;
34
55
  }
35
- if (!softDelete) {
56
+ const softDeleteInfo = this.getSoftDeleteInfo(activeDocRef, type);
57
+ if (!softDeleteInfo) {
36
58
  return null;
37
59
  }
38
- const deletedDocRef = getReferenceForFirestoreDocument(softDelete.collection, entity, type);
39
- const deletedSnapshot = await this.firestoreTransaction.get(deletedDocRef);
60
+ const deletedSnapshot = await this.firestoreTransaction.get(softDeleteInfo.ref);
40
61
  const deletedDocument = deletedSnapshot.data();
41
62
  if (!deletedDocument) {
42
63
  return null;
43
64
  }
44
- delete deletedDocument[softDelete.expirationField];
65
+ delete deletedDocument[softDeleteInfo.expirationField];
45
66
  return deletedDocument;
46
67
  }
47
68
  }
@@ -22,26 +22,26 @@ export class FirestoreStateTransaction extends FirestoreReadOnlyStateTransaction
22
22
  async delete(typeOrEntity, key) {
23
23
  const type = (key === undefined ? typeOrEntity.constructor : typeOrEntity);
24
24
  key ??= typeOrEntity;
25
- const { activeCollection, softDelete } = this.collectionResolver.getCollectionsForType(type);
25
+ const { activeCollection } = this.collectionResolver.getCollectionsForType(type);
26
26
  const activeDocRef = getReferenceForFirestoreDocument(activeCollection, key, type);
27
27
  this.firestoreTransaction.delete(activeDocRef);
28
- if (!softDelete) {
28
+ const softDeleteInfo = this.getSoftDeleteInfo(activeDocRef, type);
29
+ if (!softDeleteInfo) {
29
30
  return;
30
31
  }
31
- const deletedDocRef = getReferenceForFirestoreDocument(softDelete.collection, key, type);
32
- this.firestoreTransaction.delete(deletedDocRef);
32
+ this.firestoreTransaction.delete(softDeleteInfo.ref);
33
33
  }
34
34
  async set(entity) {
35
35
  const documentType = entity.constructor;
36
- const { activeCollection, softDelete } = this.collectionResolver.getCollectionsForType(documentType);
36
+ const { activeCollection } = this.collectionResolver.getCollectionsForType(documentType);
37
37
  const activeDocRef = getReferenceForFirestoreDocument(activeCollection, entity);
38
- if (!softDelete) {
38
+ const softDeleteInfo = this.getSoftDeleteInfo(activeDocRef, documentType);
39
+ if (!softDeleteInfo) {
39
40
  this.firestoreTransaction.set(activeDocRef, entity);
40
41
  return;
41
42
  }
42
- const deletedDocRef = getReferenceForFirestoreDocument(softDelete.collection, entity);
43
+ const { ref: deletedDocRef, expirationDelay, expirationField, } = softDeleteInfo;
43
44
  if ('deletedAt' in entity && entity.deletedAt instanceof Date) {
44
- const { expirationDelay, expirationField } = softDelete;
45
45
  const expiresAt = new Date(entity.deletedAt.getTime() + expirationDelay);
46
46
  const deletedDoc = plainToInstance(documentType, {
47
47
  ...entity,
@@ -12,6 +12,8 @@ export type FirestoreCollectionsForDocumentType<T> = {
12
12
  /**
13
13
  * Configuration about the soft-delete collection, where documents are stored when their `deletedAt` field is not
14
14
  * `null`. This can be `null` if the document type does not declare a soft-delete collection.
15
+ *
16
+ * @deprecated Use `SoftDeleteInfo` in `FirestoreReadOnlyStateTransaction.getSoftDeleteInfo` instead.
15
17
  */
16
18
  readonly softDelete: ({
17
19
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@causa/runtime-google",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "An extension to the Causa runtime SDK (`@causa/runtime`), providing Google-specific features.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,11 +32,11 @@
32
32
  "test:cov": "npm run test -- --coverage"
33
33
  },
34
34
  "dependencies": {
35
- "@causa/runtime": "^1.1.0",
35
+ "@causa/runtime": "^1.3.0",
36
36
  "@google-cloud/precise-date": "^5.0.0",
37
37
  "@google-cloud/pubsub": "^5.2.0",
38
- "@google-cloud/spanner": "^8.2.1",
39
- "@google-cloud/tasks": "^6.2.0",
38
+ "@google-cloud/spanner": "^8.2.2",
39
+ "@google-cloud/tasks": "^6.2.1",
40
40
  "@grpc/grpc-js": "^1.14.0",
41
41
  "@nestjs/common": "^11.1.6",
42
42
  "@nestjs/config": "^4.0.2",
@@ -49,7 +49,7 @@
49
49
  "firebase-admin": "^13.5.0",
50
50
  "jsonwebtoken": "^9.0.2",
51
51
  "passport-http-bearer": "^1.0.1",
52
- "pino": "^9.12.0",
52
+ "pino": "^9.13.1",
53
53
  "reflect-metadata": "^0.2.2"
54
54
  },
55
55
  "devDependencies": {
@@ -59,21 +59,21 @@
59
59
  "@tsconfig/node22": "^22.0.2",
60
60
  "@types/jest": "^30.0.0",
61
61
  "@types/jsonwebtoken": "^9.0.10",
62
- "@types/node": "^22.18.8",
62
+ "@types/node": "^22.18.11",
63
63
  "@types/passport-http-bearer": "^1.0.42",
64
64
  "@types/supertest": "^6.0.3",
65
65
  "@types/uuid": "^11.0.0",
66
66
  "dotenv": "^17.2.3",
67
- "eslint": "^9.36.0",
67
+ "eslint": "^9.38.0",
68
68
  "eslint-config-prettier": "^10.1.8",
69
69
  "eslint-plugin-prettier": "^5.5.4",
70
70
  "jest": "^30.2.0",
71
71
  "jest-extended": "^6.0.0",
72
- "pino-pretty": "^13.1.1",
72
+ "pino-pretty": "^13.1.2",
73
73
  "rimraf": "^6.0.1",
74
74
  "supertest": "^7.1.4",
75
75
  "typescript": "^5.9.3",
76
- "typescript-eslint": "^8.45.0",
76
+ "typescript-eslint": "^8.46.1",
77
77
  "uuid": "^13.0.0"
78
78
  }
79
79
  }