@enbox/api 0.2.3 → 0.2.4

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 (73) hide show
  1. package/README.md +235 -35
  2. package/dist/browser.mjs +13 -13
  3. package/dist/browser.mjs.map +4 -4
  4. package/dist/esm/dwn-api.js +24 -10
  5. package/dist/esm/dwn-api.js.map +1 -1
  6. package/dist/esm/index.js +6 -0
  7. package/dist/esm/index.js.map +1 -1
  8. package/dist/esm/live-query.js +34 -5
  9. package/dist/esm/live-query.js.map +1 -1
  10. package/dist/esm/permission-grant.js +3 -6
  11. package/dist/esm/permission-grant.js.map +1 -1
  12. package/dist/esm/permission-request.js +4 -7
  13. package/dist/esm/permission-request.js.map +1 -1
  14. package/dist/esm/record-data.js +131 -0
  15. package/dist/esm/record-data.js.map +1 -0
  16. package/dist/esm/record-types.js +9 -0
  17. package/dist/esm/record-types.js.map +1 -0
  18. package/dist/esm/record.js +58 -184
  19. package/dist/esm/record.js.map +1 -1
  20. package/dist/esm/repository-types.js +13 -0
  21. package/dist/esm/repository-types.js.map +1 -0
  22. package/dist/esm/repository.js +347 -0
  23. package/dist/esm/repository.js.map +1 -0
  24. package/dist/esm/typed-live-query.js +101 -0
  25. package/dist/esm/typed-live-query.js.map +1 -0
  26. package/dist/esm/typed-record.js +227 -0
  27. package/dist/esm/typed-record.js.map +1 -0
  28. package/dist/esm/typed-web5.js +134 -23
  29. package/dist/esm/typed-web5.js.map +1 -1
  30. package/dist/esm/web5.js +78 -20
  31. package/dist/esm/web5.js.map +1 -1
  32. package/dist/types/dwn-api.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +6 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/live-query.d.ts +43 -4
  36. package/dist/types/live-query.d.ts.map +1 -1
  37. package/dist/types/permission-grant.d.ts +1 -1
  38. package/dist/types/permission-grant.d.ts.map +1 -1
  39. package/dist/types/permission-request.d.ts +1 -1
  40. package/dist/types/permission-request.d.ts.map +1 -1
  41. package/dist/types/record-data.d.ts +49 -0
  42. package/dist/types/record-data.d.ts.map +1 -0
  43. package/dist/types/record-types.d.ts +145 -0
  44. package/dist/types/record-types.d.ts.map +1 -0
  45. package/dist/types/record.d.ts +13 -144
  46. package/dist/types/record.d.ts.map +1 -1
  47. package/dist/types/repository-types.d.ts +137 -0
  48. package/dist/types/repository-types.d.ts.map +1 -0
  49. package/dist/types/repository.d.ts +59 -0
  50. package/dist/types/repository.d.ts.map +1 -0
  51. package/dist/types/typed-live-query.d.ts +86 -0
  52. package/dist/types/typed-live-query.d.ts.map +1 -0
  53. package/dist/types/typed-record.d.ts +179 -0
  54. package/dist/types/typed-record.d.ts.map +1 -0
  55. package/dist/types/typed-web5.d.ts +55 -24
  56. package/dist/types/typed-web5.d.ts.map +1 -1
  57. package/dist/types/web5.d.ts +47 -2
  58. package/dist/types/web5.d.ts.map +1 -1
  59. package/package.json +8 -7
  60. package/src/dwn-api.ts +30 -13
  61. package/src/index.ts +6 -0
  62. package/src/live-query.ts +71 -7
  63. package/src/permission-grant.ts +2 -3
  64. package/src/permission-request.ts +3 -4
  65. package/src/record-data.ts +155 -0
  66. package/src/record-types.ts +188 -0
  67. package/src/record.ts +86 -389
  68. package/src/repository-types.ts +249 -0
  69. package/src/repository.ts +391 -0
  70. package/src/typed-live-query.ts +156 -0
  71. package/src/typed-record.ts +309 -0
  72. package/src/typed-web5.ts +202 -49
  73. package/src/web5.ts +150 -23
package/src/record.ts CHANGED
@@ -17,6 +17,13 @@ import type {
17
17
  Web5Agent,
18
18
  } from '@enbox/agent';
19
19
 
20
+ import type {
21
+ RecordDeleteParams,
22
+ RecordModel,
23
+ RecordOptions,
24
+ RecordUpdateParams,
25
+ } from './record-types.js';
26
+
20
27
  import {
21
28
  AgentPermissionsApi,
22
29
  DwnInterface,
@@ -27,181 +34,23 @@ import {
27
34
  } from '@enbox/agent';
28
35
  import { Convert, isEmptyObject, removeUndefinedProperties, Stream } from '@enbox/common';
29
36
 
30
- import { dataToBlob, SendCache } from './utils.js';
31
-
32
- /**
33
- * Represents Immutable Record properties that cannot be changed after the record is created.
34
- *
35
- * @beta
36
- * */
37
- export type ImmutableRecordProperties =
38
- Pick<DwnMessageDescriptor[DwnInterface.RecordsWrite], 'dateCreated' | 'parentId' | 'protocol' | 'protocolPath' | 'recipient' | 'schema'>;
39
-
40
- /**
41
- * Represents Optional Record properties that depend on the Record's current state.
42
- *
43
- * @beta
44
- */
45
- export type OptionalRecordProperties =
46
- Pick<DwnMessage[DwnInterface.RecordsWrite], 'authorization' | 'attestation' | 'encryption' | 'contextId' > &
47
- Pick<DwnMessageDescriptor[DwnInterface.RecordsWrite], 'dataFormat' | 'dataCid' | 'dataSize' | 'datePublished' | 'published' | 'tags'>;
48
-
49
- /**
50
- * Represents the structured data model of a record, encapsulating the essential fields that define
51
- * the record's metadata and payload within a Decentralized Web Node (DWN).
52
- *
53
- * @beta
54
- */
55
- export type RecordModel = ImmutableRecordProperties & OptionalRecordProperties & {
56
-
57
- /** The logical author of the record. */
58
- author: string;
59
-
60
- /** The unique identifier of the record. */
61
- recordId?: string;
62
-
63
- /** The message timestamp (time of creation, most recent update, or deletion). */
64
- timestamp?: string;
65
-
66
- /** The protocol role under which this record is written. */
67
- protocolRole?: RecordOptions['protocolRole'];
68
- };
69
-
70
- /**
71
- * Options for configuring a {@link Record} instance, extending the base `RecordsWriteMessage` with
72
- * additional properties.
73
- *
74
- * This type combines the standard fields required for writing DWN records with additional metadata
75
- * and configuration options used specifically in the {@link Record} class.
76
- *
77
- * @beta
78
- */
79
- export type RecordOptions = DwnMessage[DwnInterface.RecordsWrite | DwnInterface.RecordsDelete] & {
80
- /** The DID that signed the record. */
81
- author: string;
82
-
83
- /** The attestation signature(s) for the record. */
84
- attestation?: DwnMessage[DwnInterface.RecordsWrite]['attestation'];
85
-
86
- /** The encryption information for the record. */
87
- encryption?: DwnMessage[DwnInterface.RecordsWrite]['encryption'];
88
-
89
- /** The contextId associated with the record. */
90
- contextId?: string;
91
-
92
- /** The unique identifier of the record */
93
- recordId?: string;
94
-
95
- /** The DID of the DWN tenant under which record operations are being performed. */
96
- connectedDid: string;
97
-
98
- /** The optional DID that will sign the records on behalf of the connectedDid */
99
- delegateDid?: string;
100
-
101
- /** The data of the record, either as a Base64 URL encoded string or a Blob. */
102
- encodedData?: string | Blob;
103
-
104
- /**
105
- * A stream of data, conforming to the Web `ReadableStream` interface, providing a mechanism
106
- * to read the record's data sequentially. This is particularly useful for handling large
107
- * datasets that should not be loaded entirely in memory, allowing for efficient, chunked
108
- * processing of the record's data.
109
- *
110
- * The DWN SDK now returns Web `ReadableStream` natively, so no conversion is needed.
111
- */
112
- data?: ReadableStream;
113
-
114
- /** The initial `RecordsWriteMessage` that represents the initial state/version of the record. */
115
- initialWrite?: DwnMessage[DwnInterface.RecordsWrite];
116
-
117
- /** The protocol role under which this record is written. */
118
- protocolRole?: string;
119
-
120
- /** The remote tenant DID if the record was queried or read from a remote DWN. */
121
- remoteOrigin?: string;
122
- };
123
-
124
- /**
125
- * Parameters for updating a DWN record.
126
- *
127
- * This type specifies the set of properties that can be updated on an existing record. It is used
128
- * to convey the new state or changes to be applied to the record.
129
- *
130
- * @beta
131
- */
132
- export type RecordUpdateParams = {
133
- /**
134
- * The new data for the record, which can be of any type. This data will replace the existing
135
- * data of the record. It's essential to ensure that this data is compatible with the record's
136
- * schema or data format expectations.
137
- */
138
- data?: unknown;
139
-
140
- /**
141
- * The Content Identifier (CID) of the data. Updating this value changes the reference to the data
142
- * associated with the record.
143
- */
144
- dataCid?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['dataCid'];
145
-
146
- /** Whether or not to store the updated message. */
147
- store?: boolean;
148
-
149
- /** The data format/MIME type of the supplied data */
150
- dataFormat?: string;
37
+ import type { RecordData } from './record-data.js';
151
38
 
152
- /** The size of the data in bytes. */
153
- dataSize?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['dataSize'];
154
-
155
- /** The timestamp of the update message. */
156
- timestamp?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['messageTimestamp'];
157
-
158
- /** The timestamp indicating when the record was published. */
159
- datePublished?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['datePublished'];
160
-
161
- /** The protocol role under which this record is written. */
162
- protocolRole?: RecordOptions['protocolRole'];
163
-
164
- /** The published status of the record. */
165
- published?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['published'];
166
-
167
-
168
- /** The tags associated with the updated record */
169
- tags?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['tags'];
170
-
171
- /**
172
- * Controls whether the updated record should be auto-encrypted.
173
- *
174
- * If omitted, auto-detected from the original record: if the record was
175
- * originally encrypted, the update is automatically re-encrypted with a
176
- * fresh DEK. Set to `false` explicitly to skip encryption on the update.
177
- */
178
- encryption?: boolean;
179
- };
180
-
181
- /**
182
- * Parameters for deleting a DWN record.
183
- *
184
- * This type specifies the set of properties that are used when deleting an existing record. It is used
185
- * to convey the new state or changes to be applied to the record.
186
- *
187
- * @beta
188
- */
189
- export type RecordDeleteParams = {
190
- /** Whether or not to store the message. */
191
- store?: boolean;
192
-
193
- /** Whether or not to sign the delete as an owner in order to import it. */
194
- signAsOwner?: boolean;
195
-
196
- /** Whether or not to prune any children this record may have. */
197
- prune?: DwnMessageDescriptor[DwnInterface.RecordsDelete]['prune'];
39
+ import { createRecordData } from './record-data.js';
40
+ import { dataToBlob, SendCache } from './utils.js';
198
41
 
199
- /** The timestamp of the delete message. */
200
- timestamp?: DwnMessageDescriptor[DwnInterface.RecordsDelete]['messageTimestamp'];
42
+ // Re-export types for backward compatibility consumers that import from
43
+ // `./record.js` will continue to resolve every type without changes.
44
+ export type {
45
+ ImmutableRecordProperties,
46
+ OptionalRecordProperties,
47
+ RecordDeleteParams,
48
+ RecordModel,
49
+ RecordOptions,
50
+ RecordUpdateParams,
51
+ } from './record-types.js';
201
52
 
202
- /** The protocol role under which this record will be deleted. */
203
- protocolRole?: string;
204
- };
53
+ export type { RecordData } from './record-data.js';
205
54
 
206
55
  /**
207
56
  * The result of a {@link Record.update} operation.
@@ -472,147 +321,37 @@ export class Record implements RecordModel {
472
321
  *
473
322
  * @beta
474
323
  */
475
- get data(): {
476
- blob: () => Promise<Blob>;
477
- bytes: () => Promise<Uint8Array>;
478
- json: <T = unknown>() => Promise<T>;
479
- text: () => Promise<string>;
480
- stream: () => Promise<ReadableStream>;
481
- then: (
482
- onFulfilled?: (value: ReadableStream) => ReadableStream | PromiseLike<ReadableStream>,
483
- onRejected?: (reason: any) => PromiseLike<never>,
484
- ) => Promise<ReadableStream>;
485
- catch: (onRejected?: (reason: any) => PromiseLike<never>) => Promise<ReadableStream>;
486
- } {
487
- const self = this; // Capture the context of the `Record` instance.
488
- const dataObj = {
489
-
490
- /**
491
- * Returns the data of the current record as a `Blob`.
492
- *
493
- * @returns A promise that resolves to a Blob containing the record's data.
494
- * @throws If the record data is not available or cannot be converted to a `Blob`.
495
- *
496
- * @beta
497
- */
498
- async blob(): Promise<Blob> {
499
- return new Blob([await Stream.consumeToBytes({ readableStream: await this.stream() })], { type: self.dataFormat });
500
- },
501
-
502
- /**
503
- * Returns the data of the current record as a `Uint8Array`.
504
- *
505
- * @returns A Promise that resolves to a `Uint8Array` containing the record's data bytes.
506
- * @throws If the record data is not available or cannot be converted to a byte array.
507
- *
508
- * @beta
509
- */
510
- async bytes(): Promise<Uint8Array> {
511
- return await Stream.consumeToBytes({ readableStream: await this.stream() });
512
- },
513
-
514
- /**
515
- * Parses the data of the current record as JSON and returns it as a JavaScript object.
516
- *
517
- * @returns A Promise that resolves to a JavaScript object parsed from the record's JSON data.
518
- * @throws If the record data is not available, not in JSON format, or cannot be parsed.
519
- *
520
- * @beta
521
- */
522
- async json<T = unknown>(): Promise<T> {
523
- return await Stream.consumeToJson({ readableStream: await this.stream() }) as T;
524
- },
525
-
526
- /**
527
- * Returns the data of the current record as a `string`.
528
- *
529
- * @returns A promise that resolves to a `string` containing the record's text data.
530
- * @throws If the record data is not available or cannot be converted to text.
531
- *
532
- * @beta
533
- */
534
- async text(): Promise<string> {
535
- return await Stream.consumeToText({ readableStream: await this.stream() });
536
- },
537
-
538
- /**
539
- * Provides a Web `ReadableStream` containing the record's data.
540
- *
541
- * Uses the standard Web Streams API for cross-platform compatibility across
542
- * browsers, Node.js, Bun, and Deno.
543
- *
544
- * @returns A promise that resolves to a Web `ReadableStream` of the record's data.
545
- * @throws If the record data is not available in-memory and cannot be fetched.
546
- *
547
- * @beta
548
- */
549
- async stream(): Promise<ReadableStream> {
550
- if (self.deleted) {
551
- throw new Error('404: Not Found');
552
- }
553
-
554
- if (self._encodedData) {
555
- /** If `encodedData` is set, it indicates that the Record was instantiated by
556
- * `dwn.records.create()`/`dwn.records.write()` or the record's data payload was small
557
- * enough to be returned in `dwn.records.query()` results. In either case, the data is
558
- * already available in-memory and can be returned as a Web `ReadableStream`. */
559
- return Stream.fromBlob(self._encodedData);
560
-
561
- } else if (self._readableStream) {
562
- /** If a data stream is available, return it and clear the reference so subsequent
563
- * calls will re-fetch. Unlike Node Readable streams, a consumed Web ReadableStream
564
- * still appears "readable" (unlocked), so we cannot rely on `isReadable()` to
565
- * detect exhaustion. Clearing the reference ensures the next call re-fetches. */
566
- const currentStream = self._readableStream;
567
- self._readableStream = undefined;
568
- return currentStream;
569
-
570
- } else {
571
- /** The data stream has been consumed or was never set. Re-fetch from either: */
572
- return self._remoteOrigin ?
573
- // A. ...a remote DWN if the record was originally queried from a remote DWN.
574
- await self.readRecordData({ target: self._remoteOrigin, isRemote: true }) :
575
- // B. ...a local DWN if the record was originally queried from the local DWN.
576
- await self.readRecordData({ target: self._connectedDid, isRemote: false });
577
- }
578
- },
579
-
580
- /**
581
- * Attaches callbacks for the resolution and/or rejection of the `Promise` returned by
582
- * `stream()`.
583
- *
584
- * This method is a proxy to the `then` method of the `Promise` returned by `stream()`,
585
- * allowing for a seamless integration with promise-based workflows.
586
- * @param onFulfilled - A function to asynchronously execute when the `stream()` promise
587
- * becomes fulfilled.
588
- * @param onRejected - A function to asynchronously execute when the `stream()` promise
589
- * becomes rejected.
590
- * @returns A `Promise` for the completion of which ever callback is executed.
591
- */
592
- then(
593
- onFulfilled?: (value: ReadableStream) => ReadableStream | PromiseLike<ReadableStream>,
594
- onRejected?: (reason: any) => PromiseLike<never>,
595
- ): Promise<ReadableStream> {
596
- return this.stream().then(onFulfilled, onRejected);
597
- },
598
-
599
- /**
600
- * Attaches a rejection handler callback to the `Promise` returned by the `stream()` method.
601
- * This method is a shorthand for `.then(undefined, onRejected)`, specifically designed for handling
602
- * rejection cases in the promise chain initiated by accessing the record's data. It ensures that
603
- * errors during data retrieval or processing can be caught and handled appropriately.
604
- *
605
- * @param onRejected - A function to asynchronously execute when the `stream()` promise
606
- * becomes rejected.
607
- * @returns A `Promise` that resolves to the value of the callback if it is called, or to its
608
- * original fulfillment value if the promise is instead fulfilled.
609
- */
610
- catch(onRejected?: (reason: any) => PromiseLike<never>): Promise<ReadableStream> {
611
- return this.stream().catch(onRejected);
324
+ get data(): RecordData {
325
+ return createRecordData(async (): Promise<ReadableStream> => {
326
+ if (this.deleted) {
327
+ throw new Error('404: Not Found');
612
328
  }
613
- };
614
329
 
615
- return dataObj;
330
+ if (this._encodedData) {
331
+ /** If `encodedData` is set, it indicates that the Record was instantiated by
332
+ * `dwn.records.create()`/`dwn.records.write()` or the record's data payload was small
333
+ * enough to be returned in `dwn.records.query()` results. In either case, the data is
334
+ * already available in-memory and can be returned as a Web `ReadableStream`. */
335
+ return Stream.fromBlob(this._encodedData);
336
+
337
+ } else if (this._readableStream) {
338
+ /** If a data stream is available, return it and clear the reference so subsequent
339
+ * calls will re-fetch. Unlike Node Readable streams, a consumed Web ReadableStream
340
+ * still appears "readable" (unlocked), so we cannot rely on `isReadable()` to
341
+ * detect exhaustion. Clearing the reference ensures the next call re-fetches. */
342
+ const currentStream = this._readableStream;
343
+ this._readableStream = undefined;
344
+ return currentStream;
345
+
346
+ } else {
347
+ /** The data stream has been consumed or was never set. Re-fetch from either: */
348
+ return this._remoteOrigin ?
349
+ // A. ...a remote DWN if the record was originally queried from a remote DWN.
350
+ await this.readRecordData({ target: this._remoteOrigin, isRemote: true }) :
351
+ // B. ...a local DWN if the record was originally queried from the local DWN.
352
+ await this.readRecordData({ target: this._connectedDid, isRemote: false });
353
+ }
354
+ }, this.dataFormat);
616
355
  }
617
356
 
618
357
  /**
@@ -835,18 +574,7 @@ export class Record implements RecordModel {
835
574
  encryption : shouldEncrypt || undefined,
836
575
  };
837
576
 
838
- if (this._delegateDid) {
839
- const { message: delegatedGrant } = await this._permissionsApi.getPermissionForRequest({
840
- connectedDid : this._connectedDid,
841
- delegateDid : this._delegateDid,
842
- protocol : this.protocol,
843
- delegate : true,
844
- cached : true,
845
- messageType : requestOptions.messageType
846
- });
847
- requestOptions.messageParams.delegatedGrant = delegatedGrant;
848
- requestOptions.granteeDid = this._delegateDid;
849
- }
577
+ await this.applyDelegateGrant(requestOptions);
850
578
 
851
579
  const agentResponse = await this._agent.processDwnRequest(requestOptions);
852
580
 
@@ -925,23 +653,7 @@ export class Record implements RecordModel {
925
653
  };
926
654
  }
927
655
 
928
- if (this._delegateDid) {
929
- const { message: delegatedGrant } = await this._permissionsApi.getPermissionForRequest({
930
- connectedDid : this._connectedDid,
931
- delegateDid : this._delegateDid,
932
- protocol : this.protocol,
933
- delegate : true,
934
- cached : true,
935
- messageType : deleteOptions.messageType
936
- });
937
-
938
- deleteOptions.messageParams = {
939
- ...deleteOptions.messageParams,
940
- delegatedGrant
941
- };
942
-
943
- deleteOptions.granteeDid = this._delegateDid;
944
- }
656
+ await this.applyDelegateGrant(deleteOptions);
945
657
 
946
658
  const agentResponse = await this._agent.processDwnRequest(deleteOptions);
947
659
  const { message, reply: { status } } = agentResponse;
@@ -984,23 +696,7 @@ export class Record implements RecordModel {
984
696
  store,
985
697
  };
986
698
 
987
- if (this._delegateDid) {
988
- const { message: delegatedGrant } = await this._permissionsApi.getPermissionForRequest({
989
- connectedDid : this._connectedDid,
990
- delegateDid : this._delegateDid,
991
- protocol : this.protocol,
992
- delegate : true,
993
- cached : true,
994
- messageType : initialWriteRequest.messageType
995
- });
996
-
997
- initialWriteRequest.messageParams = {
998
- ...initialWriteRequest.messageParams,
999
- delegatedGrant
1000
- };
1001
-
1002
- initialWriteRequest.granteeDid = this._delegateDid;
1003
- }
699
+ await this.applyDelegateGrant(initialWriteRequest);
1004
700
 
1005
701
  // Process the prepared initial write, with the options set for storing and/or signing as the owner.
1006
702
  const agentResponse = await this._agent.processDwnRequest(initialWriteRequest);
@@ -1054,23 +750,7 @@ export class Record implements RecordModel {
1054
750
  };
1055
751
  }
1056
752
 
1057
- if (this._delegateDid) {
1058
- const { message: delegatedGrant } = await this._permissionsApi.getPermissionForRequest({
1059
- connectedDid : this._connectedDid,
1060
- delegateDid : this._delegateDid,
1061
- protocol : this.protocol,
1062
- delegate : true,
1063
- cached : true,
1064
- messageType : requestOptions.messageType
1065
- });
1066
-
1067
- requestOptions.messageParams = {
1068
- ...requestOptions.messageParams,
1069
- delegatedGrant
1070
- };
1071
-
1072
- requestOptions.granteeDid = this._delegateDid;
1073
- }
753
+ await this.applyDelegateGrant(requestOptions);
1074
754
 
1075
755
  const agentResponse = await this._agent.processDwnRequest(requestOptions);
1076
756
  const { message, reply: { status } } = agentResponse;
@@ -1122,21 +802,7 @@ export class Record implements RecordModel {
1122
802
  // NOTE: For anonymous/public record data access, callers can use `ReadOnlyRecord` via `Web5.anonymous()`.
1123
803
  // See: https://github.com/enboxorg/enbox/issues/898
1124
804
  try {
1125
- const { message: delegatedGrant } = await this._permissionsApi.getPermissionForRequest({
1126
- connectedDid : this._connectedDid,
1127
- delegateDid : this._delegateDid,
1128
- protocol : this.protocol,
1129
- delegate : true,
1130
- cached : true,
1131
- messageType : readRequest.messageType
1132
- });
1133
-
1134
- readRequest.messageParams = {
1135
- ...readRequest.messageParams,
1136
- delegatedGrant
1137
- };
1138
-
1139
- readRequest.granteeDid = this._delegateDid;
805
+ await this.applyDelegateGrant(readRequest);
1140
806
  } catch {
1141
807
  // If there is an error fetching the grant, we will attempt to read the data as the delegate.
1142
808
  readRequest.author = this._delegateDid;
@@ -1161,6 +827,37 @@ export class Record implements RecordModel {
1161
827
  }
1162
828
  }
1163
829
 
830
+ /**
831
+ * If the record is operating as a delegate, fetches the appropriate permission grant
832
+ * and applies it to the given DWN request options. This centralises the repeated
833
+ * pattern of looking up a delegated grant and attaching it to a request.
834
+ *
835
+ * @param requestOptions - The DWN request options to augment with the delegate grant.
836
+ */
837
+ private async applyDelegateGrant<T extends DwnInterface>(
838
+ requestOptions: ProcessDwnRequest<T>,
839
+ ): Promise<void> {
840
+ if (!this._delegateDid) {
841
+ return;
842
+ }
843
+
844
+ const { message: delegatedGrant } = await this._permissionsApi.getPermissionForRequest({
845
+ connectedDid : this._connectedDid,
846
+ delegateDid : this._delegateDid,
847
+ protocol : this.protocol,
848
+ delegate : true,
849
+ cached : true,
850
+ messageType : requestOptions.messageType
851
+ });
852
+
853
+ requestOptions.messageParams = {
854
+ ...requestOptions.messageParams,
855
+ delegatedGrant
856
+ };
857
+
858
+ requestOptions.granteeDid = this._delegateDid;
859
+ }
860
+
1164
861
  /**
1165
862
  * Verifies if the properties to be mutated are mutable.
1166
863
  *