@palantir/pack.state.core 0.1.0-beta.2 → 0.1.1

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 (40) hide show
  1. package/.turbo/turbo-lint.log +2 -2
  2. package/.turbo/turbo-transpileBrowser.log +1 -1
  3. package/.turbo/turbo-transpileCjs.log +1 -1
  4. package/.turbo/turbo-transpileEsm.log +1 -1
  5. package/.turbo/turbo-transpileTypes.log +1 -1
  6. package/.turbo/turbo-typecheck.log +1 -1
  7. package/CHANGELOG.md +33 -0
  8. package/build/browser/index.js +73 -0
  9. package/build/browser/index.js.map +1 -1
  10. package/build/cjs/index.cjs +73 -0
  11. package/build/cjs/index.cjs.map +1 -1
  12. package/build/cjs/index.d.cts +106 -1
  13. package/build/esm/index.js +73 -0
  14. package/build/esm/index.js.map +1 -1
  15. package/build/types/__tests__/Transaction.test.d.ts +1 -0
  16. package/build/types/__tests__/Transaction.test.d.ts.map +1 -0
  17. package/build/types/__tests__/testUtils.d.ts.map +1 -1
  18. package/build/types/service/BaseYjsDocumentService.d.ts +11 -1
  19. package/build/types/service/BaseYjsDocumentService.d.ts.map +1 -1
  20. package/build/types/service/InMemoryDocumentService.d.ts.map +1 -1
  21. package/build/types/types/DocumentRefImpl.d.ts +20 -0
  22. package/build/types/types/DocumentRefImpl.d.ts.map +1 -1
  23. package/build/types/types/DocumentService.d.ts +18 -1
  24. package/build/types/types/DocumentService.d.ts.map +1 -1
  25. package/build/types/types/RecordCollectionRefImpl.d.ts +23 -0
  26. package/build/types/types/RecordCollectionRefImpl.d.ts.map +1 -1
  27. package/build/types/types/RecordRefImpl.d.ts +25 -0
  28. package/build/types/types/RecordRefImpl.d.ts.map +1 -1
  29. package/build/types/types/StateModule.d.ts +21 -1
  30. package/build/types/types/StateModule.d.ts.map +1 -1
  31. package/package.json +6 -6
  32. package/src/__tests__/Transaction.test.ts +321 -0
  33. package/src/__tests__/testUtils.ts +1 -2
  34. package/src/service/BaseYjsDocumentService.ts +41 -0
  35. package/src/service/InMemoryDocumentService.ts +58 -1
  36. package/src/types/DocumentRefImpl.ts +58 -0
  37. package/src/types/DocumentService.ts +43 -0
  38. package/src/types/RecordCollectionRefImpl.ts +23 -0
  39. package/src/types/RecordRefImpl.ts +25 -0
  40. package/src/types/StateModule.ts +78 -0
@@ -19,14 +19,17 @@
19
19
  import type { Logger } from "@osdk/api";
20
20
  import type { PackAppInternal, Unsubscribe } from "@palantir/pack.core";
21
21
  import {
22
+ type ActivityEvent,
22
23
  type DocumentId,
23
24
  type DocumentMetadata,
24
25
  type DocumentRef,
25
26
  type DocumentSchema,
26
27
  type DocumentState,
28
+ type EditDescription,
27
29
  getMetadata,
28
30
  type Model,
29
31
  type ModelData,
32
+ type PresenceEvent,
30
33
  type RecordCollectionRef,
31
34
  type RecordId,
32
35
  type RecordRef,
@@ -124,6 +127,14 @@ export abstract class BaseYjsDocumentService<TDoc extends InternalYjsDoc = Inter
124
127
  metadata: DocumentMetadata,
125
128
  schema: T,
126
129
  ) => Promise<DocumentRef<T>>;
130
+ abstract readonly searchDocuments: <T extends DocumentSchema>(
131
+ documentTypeName: string,
132
+ schema: T,
133
+ options?: {
134
+ documentName?: string;
135
+ limit?: number;
136
+ },
137
+ ) => Promise<ReadonlyArray<DocumentMetadata & { readonly id: DocumentId }>>;
127
138
 
128
139
  readonly createDocRef = <const T extends DocumentSchema>(
129
140
  id: DocumentId,
@@ -486,6 +497,20 @@ export abstract class BaseYjsDocumentService<TDoc extends InternalYjsDoc = Inter
486
497
  return Promise.resolve();
487
498
  };
488
499
 
500
+ readonly withTransaction = (
501
+ docRef: DocumentRef,
502
+ fn: () => void,
503
+ description?: EditDescription,
504
+ ): void => {
505
+ const internalDoc = this.documents.get(docRef.id);
506
+ invariant(
507
+ internalDoc != null,
508
+ `Cannot start transaction as document not found: ${docRef.id}`,
509
+ );
510
+
511
+ internalDoc.yDoc.transact(fn, description);
512
+ };
513
+
489
514
  onMetadataChange<T extends DocumentSchema>(
490
515
  docRef: DocumentRef<T>,
491
516
  callback: DocumentMetadataChangeCallback<T>,
@@ -520,6 +545,22 @@ export abstract class BaseYjsDocumentService<TDoc extends InternalYjsDoc = Inter
520
545
  };
521
546
  }
522
547
 
548
+ abstract onActivity<T extends DocumentSchema>(
549
+ docRef: DocumentRef<T>,
550
+ callback: (docRef: DocumentRef<T>, event: ActivityEvent) => void,
551
+ ): Unsubscribe;
552
+
553
+ abstract onPresence<T extends DocumentSchema>(
554
+ docRef: DocumentRef<T>,
555
+ callback: (docRef: DocumentRef<T>, event: PresenceEvent) => void,
556
+ ): Unsubscribe;
557
+
558
+ abstract updateCustomPresence<M extends Model>(
559
+ docRef: DocumentRef,
560
+ model: M,
561
+ eventData: ModelData<M>,
562
+ ): void;
563
+
523
564
  readonly onStateChange = <T extends DocumentSchema>(
524
565
  docRef: DocumentRef<T>,
525
566
  callback: DocumentStateChangeCallback<T>,
@@ -14,13 +14,18 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import type { ModuleConfigTuple, PackAppInternal } from "@palantir/pack.core";
17
+ import type { ModuleConfigTuple, PackAppInternal, Unsubscribe } from "@palantir/pack.core";
18
18
  import { generateId } from "@palantir/pack.core";
19
19
  import type {
20
+ ActivityEvent,
20
21
  DocumentId,
21
22
  DocumentMetadata,
22
23
  DocumentRef,
23
24
  DocumentSchema,
25
+ Model,
26
+ ModelData,
27
+ PresenceEvent,
28
+ PresenceSubscriptionOptions,
24
29
  } from "@palantir/pack.document-schema.model-types";
25
30
  import type * as Y from "yjs";
26
31
  import { createDocumentServiceConfig } from "../DocumentServiceModule.js";
@@ -99,6 +104,36 @@ class InMemoryDocumentService extends BaseYjsDocumentService {
99
104
  return Promise.resolve(docRef);
100
105
  };
101
106
 
107
+ readonly searchDocuments = <T extends DocumentSchema>(
108
+ documentTypeName: string,
109
+ schema: T,
110
+ options?: {
111
+ documentName?: string;
112
+ limit?: number;
113
+ },
114
+ ): Promise<ReadonlyArray<DocumentMetadata & { readonly id: DocumentId }>> => {
115
+ const results: Array<DocumentMetadata & { readonly id: DocumentId }> = [];
116
+ const { documentName, limit } = options ?? {};
117
+
118
+ for (const [docId, internalDoc] of this.documents.entries()) {
119
+ if (internalDoc.metadata?.documentTypeName === documentTypeName) {
120
+ if (documentName && !internalDoc.metadata.name.includes(documentName)) {
121
+ continue;
122
+ }
123
+ results.push({
124
+ ...internalDoc.metadata,
125
+ id: docId as DocumentId,
126
+ });
127
+
128
+ if (limit && results.length >= limit) {
129
+ break;
130
+ }
131
+ }
132
+ }
133
+
134
+ return Promise.resolve(results);
135
+ };
136
+
102
137
  // Lifecycle method implementations
103
138
  protected onMetadataSubscriptionOpened(
104
139
  internalDoc: InternalYjsDoc,
@@ -155,6 +190,28 @@ class InMemoryDocumentService extends BaseYjsDocumentService {
155
190
  ): void {
156
191
  // No cleanup needed for in-memory service
157
192
  }
193
+
194
+ onActivity<T extends DocumentSchema>(
195
+ _docRef: DocumentRef<T>,
196
+ _callback: (docRef: DocumentRef<T>, event: ActivityEvent) => void,
197
+ ): Unsubscribe {
198
+ return () => {};
199
+ }
200
+
201
+ onPresence<T extends DocumentSchema>(
202
+ _docRef: DocumentRef<T>,
203
+ _callback: (docRef: DocumentRef<T>, event: PresenceEvent) => void,
204
+ _options?: PresenceSubscriptionOptions,
205
+ ): Unsubscribe {
206
+ return () => {};
207
+ }
208
+
209
+ updateCustomPresence<M extends Model>(
210
+ _docRef: DocumentRef,
211
+ _model: M,
212
+ _eventData: ModelData<M>,
213
+ ): void {
214
+ }
158
215
  }
159
216
 
160
217
  function generateDocumentId(): DocumentId {
@@ -16,12 +16,17 @@
16
16
 
17
17
  import type { PackAppInternal } from "@palantir/pack.core";
18
18
  import type {
19
+ ActivityEvent,
19
20
  DocumentId,
20
21
  DocumentMetadata,
21
22
  DocumentRef,
22
23
  DocumentSchema,
23
24
  DocumentState,
25
+ EditDescription,
24
26
  Model,
27
+ ModelData,
28
+ PresenceEvent,
29
+ PresenceSubscriptionOptions,
25
30
  RecordCollectionRef,
26
31
  Unsubscribe,
27
32
  } from "@palantir/pack.document-schema.model-types";
@@ -39,8 +44,14 @@ const INVALID_DOC_REF: DocumentRef = Object.freeze(
39
44
  getRecords: () => {
40
45
  throw new Error("Invalid document reference");
41
46
  },
47
+ onActivity: () => () => {},
42
48
  onMetadataChange: () => () => {},
49
+ onPresence: () => () => {},
43
50
  onStateChange: () => () => {},
51
+ updateCustomPresence: () => {},
52
+ withTransaction: () => {
53
+ throw new Error("Invalid document reference");
54
+ },
44
55
  } as const,
45
56
  );
46
57
 
@@ -52,10 +63,30 @@ export const createDocRef = <const D extends DocumentSchema>(
52
63
  return new DocumentRefImpl(app, id, schema);
53
64
  };
54
65
 
66
+ /**
67
+ * Get an invalid document reference. This is a stable reference that can be
68
+ * used to represent a non-existent or invalid document.
69
+ *
70
+ * Not to be confused with a valid reference to a non-existent document, an
71
+ * invalid reference is one that is not properly initialized. For example, code
72
+ * that initializes with an undefined or empty documentId might produce an
73
+ * invalid document reference rather than propagate nullish types.
74
+ *
75
+ * Most operations on an invalid reference are no-ops. For the rest, it is
76
+ * recommended to check for validity using {@link isValidDocRef} before
77
+ * performing operations.
78
+ */
55
79
  export function invalidDocRef<D extends DocumentSchema = DocumentSchema>(): DocumentRef<D> {
56
80
  return INVALID_DOC_REF as DocumentRef<D>;
57
81
  }
58
82
 
83
+ /**
84
+ * Check if a document reference is an invalid reference.
85
+ * Not to be confused with a valid reference to a non-existent document, an invalid reference is one that is not properly initialized.
86
+ * For example, code that initializes with an undefined or empty documentId might produce an invalid document reference rather than
87
+ * propagate nullish types, as most operations on an invalid reference are no-ops. For the rest, it is recommended to check for
88
+ * validity using this function before performing operations.
89
+ */
59
90
  export function isValidDocRef<D extends DocumentSchema = DocumentSchema>(
60
91
  docRef: DocumentRef<D>,
61
92
  ): docRef is DocumentRef<D> {
@@ -84,15 +115,42 @@ class DocumentRefImpl<T extends DocumentSchema> implements DocumentRef<T> {
84
115
  return this.#stateModule.getCreateRecordCollectionRef(this, model);
85
116
  }
86
117
 
118
+ onActivity(
119
+ callback: (docRef: DocumentRef<T>, event: ActivityEvent) => void,
120
+ ): Unsubscribe {
121
+ return this.#stateModule.onActivity(this, callback);
122
+ }
123
+
87
124
  onMetadataChange(
88
125
  cb: (docRef: DocumentRef<T>, metadata: DocumentMetadata) => void,
89
126
  ): Unsubscribe {
90
127
  return this.#stateModule.onMetadataChange(this, cb);
91
128
  }
92
129
 
130
+ onPresence(
131
+ callback: (docRef: DocumentRef<T>, event: PresenceEvent) => void,
132
+ options?: PresenceSubscriptionOptions,
133
+ ): Unsubscribe {
134
+ return this.#stateModule.onPresence(this, callback, options);
135
+ }
136
+
93
137
  onStateChange(
94
138
  callback: (docRef: DocumentRef<T>) => void,
95
139
  ): Unsubscribe {
96
140
  return this.#stateModule.onStateChange(this, callback);
97
141
  }
142
+
143
+ updateCustomPresence<M extends Model = Model>(
144
+ model: M,
145
+ eventData: ModelData<M>,
146
+ ): void {
147
+ this.#stateModule.updateCustomPresence(this, model, eventData);
148
+ }
149
+
150
+ withTransaction(
151
+ fn: () => void,
152
+ description?: EditDescription,
153
+ ): void {
154
+ this.#stateModule.withTransaction(this, fn, description);
155
+ }
98
156
  }
@@ -16,13 +16,17 @@
16
16
 
17
17
  import type { Unsubscribe } from "@palantir/pack.core";
18
18
  import type {
19
+ ActivityEvent,
19
20
  DocumentId,
20
21
  DocumentMetadata,
21
22
  DocumentRef,
22
23
  DocumentSchema,
23
24
  DocumentState,
25
+ EditDescription,
24
26
  Model,
25
27
  ModelData,
28
+ PresenceEvent,
29
+ PresenceSubscriptionOptions,
26
30
  RecordCollectionRef,
27
31
  RecordId,
28
32
  RecordRef,
@@ -83,6 +87,13 @@ export type RecordDeleteCallback<M extends Model = Model> = (
83
87
  record: RecordRef<M>,
84
88
  ) => void;
85
89
 
90
+ /**
91
+ * Base interface for specific document service implementations.
92
+ * The DocumentService is responsible for persisting document state,
93
+ * metadata, and providing methods to subscribe and interact with documents.
94
+ *
95
+ * The main implementation communicates with the Foundry platform (see @palantir/pack.state.foundry).
96
+ */
86
97
  export interface DocumentService {
87
98
  readonly hasMetadataSubscriptions: boolean;
88
99
  readonly hasStateSubscriptions: boolean;
@@ -92,6 +103,15 @@ export interface DocumentService {
92
103
  schema: T,
93
104
  ) => Promise<DocumentRef<T>>;
94
105
 
106
+ readonly searchDocuments: <T extends DocumentSchema>(
107
+ documentTypeName: string,
108
+ schema: T,
109
+ options?: {
110
+ documentName?: string;
111
+ limit?: number;
112
+ },
113
+ ) => Promise<ReadonlyArray<DocumentMetadata & { readonly id: DocumentId }>>;
114
+
95
115
  readonly createDocRef: <const T extends DocumentSchema>(
96
116
  id: DocumentId,
97
117
  schema: T,
@@ -126,6 +146,12 @@ export interface DocumentService {
126
146
  partialState: Partial<ModelData<R>>,
127
147
  ) => Promise<void>;
128
148
 
149
+ readonly withTransaction: (
150
+ docRef: DocumentRef,
151
+ fn: () => void,
152
+ description?: EditDescription,
153
+ ) => void;
154
+
129
155
  // Collection methods
130
156
  readonly getRecord: <M extends Model>(
131
157
  collection: RecordCollectionRef<M>,
@@ -170,16 +196,33 @@ export interface DocumentService {
170
196
  callback: RecordCollectionChangeCallback<M>,
171
197
  ) => Unsubscribe;
172
198
 
199
+ readonly onActivity: <T extends DocumentSchema>(
200
+ docRef: DocumentRef<T>,
201
+ callback: (docRef: DocumentRef<T>, event: ActivityEvent) => void,
202
+ ) => Unsubscribe;
203
+
173
204
  readonly onMetadataChange: <T extends DocumentSchema>(
174
205
  docRef: DocumentRef<T>,
175
206
  callback: DocumentMetadataChangeCallback<T>,
176
207
  ) => Unsubscribe;
177
208
 
209
+ readonly onPresence: <T extends DocumentSchema>(
210
+ docRef: DocumentRef<T>,
211
+ callback: (docRef: DocumentRef<T>, event: PresenceEvent) => void,
212
+ options?: PresenceSubscriptionOptions,
213
+ ) => Unsubscribe;
214
+
178
215
  readonly onStateChange: <T extends DocumentSchema>(
179
216
  docRef: DocumentRef<T>,
180
217
  callback: DocumentStateChangeCallback<T>,
181
218
  ) => Unsubscribe;
182
219
 
220
+ readonly updateCustomPresence: <M extends Model>(
221
+ docRef: DocumentRef,
222
+ model: M,
223
+ eventData: ModelData<M>,
224
+ ) => void;
225
+
183
226
  readonly onRecordChanged: <M extends Model>(
184
227
  record: RecordRef<M>,
185
228
  callback: RecordChangeCallback<M>,
@@ -54,10 +54,33 @@ export const createRecordCollectionRef = <const M extends Model>(
54
54
  return new RecordCollectionRefImpl(documentService, docRef, model);
55
55
  };
56
56
 
57
+ /**
58
+ * Get an invalid record collection reference. This is a stable reference that
59
+ * can be used to represent an invalid record collection.
60
+ *
61
+ * Not to be confused with a valid reference to a non-existent record
62
+ * collection, an invalid reference is one that is not properly initialized. For
63
+ * example, code that initializes with an undefined or empty model might produce
64
+ * an invalid record collection reference rather than propagate nullish types.
65
+ *
66
+ * Most operations on an invalid reference are no-ops. For the rest, it is
67
+ * recommended to check for validity using {@link isValidRecordCollectionRef}
68
+ * before performing operations.
69
+ */
57
70
  export function invalidRecordCollectionRef<M extends Model = Model>(): RecordCollectionRef<M> {
58
71
  return INVALID_RECORD_COLLECTION_REF as RecordCollectionRef<M>;
59
72
  }
60
73
 
74
+ /**
75
+ * Check if a record collection reference is a valid reference.
76
+ *
77
+ * Not to be confused with a valid reference to a non-existent record
78
+ * collection, an invalid reference is one that is not properly initialized.
79
+ *
80
+ * Most operations on an invalid reference are no-ops. For the rest, it is
81
+ * recommended to check for validity using this function before performing
82
+ * operations.
83
+ */
61
84
  export function isValidRecordCollectionRef<M extends Model = Model>(
62
85
  collectionRef: RecordCollectionRef<M>,
63
86
  ): collectionRef is RecordCollectionRef<M> {
@@ -49,10 +49,35 @@ export const createRecordRef = <const M extends Model>(
49
49
  return new RecordRefImpl(documentService, docRef, id, model);
50
50
  };
51
51
 
52
+ /**
53
+ * Get an invalid record reference. This is a stable reference that can be used
54
+ * to represent an invalid record.
55
+ *
56
+ * Not to be confused with a valid reference to a non-existent record, an
57
+ * invalid reference is one that is not properly initialized. For example, code
58
+ * that initializes with an undefined or empty recordId might produce an
59
+ * invalid record reference rather than propagate nullish types.
60
+ *
61
+ * Most operations on an invalid reference are no-ops. For the rest, it is
62
+ * recommended to check for validity using {@link isValidRecordRef} before
63
+ * performing operations.
64
+ */
52
65
  export function invalidRecordRef<M extends Model = Model>(): RecordRef<M> {
53
66
  return INVALID_RECORD_REF as RecordRef<M>;
54
67
  }
55
68
 
69
+ /**
70
+ * Check if a record reference is a valid reference.
71
+ *
72
+ * Not to be confused with a valid reference to a non-existent record, an
73
+ * invalid reference is one that is not properly initialized.
74
+ *
75
+ * For example, code that initializes with an undefined or empty recordId might
76
+ * produce an invalid record reference rather than propagate nullish types, as
77
+ * most operations on an invalid reference are no-ops. For the rest, it is
78
+ * recommended to check for validity using this function before performing
79
+ * operations.
80
+ */
56
81
  export function isValidRecordRef<M extends Model = Model>(
57
82
  recordRef: RecordRef<M>,
58
83
  ): recordRef is RecordRef<M> {
@@ -21,13 +21,17 @@ import {
21
21
  type Unsubscribe,
22
22
  } from "@palantir/pack.core";
23
23
  import type {
24
+ ActivityEvent,
24
25
  DocumentId,
25
26
  DocumentMetadata,
26
27
  DocumentRef,
27
28
  DocumentSchema,
28
29
  DocumentState,
30
+ EditDescription,
29
31
  Model,
30
32
  ModelData,
33
+ PresenceEvent,
34
+ PresenceSubscriptionOptions,
31
35
  RecordCollectionRef,
32
36
  RecordId,
33
37
  RecordRef,
@@ -69,20 +73,46 @@ export interface StateModule {
69
73
  schema: T,
70
74
  ) => Promise<DocumentRef<T>>;
71
75
 
76
+ readonly searchDocuments: <T extends DocumentSchema>(
77
+ documentTypeName: string,
78
+ schema: T,
79
+ options?: {
80
+ documentName?: string;
81
+ limit?: number;
82
+ },
83
+ ) => Promise<ReadonlyArray<DocumentMetadata & { readonly id: DocumentId }>>;
84
+
72
85
  readonly getDocumentSnapshot: <T extends DocumentSchema>(
73
86
  docRef: DocumentRef<T>,
74
87
  ) => Promise<DocumentState<T>>;
75
88
 
89
+ readonly onActivity: <T extends DocumentSchema>(
90
+ docRef: DocumentRef<T>,
91
+ callback: (docRef: DocumentRef<T>, event: ActivityEvent) => void,
92
+ ) => Unsubscribe;
93
+
76
94
  readonly onMetadataChange: <T extends DocumentSchema>(
77
95
  docRef: DocumentRef<T>,
78
96
  cb: (docRef: DocumentRef<T>, metadata: DocumentMetadata) => void,
79
97
  ) => Unsubscribe;
80
98
 
99
+ readonly onPresence: <T extends DocumentSchema>(
100
+ docRef: DocumentRef<T>,
101
+ callback: (docRef: DocumentRef<T>, event: PresenceEvent) => void,
102
+ options?: PresenceSubscriptionOptions,
103
+ ) => Unsubscribe;
104
+
81
105
  readonly onStateChange: <T extends DocumentSchema>(
82
106
  docRef: DocumentRef<T>,
83
107
  cb: (docRef: DocumentRef<T>) => void,
84
108
  ) => Unsubscribe;
85
109
 
110
+ readonly updateCustomPresence: <M extends Model>(
111
+ docRef: DocumentRef,
112
+ model: M,
113
+ eventData: ModelData<M>,
114
+ ) => void;
115
+
86
116
  readonly getRecordSnapshot: <R extends Model>(
87
117
  recordRef: RecordRef<R>,
88
118
  ) => Promise<ModelData<R>>;
@@ -112,6 +142,12 @@ export interface StateModule {
112
142
  partialState: Partial<ModelData<R>>,
113
143
  ) => Promise<void>;
114
144
 
145
+ readonly withTransaction: (
146
+ docRef: DocumentRef,
147
+ fn: () => void,
148
+ description?: EditDescription,
149
+ ) => void;
150
+
115
151
  readonly onRecordChanged: <M extends Model>(
116
152
  record: RecordRef<M>,
117
153
  callback: RecordChangeCallback<M>,
@@ -172,12 +208,30 @@ export class StateModuleImpl implements StateModule {
172
208
  return this.documentService.createDocument(metadata, schema);
173
209
  }
174
210
 
211
+ async searchDocuments<T extends DocumentSchema>(
212
+ documentTypeName: string,
213
+ schema: T,
214
+ options?: {
215
+ documentName?: string;
216
+ limit?: number;
217
+ },
218
+ ): Promise<ReadonlyArray<DocumentMetadata & { readonly id: DocumentId }>> {
219
+ return this.documentService.searchDocuments(documentTypeName, schema, options);
220
+ }
221
+
175
222
  async getDocumentSnapshot<T extends DocumentSchema>(
176
223
  docRef: DocumentRef<T>,
177
224
  ): Promise<DocumentState<T>> {
178
225
  return this.documentService.getDocumentSnapshot(docRef);
179
226
  }
180
227
 
228
+ onActivity<T extends DocumentSchema>(
229
+ docRef: DocumentRef<T>,
230
+ callback: (docRef: DocumentRef<T>, event: ActivityEvent) => void,
231
+ ): Unsubscribe {
232
+ return this.documentService.onActivity(docRef, callback);
233
+ }
234
+
181
235
  onMetadataChange<T extends DocumentSchema>(
182
236
  docRef: DocumentRef<T>,
183
237
  cb: (doc: DocumentRef<T>, metadata: DocumentMetadata) => void,
@@ -185,6 +239,14 @@ export class StateModuleImpl implements StateModule {
185
239
  return this.documentService.onMetadataChange(docRef, cb);
186
240
  }
187
241
 
242
+ onPresence<T extends DocumentSchema>(
243
+ docRef: DocumentRef<T>,
244
+ callback: (docRef: DocumentRef<T>, event: PresenceEvent) => void,
245
+ options?: PresenceSubscriptionOptions,
246
+ ): Unsubscribe {
247
+ return this.documentService.onPresence(docRef, callback, options);
248
+ }
249
+
188
250
  onStateChange<T extends DocumentSchema>(
189
251
  docRef: DocumentRef<T>,
190
252
  cb: (docRef: DocumentRef<T>) => void,
@@ -192,6 +254,14 @@ export class StateModuleImpl implements StateModule {
192
254
  return this.documentService.onStateChange(docRef, cb);
193
255
  }
194
256
 
257
+ updateCustomPresence<M extends Model>(
258
+ docRef: DocumentRef,
259
+ model: M,
260
+ eventData: ModelData<M>,
261
+ ): void {
262
+ this.documentService.updateCustomPresence(docRef, model, eventData);
263
+ }
264
+
195
265
  async getRecordSnapshot<R extends Model>(
196
266
  recordRef: RecordRef<R>,
197
267
  ): Promise<ModelData<R>> {
@@ -212,6 +282,14 @@ export class StateModuleImpl implements StateModule {
212
282
  return this.documentService.updateRecord(recordRef, partialState);
213
283
  }
214
284
 
285
+ withTransaction(
286
+ docRef: DocumentRef,
287
+ fn: () => void,
288
+ description?: EditDescription,
289
+ ): void {
290
+ this.documentService.withTransaction(docRef, fn, description);
291
+ }
292
+
215
293
  // Collection methods
216
294
  getCreateRecordCollectionRef<M extends Model>(
217
295
  docRef: DocumentRef,