Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+529
View File
@@ -0,0 +1,529 @@
# Web5 JS SDK
[![NPM](https://img.shields.io/npm/v/@web5/api.svg?style=flat-square&logo=npm&logoColor=FFFFFF&color=FFEC19&santize=true)](https://www.npmjs.com/package/@web5/api)
[![Build Status](https://img.shields.io/github/actions/workflow/status/TBD54566975/web5-js/tests-ci.yml?branch=main&logo=github&label=ci&logoColor=FFFFFF&style=flat-square)](https://github.com/TBD54566975/web5-js/actions/workflows/tests-ci.yml)
[![Coverage](https://img.shields.io/codecov/c/gh/TBD54566975/web5-js/main?logo=codecov&logoColor=FFFFFF&style=flat-square&token=YI87CKF1LI)](https://codecov.io/github/TBD54566975/web5-js)
[![License](https://img.shields.io/npm/l/@web5/api.svg?style=flat-square&color=24f2ff&logo=apache&logoColor=FFFFFF&santize=true)](https://github.com/TBD54566975/web5-js/blob/main/LICENSE)
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?style=flat-square&color=9a1aff&logo=discord&logoColor=FFFFFF&sanitize=true)](https://discord.com/channels/937858703112155166/969272658501976117)
Making developing with Web5 components at least 5 times easier to work with.
> ⚠️ WEB5 JS SDK IS CURRENTLY IN TECH PREVIEW ⚠️
The SDK is currently still under active development, but having entered the Tech Preview phase there is now a drive to avoid unnecessary changes unless backwards compatibility is provided. Additional functionality will be added in the lead up to 1.0 final, and modifications will be made to address issues and community feedback.
## Table of Contents
- [Introduction](#introduction)
- [Installation](#installation)
- [Usage](#usage)
- [API Documentation](#api-documentation)
- [Web5.connect](#web5connectoptions)
- [web5.dwn.records.query](#web5dwnrecordsqueryrequest)
- [web5.dwn.records.create](#web5dwnrecordscreaterequest)
- [web5.dwn.records.write](#web5dwnrecordswriterequest)
- [web5.dwn.records.read](#web5dwnrecordsreadrequest)
- [web5.dwn.records.delete](#web5dwnrecordsdeleterequest)
- [web5.dwn.protocols.configure](#web5dwnprotocolsconfigurerequest)
- [web5.dwn.protocols.query](#web5dwnprotocolsqueryrequest)
- [web5.did.create](#web5didcreatemethod-options)
- [Project Resources](#project-resources)
## Introduction
Web5 consists of the following components:
- Decentralized Identifiers
- Verifiable Credentials
- DWeb Node personal datastores
The SDK sets out to gather the most oft used functionality from all three of these
pillar technologies to provide a simple library that is as close to effortless as
possible.
## Installation
_NPM_
```yaml
npm install @web5/api
```
_CDNs_
```yaml
https://unpkg.com/0.8.4/dist/browser.js
```
```yaml
https://cdn.jsdelivr.net/npm/@web5/api@0.8.4/dist/browser.mjs
```
## Usage
### Importing the SDK
```javascript
import { Web5 } from "@web5/api";
```
or
```javascript
import { Web5 } from CDN_LINK_HERE;
```
### Additional Steps
This SDK relies indirectly on the [`@noble/ed25519`](https://github.com/paulmillr/noble-ed25519#usage)
and [`@noble/secp256k1`](https://github.com/paulmillr/noble-secp256k1#usage) packages. Therefore,
in certain environments, you'll need to perform additional steps to make it work.
- Node.js <= 18
```js
// node.js 18 and earlier, needs globalThis.crypto polyfill
import { webcrypto } from "node:crypto";
// @ts-ignore
if (!globalThis.crypto) globalThis.crypto = webcrypto;
```
- React Native:
```js
// If you're on react native. React Native needs crypto.getRandomValues polyfill and sha512
import "react-native-get-random-values";
import { hmac } from "@noble/hashes/hmac";
import { sha256 } from "@noble/hashes/sha256";
import { sha512 } from "@noble/hashes/sha512";
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
ed.etc.sha512Async = (...m) => Promise.resolve(ed.etc.sha512Sync(...m));
secp.etc.hmacSha256Sync = (k, ...m) =>
hmac(sha256, k, secp.etc.concatBytes(...m));
secp.etc.hmacSha256Async = (k, ...m) =>
Promise.resolve(secp.etc.hmacSha256Sync(k, ...m));
```
## API Documentation
### **`Web5.connect(options)`**
Enables an app to request connection to a user's local identity app (like a desktop or mobile agent - work is underway for reference apps of each), or generate an in-app DID to represent the user (e.g. if the user does not have an identity app).
> **NOTE:** The outputs of this method invocation will be used throughout the other API methods below.
```javascript
const { web5, did: myDid } = await Web5.connect();
```
#### **`options`** _(optional)_
An object which may specify any of the following properties:
- **`agent`** - _`Web5Agent instance`_ _(optional)_: an instance of a `Web5Agent` implementation. Defaults to creating a local `Web5UserAgent` if not provided.
- **`appData`** - _`AppDataStore instance`_ _(optional)_: an instance of an `AppDataStore` implementation. Defaults to a LevelDB-backed store with an insecure, static unlock passphrase if not provided. To allow the end user to enter a secure passphrase of their choosing, provide an initialized `AppDataVault`.
- **`connectedDid`** - _`string`_ _(optional)_: an existing DID to connect to.
- **`sync`** - _`string`_ _(optional)_: enable or disable synchronization of DWN records between local and remote DWNs. Sync defaults to running every 2 minutes and can be set to any value accepted by [`ms`](https://www.npmjs.com/package/ms). To disable sync set to `'off'`.
- **`techPreview`** - _`object`_ _(optional)_: an object that specifies configuration parameters that are relevant during the Tech Preview period of Web5 JS and may be deprecated in the future with advance notice.
- **`dwnEndpoints`** - _`array`_ _(optional)_: a list of DWeb Node endpoints to define in the DID created and returned by `Web5.connect()`. If this property is omitted, during the Tech Preview two nodes will be included by default (e.g., `['https://dwn.tbddev.org/dwn0', 'https://dwn.tbddev.org/dwn3']`).
For example:
```typescript
const { web5, did: myDid } = await Web5.connect({
techPreview: {
dwnEndpoints: ["https://dwn.your-domain.org/"],
},
});
```
<!-- > NOTE: This method **_MUST_** be invoked within the scope of a 'trusted user action' (something enforced by the OS/browser) if the desire is to connect to a local identity app. For browsers this is generally some direct user action, like clicking a link or button. -->
#### **Response**
An invocation of `Web5.connect()` produces the following items in response:
- **`web5`** - _`Web5 instance`_: A class instance that enables access to a locally running DWeb Node, DID interaction methods, and other capabilities related to the connected DID.
- **`did`** - _`string`_: The DID that was created or attained connection to.
### **`Record` instances from responses**
Every modifying method (`create`, `write`, etc.) and the `entries` from queries return an instance of a `Record` class, which is a representation of the Record(s) being referenced.
Each `Record` instance has the following instance properties: `id`, `attestation`, `contextId`, `dataFormat`, `dateCreated`, `encryption`, `interface`, `method`, `parentId`, `protocol`, `protocolPath`, `recipient`, `schema`, `dataCid`, `dataSize`, `dateModified`, `datePublished`, and `published`.
> **Note** The **`id`** property is a unique identifier based on the record entry's composition. All entries across all records are deterministically unique.
Each `Record` instance has the following instance methods:
- **`data`** - _`object`_: an object with the following convenience methods that read out the data of the record entry in the following formats:
- **`blob`** - _`function`_: returns the data as a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
- **`bytes`** - _`function`_: returns the data as a raw byte array in `Uint8Array` format.
- **`json`** - _`function`_: returns a parsed JSON object.
- **`stream`** - _`function`_: returns the data as a raw stream of bytes.
- **`text`** - _`function`_: returns the data as a string.
- **`send`** - _`function`_: sends the record the instance represents to the DWeb Node endpoints of a provided DID.
- **`update`** - _`function`_: takes in a new request object matching the expected method signature of a `write` and overwrites the record. This is a convenience method that allows you to easily overwrite records with less verbosity.
### **`web5.dwn.records.query(request)`**
Method for querying either the locally connected DWeb Node or any remote DWeb Node specified in the `from` property.
```javascript
// This invocation will query the user's own DWeb Nodes
const { records } = await web5.dwn.records.query({
message: {
filter: {
schema: "https://schema.org/Playlist",
dataFormat: "application/json",
},
},
});
console.log(records); // an array of record entries from Bob's DWeb Nodes
// This invocation will query Bob's DWeb Nodes
const { records } = await web5.dwn.records.query({
from: "did:example:bob",
message: {
filter: {
protocol: "https://music.org/protocol",
schema: "https://schema.org/Playlist",
dataFormat: "application/json",
},
},
});
console.log(records); // an array of record entries from Bob's DWeb Nodes
```
#### **Request**
The query `request` contains the following properties:
- **`from`** - _`DID string`_ (_optional_): the decentralized identifier of the DWeb Node the query will fetch results from.
- **`message`** - _`object`_: the properties of the DWeb Node Message Descriptor that will be used to construct a valid record query:
- **`filter`** - _`object`_: properties against which results of the query will be filtered:
- **`recordId`** - _`string`_ (_optional_): the record ID string that identifies the record data you are fetching.
- **`protocol`** - _`URI string`_ (_optional_): the URI of the protocol bucket in which to query.
- **`protocolPath`** - _`string`_ (_optional_): the path to the record in the protocol configuration.
- **`contextId`** _`string`_ (_optional_): the `recordId` of a root record of a protocol.
- **`parentId`** _`string`_ (_optional_): the `recordId` of a the parent of a protocol record.
- **`recipient`** - _`string`_ (_optional_): the DID in the `recipient` field of the record.
- **`schema`** - _`URI string`_ (_optional_): the URI of the schema bucket in which to query.
- **`dataFormat`** - _`Media Type string`_ (_optional_): the IANA string corresponding with the format of the data to filter for. See IANA's Media Type list here: https://www.iana.org/assignments/media-types/media-types.xhtml
- **`dateSort`** - _`DateSort`_ (_optional_): the `DateSort` value of the date field and direction to sort records by. Defaults to `CreatedAscending`.
- **`pagination`** - _`object`_ (_optional_): the properties used to paginate results.
- **`limit`** - _`number`_ (_optional_): the number of records that should be returned with this query. `undefined` returns all records.
- **`cursor`** - _`messageCid string`_ (_optional_): the `messageCid` of the records toc continue paginating from. This value is returned as a `cursor` in the response object of a `query` if there are more results beyond the `limit`.
#### **Response**
The query `response` contains the following properties:
- **`status`** - _`object`_: the status of the `request`:
- **`code`** - _`number`_: the `Response Status` code, following the response code patterns for `HTTP Response Status Codes`.
- **`detail`** _`string`_: a detailed message describing the response.
- **`records`** - _`Records array`_ (_optional_): the array of `Records` returned if the request was successful.
- **`cursor`** - _`messageCid string`_ (_optional_): the `messageCid` of the last message returned in the results if there are exist additional records beyond the specified `limit` in the `query`.
### **`web5.dwn.records.create(request)`**
Method for creating a new record and storing it in the user's local DWeb Node, remote DWeb Nodes, or another party's DWeb Nodes (if permitted).
```javascript
// this creates a record and stores it in the user's local DWeb Node
const { record } = await web5.dwn.records.create({
data: "Hello World!",
message: {
dataFormat: "text/plain",
},
});
console.log(await record.data.text()); // logs "Hello World!"
const { status } = await record.send(myDid); // send the record to the user's remote DWeb Nodes
const { status } = await record.send("did:example:bob"); // send the newly generated record to Bob's DWeb Nodes
// this creates a record, but does not store it in the user's local DWeb Node
const { record } = await web5.dwn.records.create({
store: false,
data: "Hello again, World!",
message: {
dataFormat: "text/plain",
},
});
const { status } = await record.send("did:example:bob"); // send the newly generated record to Bob's DWeb Nodes
```
#### **Request**
The `create` request object is composed as follows:
- **`store`** - _`boolean`_ (_optional_): tells the create function whether or not to store the record in the user's local DWeb Node. (you might pass `false` if you didn't want to retain a copy of the record for yourself)
- **`data`** - _`text|object|file|blob`_: the data payload of the record.
- **`message`** - _`object`_: The properties of the DWeb Node Message Descriptor that will be used to construct a valid record query:
- **`protocol`** - _`URI string`_ (_optional_): the URI of the protocol under which the record will be bucketed.
- **`schema`** - _`URI string`_ (_optional_): the URI of the schema under which the record will be bucketed.
- **`dataFormat`** - _`Media Type string`_ (_optional_): the IANA string corresponding with the format of the data the record will be bucketed. See IANA's Media Type list here: https://www.iana.org/assignments/media-types/media-types.xhtml
### **`web5.dwn.records.write(request)`**
The `create()` method is an alias for `write()` and both can take the same request object properties.
### **`web5.dwn.records.read(request)`**
Method for reading a record stored in the user's local DWeb Node, remote DWeb Nodes, or another party's DWeb Nodes (if permitted). The request takes a filter; if there is exactly one record matching the filter, the record and its data are returned. The most common filter is by `recordId`, but it is also useful to filter by `protocol`, `contextId`, and `protocolPath`.
```javascript
// Reads the indicated record from the user's DWeb Nodes
const { record } = await web5.dwn.records.read({
message: {
filter: {
recordId: "bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5",
},
},
});
console.log(await record.data.text()); // assuming the record is a text payload, logs the text
// Reads the indicated record from Bob's DWeb Nodes
const { record } = await web5.dwn.records.read({
from: "did:example:bob",
message: {
filter: {
recordId: "bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5",
},
},
});
console.log(await record.data.text()); // assuming the record is a text payload, logs the text
```
#### **Request**
The `read` request object is composed as follows:
- **`from`** - _`DID string`_ (_optional_): The DID of the DWeb Node the read request will fetch the indicated record from.
- **`message`** - _`object`_: The properties of the DWeb Node Message Descriptor that will be used to construct a valid DWeb Node message.
- **`filter`** - _`object`_: properties against which results of the query will be filtered:
- **`recordId`** - _`string`_ (_optional_): the record ID string that identifies the record data you are fetching.
- **`protocol`** - _`URI string`_ (_optional_): the URI of the protocol bucket in which to query.
- **`protocolPath`** - _`string`_ (_optional_): the path to the record in the protocol configuration.
- **`contextId`** _`string`_ (_optional_): the `recordId` of a root record of a protocol.
- **`parentId`** _`string`_ (_optional_): the `recordId` of a the parent of a protocol record.
- **`recipient`** - _`string`_ (_optional_): the DID in the `recipient` field of the record.
- **`schema`** - _`URI string`_ (_optional_): the URI of the schema bucket in which to query.
- **`dataFormat`** - _`Media Type string`_ (_optional_): the IANA string corresponding with the format of the data to filter for. See IANA's Media Type list here: https://www.iana.org/assignments/media-types/media-types.xhtml
### **`web5.dwn.records.delete(request)`**
Method for deleting a record stored in the user's local DWeb Node, remote DWeb Nodes, or another party's DWeb Nodes (if permitted).
```javascript
// Deletes the indicated record from the user's DWeb Node
const { record } = await web5.dwn.records.delete({
message: {
recordId: "bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5",
},
});
// Deletes the indicated record from Bob's DWeb Node
const { record } = await web5.dwn.records.delete({
from: "did:example:bob",
message: {
recordId: "bfw35evr6e54c4cqa4c589h4cq3v7w4nc534c9w7h5",
},
});
```
#### **Request**
The `delete` request object is composed as follows:
- **`from`** - _`DID string`_ (_optional_): The DID of the DWeb Node the delete tombstone will be sent to.
- **`message`** - _`object`_: The properties of the DWeb Node Message Descriptor that will be used to construct a valid DWeb Node message.
- **`recordId`** - _`string`_: the required record ID string that identifies the record being deleted.
### **`web5.dwn.protocols.configure(request)`**
Method for configuring a protocol definition in the DWeb Node of the user's local DWeb Node, remote DWeb Nodes, or another party's DWeb Nodes (if permitted).
```javascript
const { protocol } = await web5.dwn.protocols.configure({
message: {
definition: {
protocol: "https://photos.org/protocol",
published: true,
types: {
album: {
schema: "https://photos.org/protocol/album",
dataFormats: ["application/json"],
},
photo: {
schema: "https://photos.org/protocols/photo",
dataFormats: ["application/json"],
},
binaryImage: {
dataFormats: ["image/png", "jpeg", "gif"],
},
},
structure: {
album: {
$actions: [
{
who: "recipient",
can: "read",
},
],
},
photo: {
$actions: [
{
who: "recipient",
can: "read",
},
],
binaryImage: {
$actions: [
{
who: "author",
of: "photo",
can: "write",
},
],
},
},
},
},
},
});
protocol.send(myDid); // sends the protocol configuration to the user's other DWeb Nodes.
```
#### **Request**
The `configure` request object is composed as follows:
- **`message`** - _`object`_: The properties of the DWeb Node Message Descriptor that will be used to construct a valid DWeb Node message.
- **`definition`** - _`object`_: an object that defines the enforced composition of the protocol.
- **`protocol`** - _`URI string`_: a URI that represents the protocol being configured.
- **`types`** - _`object`_: an object that defines the records that can be used in the `structure` graph of the `definition` object. The following properties are optional constraints you can set for the type being defined:
- **`schema`** - _`URI string`_ (_optional_): the URI of the schema under which the record will be bucketed.
- **`dataFormats`** - _`Media Type string[]`_ (_optional_): Array of the IANA strings corresponding with the formats of the data the record will be bucketed. See IANA's Media Type list here: https://www.iana.org/assignments/media-types/media-types.xhtml
- **`structure`** - _`object`_: an object that defines the structure of a protocol, including data relationships and constraints on which entities can perform various activities. Fields under the `structure` object of the Protocol definition are expected to be either type references matching those defined in the `types` object. The type structures are recursive, so types form a graph and each type can have within it further attached types or the following rule statements that are all denoted with the prefix `$`:
- **`$actions`** - _`array`_: one or more rule objects that expose various allowed actions to actors (`author`, `recipient`), composed as follows:
- **`who`** - _`string`_: the actor (`author`, `recipient`) that is being permitted to invoke a given action.
- **`of`** - _`string`_: the protocol path that refers to the record subject. Using the above example protocol, the protocol path to `binaryImage` would be `photo/binaryImage`.
- **`can`** - _`string`_: the action being permitted by the rule.
### **`web5.dwn.protocols.query(request)`**
Method for querying a DID's DWeb Nodes for the presence of a protocol. This method is useful in detecting what protocols a given DID has installed to enable interaction over the protocol.
```javascript
const { protocols } = await web5.dwn.protocols.query({
message: {
filter: {
protocol: "https://music.org/protocol",
},
},
});
console.log(protocols); // logs an array of protocol configurations installed on the user's own DWeb Node
const { protocols } = await web5.dwn.protocols.query({
from: "did:example:bob",
message: {
filter: {
protocol: "https://music.org/protocol",
},
},
});
console.log(protocols); // logs an array of protocol configurations installed on Bob's DWeb Node
```
#### **Request**
The `query` request object must contain the following:
- **`from`** - _`DID string`_ (_optional_): the decentralized identifier of the DWeb Node the query will fetch results from.
- **`message`** - _`object`_: The properties of the DWeb Node Message Descriptor that will be used to construct a valid record query:
- **`filter`** - _`object`_ (_optional_): properties against which results of the query will be filtered:
- **`protocol`** - _`URI string`_ (_optional_): the URI of the protocol bucket in which to query.
### **`web5.did.create(request)`**
The `create` method under the `did` object enables generation of DIDs for a supported set of DID Methods ('dht'|'jwk').
The output is method-specific, and handles things like key generation and assembly of DID Documents that can be
published to DID networks.
#### **Usage**
```javascript
const myDid = await web5.did.create("dht");
```
#### **Parameters**
The `create` request object must contain the following parameters:
- **`method`** - _`string`_: The DID method to use for generating the DID. Supported methods include 'dht' and 'jwk', among others that may be supported by the SDK.
- **`options`** - _`object`_ (_optional_): An object containing options specific to the DID method chosen. These options can influence how the DID is generated. For instance, they can dictate specifics about the cryptographic keys that are generated or associated with the new DID. Common options include:
- **`store`** - _`boolean`_ (_optional_): Determines whether the DID's cryptographic keys and metadata will be stored in
the user's DWeb Node.
#### **Notes**
- Typically developers will not manually invoke this method as the more common approach is to use the `Web5.connect()`
method to acquire a DID for the user (either by direct creation or connection to an identity agent app).
### **`web5.did.resolve(didUri, options)`**
The `resolve` method under the `did` object enables the resolution of a Decentralized Identifier (DID) to its
corresponding DID Document. This operation allows applications to fetch the public keys, service endpoints, and other
metadata associated with a DID.
#### **Usage**
```javascript
const { didDocument } = await web5.did.resolve('did:dht:qftx7z968xcpfy1a1diu75pg5meap3gdtg6ezagaw849wdh6oubo');
```
#### **Parameters**
- **`didUri`** - _`string`_: The DID URI to be resolved. This should be a fully qualified DID following the standard scheme `did:<method>:<identifier>`.
- **`options`** (_optional_): An object containing options for DID resolution. This can include method-specific parameters that influence the resolution process.
#### **Response**
The method returns a DID resolution result as a JavaScript object. The structure of this object adheres to the
[DID Core specifications](https://www.w3.org/TR/did-core/#did-resolution), containing the elements
`didResolutionMetadata`, `didDocument`, and `didDocumentMetadata`.
#### **Notes**
- The resolution process for some DID methods like DID DHT involve network requests to the relevant DID verifiable
data registry or a resolver endpoint, which may introduce latency based on the network conditions and the specific DID
method utilized.
## Project Resources
| Resource | Description |
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [CODEOWNERS](https://github.com/TBD54566975/web5-js/blob/main/CODEOWNERS) | Outlines the project lead(s) |
| [CODE_OF_CONDUCT.md](https://github.com/TBD54566975/web5-js/blob/main/CODE_OF_CONDUCT.md) | Expected behavior for project contributors, promoting a welcoming environment |
| [CONTRIBUTING.md](https://github.com/TBD54566975/web5-js/blob/main/CONTRIBUTING.md) | Developer guide to build, test, run, access CI, chat, discuss, file issues |
| [GOVERNANCE.md](https://github.com/TBD54566975/web5-js/blob/main/GOVERNANCE.md) | Project governance |
| [LICENSE](./LICENSE) | Apache License, Version 2.0 |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+126
View File
@@ -0,0 +1,126 @@
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DidApi = void 0;
var agent_1 = require("@web5/agent");
/**
* The DID API is used to resolve DIDs.
*
* @beta
*/
var DidApi = /** @class */ (function () {
function DidApi(options) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* Initiates the creation of a Decentralized Identifier (DID) using the specified method, options,
* and storage preference.
*
* This method sends a request to the Web5 Agent to create a new DID based on the provided method,
* with method-specific options. It also specifies whether the newly created DID should be stored.
*
* @param request - The request parameters for creating a DID, including the method, options, and
* storage flag.
* @returns A promise that resolves to a `DidCreateResponse`, which includes the operation's
* status and, if successful, the newly created DID.
*/
DidApi.prototype.create = function (request) {
return __awaiter(this, void 0, void 0, function () {
var _a, result, status;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, this.agent.processDidRequest({
messageType: agent_1.DidInterface.Create,
messageParams: __assign({}, request)
})];
case 1:
_a = _b.sent(), result = _a.result, status = __rest(_a, ["result"]);
return [2 /*return*/, __assign({ did: result }, status)];
}
});
});
};
/**
* Resolves a DID to a DID Resolution Result.
*
* @param didUri - The DID or DID URL to resolve.
* @returns A promise that resolves to the DID Resolution Result.
*/
DidApi.prototype.resolve = function (didUri, options) {
return __awaiter(this, void 0, void 0, function () {
var didResolutionResult;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.agent.processDidRequest({
messageParams: { didUri: didUri, options: options },
messageType: agent_1.DidInterface.Resolve
})];
case 1:
didResolutionResult = (_a.sent()).result;
return [2 /*return*/, didResolutionResult];
}
});
});
};
return DidApi;
}());
exports.DidApi = DidApi;
//# sourceMappingURL=did-api.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"did-api.js","sourceRoot":"","sources":["../../src/did-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,qCAA2C;AA8B3C;;;;GAIG;AACH;IAUE,gBAAY,OAAmD;QAC7D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;OAWG;IACU,uBAAM,GAAnB,UAAoB,OAAyB;;;;;4BACb,qBAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;4BAC/D,WAAW,EAAK,oBAAY,CAAC,MAAM;4BACnC,aAAa,eAAQ,OAAO,CAAE;yBAC/B,CAAC,EAAA;;wBAHI,KAAwB,SAG5B,EAHM,MAAM,YAAA,EAAK,MAAM,cAAnB,UAAqB,CAAF;wBAKzB,iCAAS,GAAG,EAAE,MAAM,IAAK,MAAM,GAAG;;;;KACnC;IAED;;;;;OAKG;IACU,wBAAO,GAApB,UACE,MAAkC,EAAE,OAAqC;;;;;4BAEjC,qBAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;4BACzE,aAAa,EAAG,EAAE,MAAM,QAAA,EAAE,OAAO,SAAA,EAAE;4BACnC,WAAW,EAAK,oBAAY,CAAC,OAAO;yBACrC,CAAC,EAAA;;wBAHc,mBAAmB,GAAK,CAAA,SAGtC,CAAA,OAHiC;wBAKnC,sBAAO,mBAAmB,EAAC;;;;KAC5B;IACH,aAAC;AAAD,CAAC,AApDD,IAoDC;AApDY,wBAAM"}
+407
View File
@@ -0,0 +1,407 @@
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DwnApi = void 0;
var common_1 = require("@web5/common");
var agent_1 = require("@web5/agent");
var record_js_1 = require("./record.js");
var utils_js_1 = require("./utils.js");
var protocol_js_1 = require("./protocol.js");
/**
* Interface to interact with DWN Records and Protocols
*/
var DwnApi = /** @class */ (function () {
function DwnApi(options) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
Object.defineProperty(DwnApi.prototype, "protocols", {
/**
* API to interact with DWN protocols (e.g., `dwn.protocols.configure()`).
*/
get: function () {
var _this = this;
return {
/**
* Configure method, used to setup a new protocol (or update) with the passed definitions
*/
configure: function (request) { return __awaiter(_this, void 0, void 0, function () {
var agentResponse, message, messageCid, status, response, metadata;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.agent.processDwnRequest({
author: this.connectedDid,
messageParams: request.message,
messageType: agent_1.DwnInterface.ProtocolsConfigure,
target: this.connectedDid
})];
case 1:
agentResponse = _a.sent();
message = agentResponse.message, messageCid = agentResponse.messageCid, status = agentResponse.reply.status;
response = { status: status };
if (status.code < 300) {
metadata = { author: this.connectedDid, messageCid: messageCid };
response.protocol = new protocol_js_1.Protocol(this.agent, message, metadata);
}
return [2 /*return*/, response];
}
});
}); },
/**
* Query the available protocols
*/
query: function (request) { return __awaiter(_this, void 0, void 0, function () {
var agentRequest, agentResponse, reply, _a, entries, status, protocols;
var _this = this;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
agentRequest = {
author: this.connectedDid,
messageParams: request.message,
messageType: agent_1.DwnInterface.ProtocolsQuery,
target: request.from || this.connectedDid
};
if (!request.from) return [3 /*break*/, 2];
return [4 /*yield*/, this.agent.sendDwnRequest(agentRequest)];
case 1:
agentResponse = _b.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, this.agent.processDwnRequest(agentRequest)];
case 3:
agentResponse = _b.sent();
_b.label = 4;
case 4:
reply = agentResponse.reply;
_a = reply.entries, entries = _a === void 0 ? [] : _a, status = reply.status;
protocols = entries.map(function (entry) {
var metadata = { author: _this.connectedDid };
return new protocol_js_1.Protocol(_this.agent, entry, metadata);
});
return [2 /*return*/, { protocols: protocols, status: status }];
}
});
}); }
};
},
enumerable: false,
configurable: true
});
Object.defineProperty(DwnApi.prototype, "records", {
/**
* API to interact with DWN records (e.g., `dwn.records.create()`).
*/
get: function () {
var _this = this;
return {
/**
* Alias for the `write` method
*/
create: function (request) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.records.write(request)];
});
}); },
/**
* Write a record based on an existing one (useful for updating an existing record)
*/
createFrom: function (request) { return __awaiter(_this, void 0, void 0, function () {
var _a, inheritedAuthor, inheritedProperties;
var _b;
return __generator(this, function (_c) {
_a = request.record.toJSON(), inheritedAuthor = _a.author, inheritedProperties = __rest(_a, ["author"]);
// If `data` is being updated then `dataCid` and `dataSize` must not be present.
if (request.data !== undefined) {
delete inheritedProperties.dataCid;
delete inheritedProperties.dataSize;
}
// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
// will throw an error if `published` is false but `datePublished` is set.
if (((_b = request.message) === null || _b === void 0 ? void 0 : _b.published) === false && inheritedProperties.datePublished !== undefined) {
delete inheritedProperties.datePublished;
delete inheritedProperties.published;
}
// If the request changes the `author` or message `descriptor` then the deterministic `recordId` will change.
// As a result, we will discard the `recordId` if either of these changes occur.
if (!(0, common_1.isEmptyObject)(request.message) || (request.author && request.author !== inheritedAuthor)) {
delete inheritedProperties.recordId;
}
return [2 /*return*/, this.records.write({
data: request.data,
message: __assign(__assign({}, inheritedProperties), request.message),
})];
});
}); },
/**
* Delete a record
*/
delete: function (request) { return __awaiter(_this, void 0, void 0, function () {
var agentRequest, agentResponse, status;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
agentRequest = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author: this.connectedDid,
messageParams: request.message,
messageType: agent_1.DwnInterface.RecordsDelete,
/**
* The `target` is the DID of the DWN tenant under which the delete will be executed.
* If `from` is provided, the delete operation will be executed on a remote DWN.
* Otherwise, the record will be deleted on the local DWN.
*/
target: request.from || this.connectedDid
};
if (!request.from) return [3 /*break*/, 2];
return [4 /*yield*/, this.agent.sendDwnRequest(agentRequest)];
case 1:
agentResponse = _a.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, this.agent.processDwnRequest(agentRequest)];
case 3:
agentResponse = _a.sent();
_a.label = 4;
case 4:
status = agentResponse.reply.status;
return [2 /*return*/, { status: status }];
}
});
}); },
/**
* Query a single or multiple records based on the given filter
*/
query: function (request) { return __awaiter(_this, void 0, void 0, function () {
var agentRequest, agentResponse, reply, entries, status, cursor, records;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
agentRequest = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author: this.connectedDid,
messageParams: request.message,
messageType: agent_1.DwnInterface.RecordsQuery,
/**
* The `target` is the DID of the DWN tenant under which the query will be executed.
* If `from` is provided, the query operation will be executed on a remote DWN.
* Otherwise, the local DWN will be queried.
*/
target: request.from || this.connectedDid
};
if (!request.from) return [3 /*break*/, 2];
return [4 /*yield*/, this.agent.sendDwnRequest(agentRequest)];
case 1:
agentResponse = _a.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, this.agent.processDwnRequest(agentRequest)];
case 3:
agentResponse = _a.sent();
_a.label = 4;
case 4:
reply = agentResponse.reply;
entries = reply.entries, status = reply.status, cursor = reply.cursor;
records = entries.map(function (entry) {
var recordOptions = __assign({
/**
* Extract the `author` DID from the record entry since records may be signed by the
* tenant owner or any other entity.
*/
author: (0, agent_1.getRecordAuthor)(entry),
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN even if the record was returned by a query of a remote DWN.
*/
connectedDid: _this.connectedDid,
/**
* If the record was returned by a query of a remote DWN, set the `remoteOrigin` to
* the DID of the DWN that returned the record. The `remoteOrigin` property will be used
* to determine which DWN to send subsequent read requests to in the event the data
* payload exceeds the threshold for being returned with queries.
*/
remoteOrigin: request.from }, entry);
var record = new record_js_1.Record(_this.agent, recordOptions);
return record;
});
return [2 /*return*/, { records: records, status: status, cursor: cursor }];
}
});
}); },
/**
* Read a single record based on the given filter
*/
read: function (request) { return __awaiter(_this, void 0, void 0, function () {
var agentRequest, agentResponse, _a, responseRecord, status, record, recordOptions;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
agentRequest = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author: this.connectedDid,
messageParams: request.message,
messageType: agent_1.DwnInterface.RecordsRead,
/**
* The `target` is the DID of the DWN tenant under which the read will be executed.
* If `from` is provided, the read operation will be executed on a remote DWN.
* Otherwise, the read will occur on the local DWN.
*/
target: request.from || this.connectedDid
};
if (!request.from) return [3 /*break*/, 2];
return [4 /*yield*/, this.agent.sendDwnRequest(agentRequest)];
case 1:
agentResponse = _b.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, this.agent.processDwnRequest(agentRequest)];
case 3:
agentResponse = _b.sent();
_b.label = 4;
case 4:
_a = agentResponse.reply, responseRecord = _a.record, status = _a.status;
if (200 <= status.code && status.code <= 299) {
recordOptions = __assign({
/**
* Extract the `author` DID from the record since records may be signed by the
* tenant owner or any other entity.
*/
author: (0, agent_1.getRecordAuthor)(responseRecord),
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN even if the record was read from a remote DWN.
*/
connectedDid: this.connectedDid,
/**
* If the record was returned by reading from a remote DWN, set the `remoteOrigin` to
* the DID of the DWN that returned the record. The `remoteOrigin` property will be used
* to determine which DWN to send subsequent read requests to in the event the data
* payload must be read again (e.g., if the data stream is consumed).
*/
remoteOrigin: request.from }, responseRecord);
record = new record_js_1.Record(this.agent, recordOptions);
}
return [2 /*return*/, { record: record, status: status }];
}
});
}); },
/**
* Writes a record to the DWN
*
* As a convenience, the Record instance returned will cache a copy of the data. This is done
* to maintain consistency with other DWN methods, like RecordsQuery, that include relatively
* small data payloads when returning RecordsWrite message properties. Regardless of data
* size, methods such as `record.data.stream()` will return the data when called even if it
* requires fetching from the DWN datastore.
*/
write: function (request) { return __awaiter(_this, void 0, void 0, function () {
var _a, dataBlob, dataFormat, agentResponse, responseMessage, status, record, recordOptions;
var _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = (0, utils_js_1.dataToBlob)(request.data, (_b = request.message) === null || _b === void 0 ? void 0 : _b.dataFormat), dataBlob = _a.dataBlob, dataFormat = _a.dataFormat;
return [4 /*yield*/, this.agent.processDwnRequest({
author: this.connectedDid,
dataStream: dataBlob,
messageParams: __assign(__assign({}, request.message), { dataFormat: dataFormat }),
messageType: agent_1.DwnInterface.RecordsWrite,
store: request.store,
target: this.connectedDid
})];
case 1:
agentResponse = _c.sent();
responseMessage = agentResponse.message, status = agentResponse.reply.status;
if (200 <= status.code && status.code <= 299) {
recordOptions = __assign({
/**
* Assume the author is the connected DID since the record was just written to the
* local DWN.
*/
author: this.connectedDid,
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN.
*/
connectedDid: this.connectedDid, encodedData: dataBlob }, responseMessage);
record = new record_js_1.Record(this.agent, recordOptions);
}
return [2 /*return*/, { record: record, status: status }];
}
});
}); },
};
},
enumerable: false,
configurable: true
});
return DwnApi;
}());
exports.DwnApi = DwnApi;
//# sourceMappingURL=dwn-api.js.map
File diff suppressed because one or more lines are too long
+62
View File
@@ -0,0 +1,62 @@
"use strict";
/**
* Making developing with Web5 components at least 5 times easier to work with.
*
* Web5 consists of the following components:
* - Decentralized Identifiers
* - Verifiable Credentials
* - DWeb Node personal datastores
*
* The SDK sets out to gather the most oft used functionality from all three of
* these pillar technologies to provide a simple library that is as close to
* effortless as possible.
*
* The SDK is currently still under active development, but having entered the
* Tech Preview phase there is now a drive to avoid unnecessary changes unless
* backwards compatibility is provided. Additional functionality will be added
* in the lead up to 1.0 final, and modifications will be made to address
* issues and community feedback.
*
* [Link to GitHub Repo](https://github.com/TBD54566975/web5-js)
*
* @packageDocumentation
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.utils = void 0;
__exportStar(require("./did-api.js"), exports);
__exportStar(require("./dwn-api.js"), exports);
__exportStar(require("./protocol.js"), exports);
__exportStar(require("./record.js"), exports);
__exportStar(require("./vc-api.js"), exports);
__exportStar(require("./web5.js"), exports);
__exportStar(require("./tech-preview.js"), exports);
__exportStar(require("./web-features.js"), exports);
var utils = __importStar(require("./utils.js"));
exports.utils = utils;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,+CAA6B;AAC7B,+CAA6B;AAC7B,gDAA8B;AAC9B,8CAA4B;AAC5B,8CAA4B;AAC5B,4CAA0B;AAC1B,oDAAkC;AAClC,oDAAkC;AAElC,gDAAoC;AAC3B,sBAAK"}
+1
View File
@@ -0,0 +1 @@
{"type": "commonjs"}
+110
View File
@@ -0,0 +1,110 @@
"use strict";
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
/// <reference types="@tbd54566975/dwn-sdk-js" />
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Protocol = void 0;
var agent_1 = require("@web5/agent");
/**
* Encapsulates a DWN Protocol with its associated metadata and configuration.
*
* This class primarly exists to provide developers with a convenient way to configure/install
* protocols on remote DWNs.
*/
var Protocol = /** @class */ (function () {
/**
* Constructs a new instance of the Protocol class.
*
* @param agent - The Web5Agent instance used for network interactions.
* @param protocolsConfigureMessage - The configuration message containing the protocol details.
* @param metadata - Metadata associated with the protocol, including the author and optional message CID.
*/
function Protocol(agent, protocolsConfigureMessage, metadata) {
this._agent = agent;
this._metadata = metadata;
this._protocolsConfigureMessage = protocolsConfigureMessage;
}
Object.defineProperty(Protocol.prototype, "definition", {
/**
* Retrieves the protocol definition from the protocol's configuration message.
* @returns The protocol definition.
*/
get: function () {
return this._protocolsConfigureMessage.descriptor.definition;
},
enumerable: false,
configurable: true
});
/**
* Serializes the protocol's configuration message to JSON.
* @returns The serialized JSON object of the protocol's configuration message.
*/
Protocol.prototype.toJSON = function () {
return this._protocolsConfigureMessage;
};
/**
* Sends the protocol configuration to a remote DWN identified by the target DID.
*
* @param target - The DID of the target DWN to which the protocol configuration will be installed.
* @returns A promise that resolves to an object containing the status of the send operation.
*/
Protocol.prototype.send = function (target) {
return __awaiter(this, void 0, void 0, function () {
var reply;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this._agent.sendDwnRequest({
author: this._metadata.author,
messageCid: this._metadata.messageCid,
messageType: agent_1.DwnInterface.ProtocolsConfigure,
target: target,
})];
case 1:
reply = (_a.sent()).reply;
return [2 /*return*/, { status: reply.status }];
}
});
});
};
return Protocol;
}());
exports.Protocol = Protocol;
//# sourceMappingURL=protocol.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,iDAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIjD,qCAA2C;AAiB3C;;;;;GAKG;AACH;IAUE;;;;;;OAMG;IACH,kBAAY,KAAgB,EAAE,yBAAsE,EAAE,QAA0B;QAC9H,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,0BAA0B,GAAG,yBAAyB,CAAC;IAC9D,CAAC;IAMD,sBAAI,gCAAU;QAJd;;;WAGG;aACH;YACE,OAAO,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,UAAU,CAAC;QAC/D,CAAC;;;OAAA;IAED;;;OAGG;IACH,yBAAM,GAAN;QACE,OAAO,IAAI,CAAC,0BAA0B,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACG,uBAAI,GAAV,UAAW,MAAc;;;;;4BACL,qBAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;4BACjD,MAAM,EAAQ,IAAI,CAAC,SAAS,CAAC,MAAM;4BACnC,UAAU,EAAI,IAAI,CAAC,SAAS,CAAC,UAAU;4BACvC,WAAW,EAAG,oBAAY,CAAC,kBAAkB;4BAC7C,MAAM,EAAQ,MAAM;yBACrB,CAAC,EAAA;;wBALM,KAAK,GAAK,CAAA,SAKhB,CAAA,MALW;wBAOb,sBAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,EAAC;;;;KACjC;IACH,eAAC;AAAD,CAAC,AAvDD,IAuDC;AAvDY,4BAAQ"}
+885
View File
@@ -0,0 +1,885 @@
"use strict";
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
/// <reference types="@tbd54566975/dwn-sdk-js" />
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Record = void 0;
var agent_1 = require("@web5/agent");
var agent_2 = require("@web5/agent");
var common_1 = require("@web5/common");
var utils_js_1 = require("./utils.js");
/**
* Record wrapper class with convenience methods to send and update,
* aside from manipulating and reading the record data.
*
* Note: The `messageTimestamp` of the most recent RecordsWrite message is
* logically equivalent to the date/time at which a Record was most
* recently modified. Since this Record class implementation is
* intended to simplify the developer experience of working with
* logical records (and not individual DWN messages) the
* `messageTimestamp` is mapped to `dateModified`.
*
* @beta
*/
/**
* The `Record` class encapsulates a single record's data and metadata, providing a more
* developer-friendly interface for working with Decentralized Web Node (DWN) records.
*
* Methods are provided to read, update, and manage the record's lifecycle, including writing to
* remote DWNs.
*
* @beta
*/
var Record = exports.Record = /** @class */ (function () {
function Record(agent, options) {
this._agent = agent;
// Store the author DID that originally signed the message as a convenience for developers, so
// that they don't have to decode the signer's DID from the JWS.
this._author = options.author;
// Store the currently `connectedDid` so that subsequent message signing is done with the
// connected DID's keys and DWN requests target the connected DID's DWN.
this._connectedDid = options.connectedDid;
// If the record was queried or read from a remote DWN, the `remoteOrigin` DID will be
// defined. This value is used to send subsequent read requests to the same remote DWN in the
// event the record's data payload was too large to be returned in query results. or must be
// read again (e.g., if the data stream is consumed).
this._remoteOrigin = options.remoteOrigin;
// RecordsWriteMessage properties.
this._attestation = options.attestation;
this._authorization = options.authorization;
this._contextId = options.contextId;
this._descriptor = options.descriptor;
this._encryption = options.encryption;
this._initialWrite = options.initialWrite;
this._recordId = options.recordId;
this._protocolRole = options.protocolRole;
if (options.encodedData) {
// If `encodedData` is set, then it is expected that:
// type is Blob if the Record object was instantiated by dwn.records.create()/write().
// type is Base64 URL encoded string if the Record object was instantiated by dwn.records.query().
// If it is a string, we need to Base64 URL decode to bytes and instantiate a Blob.
this._encodedData = (typeof options.encodedData === 'string') ?
new Blob([common_1.Convert.base64Url(options.encodedData).toUint8Array()], { type: this.dataFormat }) :
options.encodedData;
}
if (options.data) {
// If the record was created from a RecordsRead reply then it will have a `data` property.
// If the `data` property is a web ReadableStream, convert it to a Node.js Readable.
this._readableStream = common_1.Stream.isReadableStream(options.data) ?
common_1.NodeStream.fromWebReadable({ readableStream: options.data }) :
options.data;
}
}
Object.defineProperty(Record.prototype, "attestation", {
// Getters for immutable DWN Record properties.
/** Record's signatures attestation */
get: function () { return this._attestation; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "authorization", {
/** Record's signatures attestation */
get: function () { return this._authorization; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "author", {
/** DID that signed the record. */
get: function () { return this._author; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "contextId", {
/** Record's context ID */
get: function () { return this._contextId; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "dataFormat", {
/** Record's data format */
get: function () { return this._descriptor.dataFormat; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "dateCreated", {
/** Record's creation date */
get: function () { return this._descriptor.dateCreated; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "encryption", {
/** Record's encryption */
get: function () { return this._encryption; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "initialWrite", {
/** Record's initial write if the record has been updated */
get: function () { return this._initialWrite; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "id", {
/** Record's ID */
get: function () { return this._recordId; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "interface", {
/** Interface is always `Records` */
get: function () { return this._descriptor.interface; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "method", {
/** Method is always `Write` */
get: function () { return this._descriptor.method; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "parentId", {
/** Record's parent ID */
get: function () { return this._descriptor.parentId; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "protocol", {
/** Record's protocol */
get: function () { return this._descriptor.protocol; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "protocolPath", {
/** Record's protocol path */
get: function () { return this._descriptor.protocolPath; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "protocolRole", {
/** Role under which the author is writing the record */
get: function () { return this._protocolRole; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "recipient", {
/** Record's recipient */
get: function () { return this._descriptor.recipient; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "schema", {
/** Record's schema */
get: function () { return this._descriptor.schema; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "dataCid", {
// Getters for mutable DWN Record properties.
/** Record's CID */
get: function () { return this._descriptor.dataCid; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "dataSize", {
/** Record's data size */
get: function () { return this._descriptor.dataSize; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "dateModified", {
/** Record's modified date */
get: function () { return this._descriptor.messageTimestamp; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "datePublished", {
/** Record's published date */
get: function () { return this._descriptor.datePublished; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "messageTimestamp", {
/** Record's published status */
get: function () { return this._descriptor.messageTimestamp; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "published", {
/** Record's published status (true/false) */
get: function () { return this._descriptor.published; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "tags", {
/** Tags of the record */
get: function () { return this._descriptor.tags; },
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "rawMessage", {
/**
* Returns a copy of the raw `RecordsWriteMessage` that was used to create the current `Record` instance.
*/
get: function () {
var message = JSON.parse(JSON.stringify({
contextId: this._contextId,
recordId: this._recordId,
descriptor: this._descriptor,
attestation: this._attestation,
authorization: this._authorization,
encryption: this._encryption,
}));
(0, common_1.removeUndefinedProperties)(message);
return message;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Record.prototype, "data", {
/**
* Returns the data of the current record.
* If the record data is not available, it attempts to fetch the data from the DWN.
* @returns a data stream with convenience methods such as `blob()`, `json()`, `text()`, and `stream()`, similar to the fetch API response
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
get: function () {
var self = this; // Capture the context of the `Record` instance.
var dataObj = {
/**
* Returns the data of the current record as a `Blob`.
*
* @returns A promise that resolves to a Blob containing the record's data.
* @throws If the record data is not available or cannot be converted to a `Blob`.
*
* @beta
*/
blob: function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b, _c;
var _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
_a = Blob.bind;
_c = (_b = common_1.NodeStream).consumeToBytes;
_d = {};
return [4 /*yield*/, this.stream()];
case 1: return [4 /*yield*/, _c.apply(_b, [(_d.readable = _e.sent(), _d)])];
case 2: return [2 /*return*/, new (_a.apply(Blob, [void 0, [_e.sent()], { type: self.dataFormat }]))()];
}
});
});
},
/**
* Returns the data of the current record as a `Uint8Array`.
*
* @returns A Promise that resolves to a `Uint8Array` containing the record's data bytes.
* @throws If the record data is not available or cannot be converted to a byte array.
*
* @beta
*/
bytes: function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
var _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_b = (_a = common_1.NodeStream).consumeToBytes;
_c = {};
return [4 /*yield*/, this.stream()];
case 1: return [4 /*yield*/, _b.apply(_a, [(_c.readable = _d.sent(), _c)])];
case 2: return [2 /*return*/, _d.sent()];
}
});
});
},
/**
* Parses the data of the current record as JSON and returns it as a JavaScript object.
*
* @returns A Promise that resolves to a JavaScript object parsed from the record's JSON data.
* @throws If the record data is not available, not in JSON format, or cannot be parsed.
*
* @beta
*/
json: function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
var _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_b = (_a = common_1.NodeStream).consumeToJson;
_c = {};
return [4 /*yield*/, this.stream()];
case 1: return [4 /*yield*/, _b.apply(_a, [(_c.readable = _d.sent(), _c)])];
case 2: return [2 /*return*/, _d.sent()];
}
});
});
},
/**
* Returns the data of the current record as a `string`.
*
* @returns A promise that resolves to a `string` containing the record's text data.
* @throws If the record data is not available or cannot be converted to text.
*
* @beta
*/
text: function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
var _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_b = (_a = common_1.NodeStream).consumeToText;
_c = {};
return [4 /*yield*/, this.stream()];
case 1: return [4 /*yield*/, _b.apply(_a, [(_c.readable = _d.sent(), _c)])];
case 2: return [2 /*return*/, _d.sent()];
}
});
});
},
/**
* Provides a `Readable` stream containing the record's data.
*
* @returns A promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If the record data is not available in-memory and cannot be fetched.
*
* @beta
*/
stream: function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!self._encodedData) return [3 /*break*/, 1];
/** If `encodedData` is set, it indicates that the Record was instantiated by
* `dwn.records.create()`/`dwn.records.write()` or the record's data payload was small
* enough to be returned in `dwn.records.query()` results. In either case, the data is
* already available in-memory and can be returned as a Node.js `Readable` stream. */
self._readableStream = common_1.NodeStream.fromWebReadable({ readableStream: self._encodedData.stream() });
return [3 /*break*/, 6];
case 1:
if (!!common_1.NodeStream.isReadable({ readable: self._readableStream })) return [3 /*break*/, 6];
/** If the data stream for this `Record` instance has already been partially or fully
* consumed, then the data must be fetched again from either: */
_a = self;
if (!self._remoteOrigin) return [3 /*break*/, 3];
// A. ...a remote DWN if the record was originally queried from a remote DWN.
return [4 /*yield*/, self.readRecordData({ target: self._remoteOrigin, isRemote: true })];
case 2:
// A. ...a remote DWN if the record was originally queried from a remote DWN.
_b = _c.sent();
return [3 /*break*/, 5];
case 3:
// B. ...a local DWN if the record was originally queried from the local DWN.
return [4 /*yield*/, self.readRecordData({ target: self._connectedDid, isRemote: false })];
case 4:
// B. ...a local DWN if the record was originally queried from the local DWN.
_b = _c.sent();
_c.label = 5;
case 5:
/** If the data stream for this `Record` instance has already been partially or fully
* consumed, then the data must be fetched again from either: */
_a._readableStream = _b;
_c.label = 6;
case 6:
if (!self._readableStream) {
throw new Error('Record data is not available.');
}
return [2 /*return*/, self._readableStream];
}
});
});
},
/**
* Attaches callbacks for the resolution and/or rejection of the `Promise` returned by
* `stream()`.
*
* This method is a proxy to the `then` method of the `Promise` returned by `stream()`,
* allowing for a seamless integration with promise-based workflows.
* @param onFulfilled - A function to asynchronously execute when the `stream()` promise
* becomes fulfilled.
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` for the completion of which ever callback is executed.
*/
then: function (onFulfilled, onRejected) {
return this.stream().then(onFulfilled, onRejected);
},
/**
* Attaches a rejection handler callback to the `Promise` returned by the `stream()` method.
* This method is a shorthand for `.then(undefined, onRejected)`, specifically designed for handling
* rejection cases in the promise chain initiated by accessing the record's data. It ensures that
* errors during data retrieval or processing can be caught and handled appropriately.
*
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` that resolves to the value of the callback if it is called, or to its
* original fulfillment value if the promise is instead fulfilled.
*/
catch: function (onRejected) {
return this.stream().catch(onRejected);
}
};
return dataObj;
},
enumerable: false,
configurable: true
});
/**
* Stores the current record state as well as any initial write to the owner's DWN.
*
* @param importRecord - if true, the record will signed by the owner before storing it to the owner's DWN. Defaults to false.
* @returns the status of the store request
*
* @beta
*/
Record.prototype.store = function (importRecord) {
if (importRecord === void 0) { importRecord = false; }
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
// if we are importing the record we sign it as the owner
return [2 /*return*/, this.processRecord({ signAsOwner: importRecord, store: true })];
});
});
};
/**
* Signs the current record state as well as any initial write and optionally stores it to the owner's DWN.
* This is useful when importing a record that was signed by someone else into your own DWN.
*
* @param store - if true, the record will be stored to the owner's DWN after signing. Defaults to true.
* @returns the status of the import request
*
* @beta
*/
Record.prototype.import = function (store) {
if (store === void 0) { store = true; }
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.processRecord({ store: store, signAsOwner: true })];
});
});
};
/**
* Send the current record to a remote DWN by specifying their DID
* If no DID is specified, the target is assumed to be the owner (connectedDID).
* If an initial write is present and the Record class send cache has no awareness of it, the initial write is sent first
* (vs waiting for the regular DWN sync)
*
* @param target - the optional DID to send the record to, if none is set it is sent to the connectedDid
* @returns the status of the send record request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
Record.prototype.send = function (target) {
return __awaiter(this, void 0, void 0, function () {
var initialWrite, rawMessage, reply, _a, _b;
var _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
initialWrite = this._initialWrite;
target !== null && target !== void 0 ? target : (target = this._connectedDid);
if (!(initialWrite && !Record._sendCache.check(this._recordId, target))) return [3 /*break*/, 2];
rawMessage = __assign({}, initialWrite);
(0, common_1.removeUndefinedProperties)(rawMessage);
// Send the initial write to the target.
return [4 /*yield*/, this._agent.sendDwnRequest({
messageType: agent_2.DwnInterface.RecordsWrite,
author: this._connectedDid,
target: target,
rawMessage: rawMessage
})];
case 1:
// Send the initial write to the target.
_d.sent();
// Set the cache to maintain awareness that we don't need to send the initial write next time.
Record._sendCache.set(this._recordId, target);
_d.label = 2;
case 2:
_b = (_a = this._agent).sendDwnRequest;
_c = {
messageType: agent_2.DwnInterface.RecordsWrite,
author: this._connectedDid
};
return [4 /*yield*/, this.data.blob()];
case 3: return [4 /*yield*/, _b.apply(_a, [(_c.dataStream = _d.sent(),
_c.target = target,
_c.rawMessage = __assign({}, this.rawMessage),
_c)])];
case 4:
reply = (_d.sent()).reply;
return [2 /*return*/, reply];
}
});
});
};
/**
* Returns a JSON representation of the Record instance.
* It's called by `JSON.stringify(...)` automatically.
*/
Record.prototype.toJSON = function () {
return {
attestation: this.attestation,
author: this.author,
authorization: this.authorization,
contextId: this.contextId,
dataCid: this.dataCid,
dataFormat: this.dataFormat,
dataSize: this.dataSize,
dateCreated: this.dateCreated,
messageTimestamp: this.dateModified,
datePublished: this.datePublished,
encryption: this.encryption,
interface: this.interface,
method: this.method,
parentId: this.parentId,
protocol: this.protocol,
protocolPath: this.protocolPath,
protocolRole: this.protocolRole,
published: this.published,
recipient: this.recipient,
recordId: this.id,
schema: this.schema,
tags: this.tags,
};
};
/**
* Convenience method to return the string representation of the Record instance.
* Called automatically in string concatenation, String() type conversion, and template literals.
*/
Record.prototype.toString = function () {
var str = "Record: {\n";
str += " ID: ".concat(this.id, "\n");
str += this.contextId ? " Context ID: ".concat(this.contextId, "\n") : '';
str += this.protocol ? " Protocol: ".concat(this.protocol, "\n") : '';
str += this.schema ? " Schema: ".concat(this.schema, "\n") : '';
str += " Data CID: ".concat(this.dataCid, "\n");
str += " Data Format: ".concat(this.dataFormat, "\n");
str += " Data Size: ".concat(this.dataSize, "\n");
str += " Created: ".concat(this.dateCreated, "\n");
str += " Modified: ".concat(this.dateModified, "\n");
str += "}";
return str;
};
/**
* Returns a pagination cursor for the current record given a sort order.
*
* @param sort the sort order to use for the pagination cursor.
* @returns A promise that resolves to a pagination cursor for the current record.
*/
Record.prototype.paginationCursor = function (sort) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, (0, agent_1.getPaginationCursor)(this.rawMessage, sort)];
});
});
};
/**
* Update the current record on the DWN.
* @param params - Parameters to update the record.
* @returns the status of the update request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
Record.prototype.update = function (_a) {
var dateModified = _a.dateModified, data = _a.data, params = __rest(_a, ["dateModified", "data"]);
return __awaiter(this, void 0, void 0, function () {
var _b, parentId, descriptor, parentContextId, updateMessage, dataBlob, mutableDescriptorProperties, agentResponse, message, status, responseMessage;
var _this = this;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_b = this._descriptor, parentId = _b.parentId, descriptor = __rest(_b, ["parentId"]);
parentContextId = parentId ? this._contextId.split('/').slice(0, -1).join('/') : undefined;
updateMessage = __assign(__assign(__assign({}, descriptor), params), { parentContextId: parentContextId, messageTimestamp: dateModified, recordId: this._recordId });
// NOTE: The original Record's tags are copied to the update message, so that the tags are not lost.
// However if a user passes new tags in the `RecordUpdateParams` object, they will overwrite the original tags.
// If the updated tag object is empty or set to null, we remove the tags property to avoid schema validation errors in the DWN SDK.
if ((0, common_1.isEmptyObject)(updateMessage.tags) || updateMessage.tags === null) {
delete updateMessage.tags;
}
if (data !== undefined) {
// If `data` is being updated then `dataCid` and `dataSize` must be undefined and the `data`
// value must be converted to a Blob and later passed as a top-level property to
// `agent.processDwnRequest()`.
delete updateMessage.dataCid;
delete updateMessage.dataSize;
(dataBlob = (0, utils_js_1.dataToBlob)(data, updateMessage.dataFormat).dataBlob);
}
mutableDescriptorProperties = new Set(['data', 'dataCid', 'dataSize', 'datePublished', 'messageTimestamp', 'published', 'tags']);
Record.verifyPermittedMutation(Object.keys(params), mutableDescriptorProperties);
// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
// will throw an error if `published` is false but `datePublished` is set.
if (params.published === false && updateMessage.datePublished !== undefined) {
delete updateMessage.datePublished;
}
return [4 /*yield*/, this._agent.processDwnRequest({
author: this._connectedDid,
dataStream: dataBlob,
messageParams: __assign({}, updateMessage),
messageType: agent_2.DwnInterface.RecordsWrite,
target: this._connectedDid,
})];
case 1:
agentResponse = _c.sent();
message = agentResponse.message, status = agentResponse.reply.status;
responseMessage = message;
if (200 <= status.code && status.code <= 299) {
// copy the original raw message to the initial write before we update the values.
if (!this._initialWrite) {
this._initialWrite = __assign({}, this.rawMessage);
}
// Only update the local Record instance mutable properties if the record was successfully (over)written.
this._authorization = responseMessage.authorization;
this._protocolRole = params.protocolRole;
mutableDescriptorProperties.forEach(function (property) {
_this._descriptor[property] = responseMessage.descriptor[property];
});
// Cache data.
if (data !== undefined) {
this._encodedData = dataBlob;
}
}
return [2 /*return*/, { status: status }];
}
});
});
};
/**
* Handles the various conditions around there being an initial write, whether to store initial/current state,
* and whether to add an owner signature to the initial write to enable storage when protocol rules require it.
*/
Record.prototype.processRecord = function (_a) {
var store = _a.store, signAsOwner = _a.signAsOwner;
return __awaiter(this, void 0, void 0, function () {
var initialWriteRequest, agentResponse_1, message_1, status_1, responseMessage_1, requestOptions, agentResponse, message, status, responseMessage;
var _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!(this._initialWrite && ((signAsOwner && !this._initialWriteSigned) || (store && !this._initialWriteStored)))) return [3 /*break*/, 2];
initialWriteRequest = {
messageType: agent_2.DwnInterface.RecordsWrite,
rawMessage: this.initialWrite,
author: this._connectedDid,
target: this._connectedDid,
signAsOwner: signAsOwner,
store: store,
};
return [4 /*yield*/, this._agent.processDwnRequest(initialWriteRequest)];
case 1:
agentResponse_1 = _c.sent();
message_1 = agentResponse_1.message, status_1 = agentResponse_1.reply.status;
responseMessage_1 = message_1;
// If we are signing as owner, make sure to update the initial write's authorization, because now it will have the owner's signature on it
// set the stored or signed status to true so we don't process it again.
if (200 <= status_1.code && status_1.code <= 299) {
if (store)
this._initialWriteStored = true;
if (signAsOwner) {
this._initialWriteSigned = true;
this.initialWrite.authorization = responseMessage_1.authorization;
}
}
_c.label = 2;
case 2:
_b = {
messageType: agent_2.DwnInterface.RecordsWrite,
rawMessage: this.rawMessage,
author: this._connectedDid,
target: this._connectedDid
};
return [4 /*yield*/, this.data.blob()];
case 3:
requestOptions = (_b.dataStream = _c.sent(),
_b.signAsOwner = signAsOwner,
_b.store = store,
_b);
return [4 /*yield*/, this._agent.processDwnRequest(requestOptions)];
case 4:
agentResponse = _c.sent();
message = agentResponse.message, status = agentResponse.reply.status;
responseMessage = message;
if (200 <= status.code && status.code <= 299) {
// If we are signing as the owner, make sure to update the current record state's authorization, because now it will have the owner's signature on it.
if (signAsOwner)
this._authorization = responseMessage.authorization;
}
return [2 /*return*/, { status: status }];
}
});
});
};
/**
* Fetches the record's data from the specified DWN.
*
* This private method is called when the record data is not available in-memory
* and needs to be fetched from either a local or a remote DWN.
* It makes a read request to the specified DWN and processes the response to provide
* a Node.js `Readable` stream of the record's data.
*
* @param params - Parameters for fetching the record's data.
* @param params.target - The DID of the DWN to fetch the data from.
* @param params.isRemote - Indicates whether the target DWN is a remote node.
* @returns A Promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If there is an error while fetching or processing the data from the DWN.
*
* @beta
*/
Record.prototype.readRecordData = function (_a) {
var target = _a.target, isRemote = _a.isRemote;
return __awaiter(this, void 0, void 0, function () {
var readRequest, agentResponsePromise, record, dataStream, nodeReadable, error_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
readRequest = {
author: this._connectedDid,
messageParams: { filter: { recordId: this.id } },
messageType: agent_2.DwnInterface.RecordsRead,
target: target,
};
agentResponsePromise = isRemote ?
this._agent.sendDwnRequest(readRequest) :
this._agent.processDwnRequest(readRequest);
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, agentResponsePromise];
case 2:
record = (_b.sent()).reply.record;
dataStream = record.data;
nodeReadable = common_1.Stream.isReadableStream(dataStream) ?
common_1.NodeStream.fromWebReadable({ readableStream: dataStream }) :
dataStream;
return [2 /*return*/, nodeReadable];
case 3:
error_1 = _b.sent();
throw new Error("Error encountered while attempting to read data: ".concat(error_1.message));
case 4: return [2 /*return*/];
}
});
});
};
/**
* Verifies if the properties to be mutated are mutable.
*
* This private method is used to ensure that only mutable properties of the `Record` instance
* are being changed. It checks whether the properties specified for mutation are among the
* set of properties that are allowed to be modified. If any of the properties to be mutated
* are not in the set of mutable properties, the method throws an error.
*
* @param propertiesToMutate - An iterable of property names that are intended to be mutated.
* @param mutableDescriptorProperties - A set of property names that are allowed to be mutated.
*
* @throws If any of the properties in `propertiesToMutate` are not in `mutableDescriptorProperties`.
*
* @beta
*/
Record.verifyPermittedMutation = function (propertiesToMutate, mutableDescriptorProperties) {
var e_1, _a;
try {
for (var propertiesToMutate_1 = __values(propertiesToMutate), propertiesToMutate_1_1 = propertiesToMutate_1.next(); !propertiesToMutate_1_1.done; propertiesToMutate_1_1 = propertiesToMutate_1.next()) {
var property = propertiesToMutate_1_1.value;
if (!mutableDescriptorProperties.has(property)) {
throw new Error("".concat(property, " is an immutable property. Its value cannot be changed."));
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (propertiesToMutate_1_1 && !propertiesToMutate_1_1.done && (_a = propertiesToMutate_1.return)) _a.call(propertiesToMutate_1);
}
finally { if (e_1) throw e_1.error; }
}
};
/**
* Cache to minimize the amount of redundant two-phase commits we do in store() and send()
* Retains awareness of the last 100 records stored/sent for up to 100 target DIDs each.
*/
Record._sendCache = utils_js_1.SendCache;
return Record;
}());
//# sourceMappingURL=record.js.map
File diff suppressed because one or more lines are too long
+153
View File
@@ -0,0 +1,153 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
var dids_1 = require("@web5/dids");
var workerSelf = self;
var DidResolver = new dids_1.UniversalResolver({ didResolvers: [dids_1.DidDht, dids_1.DidWeb] });
var didUrlRegex = /^https?:\/\/dweb\/(([^/]+)\/.*)?$/;
var httpToHttpsRegex = /^http:/;
var trailingSlashRegex = /\/$/;
workerSelf.addEventListener('fetch', function (event) {
var match = event.request.url.match(didUrlRegex);
if (match) {
event.respondWith((function () { return __awaiter(void 0, void 0, void 0, function () {
var normalizedUrl, cachedResponse;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
normalizedUrl = event.request.url.replace(httpToHttpsRegex, 'https:').replace(trailingSlashRegex, '');
return [4 /*yield*/, caches.open('drl').then(function (cache) { return cache.match(normalizedUrl); })];
case 1:
cachedResponse = _a.sent();
return [2 /*return*/, cachedResponse || handleEvent(event, match[2], match[1])];
}
});
}); })());
}
});
function handleEvent(event, did, route) {
return __awaiter(this, void 0, void 0, function () {
var result, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
return [4 /*yield*/, DidResolver.resolve(did)];
case 1:
result = _a.sent();
return [4 /*yield*/, fetchResource(event, result.didDocument, route)];
case 2: return [2 /*return*/, _a.sent()];
case 3:
error_1 = _a.sent();
if (error_1 instanceof Response) {
return [2 /*return*/, error_1];
}
console.log("Error in DID URL fetch: ".concat(error_1));
return [2 /*return*/, new Response('DID URL fetch error', { status: 500 })];
case 4: return [2 /*return*/];
}
});
});
}
function fetchResource(event, ddo, route) {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var endpoints, endpoints_1, endpoints_1_1, endpoint, response, error_2, e_1_1;
var e_1, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
endpoints = (_b = (_a = ddo === null || ddo === void 0 ? void 0 : ddo.service) === null || _a === void 0 ? void 0 : _a.find(function (service) { return service.type === 'DecentralizedWebNode'; })) === null || _b === void 0 ? void 0 : _b.serviceEndpoint;
endpoints = (Array.isArray(endpoints) ? endpoints : [endpoints]).filter(function (url) { return url.startsWith('http'); });
if (!(endpoints === null || endpoints === void 0 ? void 0 : endpoints.length)) {
throw new Response('DWeb Node resolution failed: no valid endpoints found.', { status: 530 });
}
_d.label = 1;
case 1:
_d.trys.push([1, 8, 9, 10]);
endpoints_1 = __values(endpoints), endpoints_1_1 = endpoints_1.next();
_d.label = 2;
case 2:
if (!!endpoints_1_1.done) return [3 /*break*/, 7];
endpoint = endpoints_1_1.value;
_d.label = 3;
case 3:
_d.trys.push([3, 5, , 6]);
return [4 /*yield*/, fetch("".concat(endpoint.replace(trailingSlashRegex, ''), "/").concat(route), { headers: event.request.headers })];
case 4:
response = _d.sent();
if (response.ok) {
return [2 /*return*/, response];
}
console.log("DWN endpoint error: ".concat(response.status));
return [2 /*return*/, new Response('DWeb Node request failed', { status: response.status })];
case 5:
error_2 = _d.sent();
console.log("DWN endpoint error: ".concat(error_2));
return [2 /*return*/, new Response('DWeb Node request failed: ' + error_2, { status: 500 })];
case 6:
endpoints_1_1 = endpoints_1.next();
return [3 /*break*/, 2];
case 7: return [3 /*break*/, 10];
case 8:
e_1_1 = _d.sent();
e_1 = { error: e_1_1 };
return [3 /*break*/, 10];
case 9:
try {
if (endpoints_1_1 && !endpoints_1_1.done && (_c = endpoints_1.return)) _c.call(endpoints_1);
}
finally { if (e_1) throw e_1.error; }
return [7 /*endfinally*/];
case 10: return [2 /*return*/];
}
});
});
}
//# sourceMappingURL=service-worker.js.map
@@ -0,0 +1 @@
{"version":3,"file":"service-worker.js","sourceRoot":"","sources":["../../src/service-worker.ts"],"names":[],"mappings":"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"}
+125
View File
@@ -0,0 +1,125 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTechPreviewDwnEndpoints = void 0;
var dids_1 = require("@web5/dids");
/**
* Dynamically selects up to 2 DWN endpoints that are provided
* by default during the Tech Preview period.
*
* @beta
*/
function getTechPreviewDwnEndpoints() {
return __awaiter(this, void 0, void 0, function () {
var response, error_1, didDocument, _a, dwnService, techPreviewEndpoints, dwnUrls, numNodesToAllocate, attempts, nodeIdx, dwnUrl, healthCheck, error_2;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, fetch('https://dwn.tbddev.org/.well-known/did.json')];
case 1:
response = _b.sent();
if (!response.ok) {
throw new Error("HTTP Error: ".concat(response.status, " ").concat(response.statusText));
}
return [3 /*break*/, 3];
case 2:
error_1 = _b.sent();
console.warn('failed to get tech preview dwn endpoints:', error_1.message);
return [2 /*return*/, []];
case 3: return [4 /*yield*/, response.json()];
case 4:
didDocument = _b.sent();
_a = __read(dids_1.utils.getServices({ didDocument: didDocument, id: '#dwn', type: 'DecentralizedWebNode' }), 1), dwnService = _a[0];
techPreviewEndpoints = new Set();
if (!('serviceEndpoint' in dwnService
&& !Array.isArray(dwnService.serviceEndpoint)
&& typeof dwnService.serviceEndpoint !== 'string'
&& Array.isArray(dwnService.serviceEndpoint.nodes))) return [3 /*break*/, 10];
dwnUrls = dwnService.serviceEndpoint.nodes;
numNodesToAllocate = Math.min(dwnUrls.length, 2);
attempts = 0;
_b.label = 5;
case 5:
if (!(attempts < dwnUrls.length && techPreviewEndpoints.size < numNodesToAllocate)) return [3 /*break*/, 10];
nodeIdx = getRandomInt(0, dwnUrls.length);
dwnUrl = dwnUrls[nodeIdx];
_b.label = 6;
case 6:
_b.trys.push([6, 8, , 9]);
return [4 /*yield*/, fetch("".concat(dwnUrl, "/health"))];
case 7:
healthCheck = _b.sent();
if (healthCheck.ok) {
techPreviewEndpoints.add(dwnUrl);
}
return [3 /*break*/, 9];
case 8:
error_2 = _b.sent();
return [3 /*break*/, 9];
case 9:
attempts += 1;
return [3 /*break*/, 5];
case 10: return [2 /*return*/, Array.from(techPreviewEndpoints)];
}
});
});
}
exports.getTechPreviewDwnEndpoints = getTechPreviewDwnEndpoints;
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
//# sourceMappingURL=tech-preview.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"tech-preview.js","sourceRoot":"","sources":["../../src/tech-preview.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mCAA+C;AAE/C;;;;;GAKG;AACH,SAAsB,0BAA0B;;;;;;;oBAGjC,qBAAM,KAAK,CAAC,6CAA6C,CAAC,EAAA;;oBAArE,QAAQ,GAAG,SAA0D,CAAC;oBACtE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;wBAChB,MAAM,IAAI,KAAK,CAAC,sBAAe,QAAQ,CAAC,MAAM,cAAI,QAAQ,CAAC,UAAU,CAAE,CAAC,CAAC;qBAC1E;;;;oBAED,OAAO,CAAC,IAAI,CAAC,2CAA2C,EAAE,OAAK,CAAC,OAAO,CAAC,CAAC;oBACzE,sBAAO,EAAE,EAAC;wBAGQ,qBAAM,QAAQ,CAAC,IAAI,EAAE,EAAA;;oBAAnC,WAAW,GAAG,SAAqB;oBACnC,KAAA,OAAiB,YAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,aAAA,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,IAAA,EAA9F,UAAU,QAAA,CAAqF;oBAGjG,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAC;yBAE3C,CAAA,iBAAiB,IAAI,UAAU;2BAC5B,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC;2BAC1C,OAAO,UAAU,CAAC,eAAe,KAAK,QAAQ;2BAC9C,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA,EAHlD,yBAGkD;oBAC9C,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC;oBAE3C,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;oBAE9C,QAAQ,GAAG,CAAC;;;yBAAE,CAAA,QAAQ,GAAG,OAAO,CAAC,MAAM,IAAI,oBAAoB,CAAC,IAAI,GAAG,kBAAkB,CAAA;oBAC1F,OAAO,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;oBAC1C,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;;;;oBAGV,qBAAM,KAAK,CAAC,UAAG,MAAM,YAAS,CAAC,EAAA;;oBAA7C,WAAW,GAAG,SAA+B;oBACnD,IAAI,WAAW,CAAC,EAAE,EAAE;wBAClB,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;qBAClC;;;;;;oBAR+F,QAAQ,IAAI,CAAC,CAAA;;yBAenH,sBAAO,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAC;;;;CACzC;AA1CD,gEA0CC;AAED,SAAS,YAAY,CAAC,GAAG,EAAE,GAAG;IAC5B,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC"}
+127
View File
@@ -0,0 +1,127 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SendCache = exports.dataToBlob = void 0;
var common_1 = require("@web5/common");
/**
* Converts various data types to a `Blob` object, automatically detecting the data type or using
* the specified `dataFormat` to set the Blob's MIME type.
*
* This function supports plain text, JSON objects, binary data (Uint8Array, ArrayBuffer), and Blob
* inputs and will attempt to automatically detect the type of the data if `dataFormat` is not
* explicitly provided.
*
* @beta
*
* @example
* ```ts
* // Convert a JSON object to a Blob
* const { dataBlob, dataFormat } = dataToBlob({ key: 'value' }, 'application/json');
*
* // Convert a plain text string to a Blob without specifying dataFormat
* const { dataBlob: textBlob } = dataToBlob('Hello, world!');
*
* // Convert binary data to a Blob
* const binaryData = new Uint8Array([0, 1, 2, 3]);
* const { dataBlob: binaryBlob } = dataToBlob(binaryData);
* ```
*
* @param data - The data to be converted into a `Blob`. This can be a string, an object, binary
* data (Uint8Array or ArrayBuffer), or a Blob.
* @param dataFormat - An optional MIME type string that specifies the format of the data. Common
* types include 'text/plain' for string data, 'application/json' for JSON
* objects, and 'application/octet-stream' for binary data. If not provided, the
* function will attempt to detect the format based on the data type or default
* to 'application/octet-stream'.
* @returns An object containing the `dataBlob`, a Blob representation of the input data, and
* `dataFormat`, the MIME type of the data as determined by the function or specified by the caller.
* @throws An error if the data type is not supported or cannot be converted to a Blob.
*/
function dataToBlob(data, dataFormat) {
var dataBlob;
// Check for Object or String, and if neither, assume bytes.
var detectedType = (0, common_1.universalTypeOf)(data);
if (dataFormat === 'text/plain' || detectedType === 'String') {
dataBlob = new Blob([data], { type: 'text/plain' });
}
else if (dataFormat === 'application/json' || detectedType === 'Object') {
var dataBytes = common_1.Convert.object(data).toUint8Array();
dataBlob = new Blob([dataBytes], { type: 'application/json' });
}
else if (detectedType === 'Uint8Array' || detectedType === 'ArrayBuffer') {
dataBlob = new Blob([data], { type: 'application/octet-stream' });
}
else if (detectedType === 'Blob') {
dataBlob = data;
}
else {
throw new Error('data type not supported.');
}
dataFormat = dataFormat || dataBlob.type || 'application/octet-stream';
return { dataBlob: dataBlob, dataFormat: dataFormat };
}
exports.dataToBlob = dataToBlob;
/**
* The `SendCache` class provides a static caching mechanism to optimize the process of sending
* records to remote DWN targets by minimizing redundant sends.
*
* It maintains a cache of record IDs and their associated target DIDs to which they have been sent.
* This helps in avoiding unnecessary network requests and ensures efficient data synchronization
* across Decentralized Web Nodes (DWNs).
*
* The cache employs a simple eviction policy to maintain a manageable size, ensuring that the cache
* does not grow indefinitely and consume excessive memory resources.
*
* @beta
*/
var SendCache = exports.SendCache = /** @class */ (function () {
function SendCache() {
}
/**
* Checks if a given record ID has been sent to a specified target DID. This method is used to
* determine whether a send operation is necessary or if it can be skipped to avoid redundancy.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to check against.
* @returns A boolean indicating whether the record has been sent to the target.
*/
SendCache.check = function (id, target) {
var targetCache = SendCache.cache.get(id);
return targetCache ? targetCache.has(target) : false;
};
/**
* Adds or updates an entry in the cache for a given record ID and target DID. If the cache
* exceeds its size limit, the oldest entry is removed. This method ensures that the cache
* reflects the most recent sends.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to which the record has been sent.
*/
SendCache.set = function (id, target) {
var targetCache = SendCache.cache.get(id) || new Set();
SendCache.cache.delete(id);
SendCache.cache.set(id, targetCache);
if (this.cache.size > SendCache.sendCacheLimit) {
var firstRecord = SendCache.cache.keys().next().value;
SendCache.cache.delete(firstRecord);
}
targetCache.delete(target);
targetCache.add(target);
if (targetCache.size > SendCache.sendCacheLimit) {
var firstTarget = targetCache.keys().next().value;
targetCache.delete(firstTarget);
}
};
/**
* A private static map that serves as the core storage mechanism for the cache. It maps record
* IDs to a set of target DIDs, indicating which records have been sent to which targets.
*/
SendCache.cache = new Map();
/**
* The maximum number of entries allowed in the cache. Once this limit is exceeded, the oldest
* entries are evicted to make room for new ones. This limit applies both to the number of records
* and the number of targets per record.
*/
SendCache.sendCacheLimit = 100;
return SendCache;
}());
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
+64
View File
@@ -0,0 +1,64 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VcApi = void 0;
/**
* The VC API is used to issue, present and verify VCs
*
* @beta
*/
var VcApi = /** @class */ (function () {
function VcApi(options) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* Issues a VC (Not implemented yet)
*/
VcApi.prototype.create = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
// TODO: implement
throw new Error('Not implemented.');
});
});
};
return VcApi;
}());
exports.VcApi = VcApi;
//# sourceMappingURL=vc-api.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"vc-api.js","sourceRoot":"","sources":["../../src/vc-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA;;;;GAIG;AACH;IAUE,eAAY,OAAmD;QAC7D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC3C,CAAC;IAED;;OAEG;IACG,sBAAM,GAAZ;;;gBACE,kBAAkB;gBAClB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;;;KACrC;IACH,YAAC;AAAD,CAAC,AAtBD,IAsBC;AAtBY,sBAAK"}
+32
View File
@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.installNetworkingFeatures = void 0;
/**
* Installs the DWeb networking features in the current environment.
*/
function installNetworkingFeatures(path) {
var _a;
var workerSelf = self;
try {
if (typeof ServiceWorkerGlobalScope !== 'undefined' && workerSelf instanceof ServiceWorkerGlobalScope) {
// Dynamically import service worker code only if we're in a Service Worker context
import('./service-worker.js').catch(function (error) {
console.error('Error loading service worker module:', error);
});
}
else if ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.navigator) === null || _a === void 0 ? void 0 : _a.serviceWorker) {
if (path)
navigator.serviceWorker.register(path).catch(function (error) {
console.error('DWeb networking feature installation failed: ', error);
});
}
else {
throw new Error('DWeb networking features are not available for install in this environment');
}
}
catch (error) {
console.error('Error in installing networking features:', error);
}
}
exports.installNetworkingFeatures = installNetworkingFeatures;
//# sourceMappingURL=web-features.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"web-features.js","sourceRoot":"","sources":["../../src/web-features.ts"],"names":[],"mappings":";;;AAGA;;GAEG;AACH,SAAgB,yBAAyB,CAAC,IAAY;;IACpD,IAAM,UAAU,GAAG,IAAW,CAAC;IAE/B,IAAI;QACF,IAAI,OAAO,wBAAwB,KAAK,WAAW,IAAI,UAAU,YAAY,wBAAwB,EAAE;YACrG,mFAAmF;YACnF,MAAM,CAAC,qBAAqB,CAAC,CAAC,KAAK,CAAC,UAAA,KAAK;gBACvC,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;YAC/D,CAAC,CAAC,CAAC;SACJ;aACI,IAAI,MAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,SAAS,0CAAE,aAAa,EAAE;YAC7C,IAAI,IAAI;gBAAE,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,UAAA,KAAK;oBAC1D,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;gBACxE,CAAC,CAAC,CAAC;SACJ;aACI;YACH,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;SAC/F;KACF;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;KAClE;AACH,CAAC;AArBD,8DAqBC"}
+193
View File
@@ -0,0 +1,193 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Web5 = void 0;
var user_agent_1 = require("@web5/user-agent");
var vc_api_js_1 = require("./vc-api.js");
var dwn_api_js_1 = require("./dwn-api.js");
var did_api_js_1 = require("./did-api.js");
var tech_preview_js_1 = require("./tech-preview.js");
/**
* The main Web5 API interface. It manages the creation of a DID if needed, the connection to the
* local DWN and all the web5 main foundational APIs such as VC, syncing, etc.
*/
var Web5 = /** @class */ (function () {
function Web5(_a) {
var agent = _a.agent, connectedDid = _a.connectedDid;
this.agent = agent;
this.connectedDid = connectedDid;
this.did = new did_api_js_1.DidApi({ agent: agent, connectedDid: connectedDid });
this.dwn = new dwn_api_js_1.DwnApi({ agent: agent, connectedDid: connectedDid });
this.vc = new vc_api_js_1.VcApi({ agent: agent, connectedDid: connectedDid });
}
/**
* Connects to a {@link Web5Agent}. Defaults to creating a local {@link Web5UserAgent} if one
* isn't provided.
*
* @param options - Optional overrides that can be provided when calling {@link Web5.connect}.
* @returns A promise that resolves to a {@link Web5} instance and the connected DID.
*/
Web5.connect = function (_a) {
var _b;
var _c = _a === void 0 ? {} : _a, agent = _c.agent, agentVault = _c.agentVault, connectedDid = _c.connectedDid, password = _c.password, recoveryPhrase = _c.recoveryPhrase, sync = _c.sync, techPreview = _c.techPreview;
return __awaiter(this, void 0, void 0, function () {
var userAgent, notConnected, identity, identities, existingIdentityCount, serviceEndpointNodes, _d, _e, _f, web5;
var _g;
return __generator(this, function (_h) {
switch (_h.label) {
case 0:
if (!(agent === undefined)) return [3 /*break*/, 17];
return [4 /*yield*/, user_agent_1.Web5UserAgent.create({ agentVault: agentVault })];
case 1:
userAgent = _h.sent();
agent = userAgent;
// Warn the developer and application user of the security risks of using a static password.
if (password === undefined) {
password = 'insecure-static-phrase';
console.warn('%cSECURITY WARNING:%c ' +
'You have not set a password, which defaults to a static, guessable value. ' +
'This significantly compromises the security of your data. ' +
'Please configure a secure, unique password.', 'font-weight: bold; color: red;', 'font-weight: normal; color: inherit;');
}
return [4 /*yield*/, userAgent.firstLaunch()];
case 2:
if (!_h.sent()) return [3 /*break*/, 4];
return [4 /*yield*/, userAgent.initialize({ password: password, recoveryPhrase: recoveryPhrase })];
case 3:
recoveryPhrase = _h.sent();
_h.label = 4;
case 4: return [4 /*yield*/, userAgent.start({ password: password })];
case 5:
_h.sent();
notConnected = true;
if (!notConnected) return [3 /*break*/, 15];
identity = void 0;
return [4 /*yield*/, userAgent.identity.list()];
case 6:
identities = _h.sent();
existingIdentityCount = identities.length;
if (!(existingIdentityCount === 0)) return [3 /*break*/, 13];
if (!((_b = techPreview === null || techPreview === void 0 ? void 0 : techPreview.dwnEndpoints) !== null && _b !== void 0)) return [3 /*break*/, 7];
_d = _b;
return [3 /*break*/, 9];
case 7: return [4 /*yield*/, (0, tech_preview_js_1.getTechPreviewDwnEndpoints)()];
case 8:
_d = _h.sent();
_h.label = 9;
case 9:
serviceEndpointNodes = _d;
return [4 /*yield*/, userAgent.identity.create({
didMethod: 'dht',
metadata: { name: 'Default' },
didOptions: {
services: [
{
id: 'dwn',
type: 'DecentralizedWebNode',
serviceEndpoint: serviceEndpointNodes,
enc: '#enc',
sig: '#sig',
}
],
verificationMethods: [
{
algorithm: 'Ed25519',
id: 'sig',
purposes: ['assertionMethod', 'authentication']
},
{
algorithm: 'secp256k1',
id: 'enc',
purposes: ['keyAgreement']
}
]
}
})];
case 10:
// Generate a new Identity for the end-user.
identity = _h.sent();
_f = (_e = userAgent.identity).manage;
_g = {};
return [4 /*yield*/, identity.export()];
case 11:
// The User Agent will manage the Identity, which ensures it will be available on future
// sessions.
return [4 /*yield*/, _f.apply(_e, [(_g.portableIdentity = _h.sent(), _g)])];
case 12:
// The User Agent will manage the Identity, which ensures it will be available on future
// sessions.
_h.sent();
return [3 /*break*/, 14];
case 13:
if (existingIdentityCount === 1) {
// An existing identity was found in the User Agent's tenant.
identity = identities[0];
}
else {
throw new Error("connect() failed due to unexpected state: Expected 1 but found ".concat(existingIdentityCount, " stored identities."));
}
_h.label = 14;
case 14:
// Set the stored identity as the connected DID.
connectedDid = identity.did.uri;
_h.label = 15;
case 15:
if (!(sync !== 'off')) return [3 /*break*/, 17];
// First, register the user identity for sync.
return [4 /*yield*/, userAgent.sync.registerIdentity({ did: connectedDid })];
case 16:
// First, register the user identity for sync.
_h.sent();
// Enable sync using the specified interval or default.
sync !== null && sync !== void 0 ? sync : (sync = '2m');
userAgent.sync.startSync({ interval: sync })
.catch(function (error) {
console.error("Sync failed: ".concat(error));
});
_h.label = 17;
case 17:
web5 = new Web5({ agent: agent, connectedDid: connectedDid });
return [2 /*return*/, { web5: web5, did: connectedDid, recoveryPhrase: recoveryPhrase }];
}
});
});
};
return Web5;
}());
exports.Web5 = Web5;
//# sourceMappingURL=web5.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"web5.js","sourceRoot":"","sources":["../../src/web5.ts"],"names":[],"mappings":"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"}
+69
View File
@@ -0,0 +1,69 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { DidInterface } from '@web5/agent';
/**
* The DID API is used to resolve DIDs.
*
* @beta
*/
export class DidApi {
constructor(options) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* Initiates the creation of a Decentralized Identifier (DID) using the specified method, options,
* and storage preference.
*
* This method sends a request to the Web5 Agent to create a new DID based on the provided method,
* with method-specific options. It also specifies whether the newly created DID should be stored.
*
* @param request - The request parameters for creating a DID, including the method, options, and
* storage flag.
* @returns A promise that resolves to a `DidCreateResponse`, which includes the operation's
* status and, if successful, the newly created DID.
*/
create(request) {
return __awaiter(this, void 0, void 0, function* () {
const _a = yield this.agent.processDidRequest({
messageType: DidInterface.Create,
messageParams: Object.assign({}, request)
}), { result } = _a, status = __rest(_a, ["result"]);
return Object.assign({ did: result }, status);
});
}
/**
* Resolves a DID to a DID Resolution Result.
*
* @param didUri - The DID or DID URL to resolve.
* @returns A promise that resolves to the DID Resolution Result.
*/
resolve(didUri, options) {
return __awaiter(this, void 0, void 0, function* () {
const { result: didResolutionResult } = yield this.agent.processDidRequest({
messageParams: { didUri, options },
messageType: DidInterface.Resolve
});
return didResolutionResult;
});
}
}
//# sourceMappingURL=did-api.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"did-api.js","sourceRoot":"","sources":["../../src/did-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8B3C;;;;GAIG;AACH,MAAM,OAAO,MAAM;IAUjB,YAAY,OAAmD;QAC7D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;OAWG;IACU,MAAM,CAAC,OAAyB;;YAC3C,MAAM,KAAwB,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;gBAC/D,WAAW,EAAK,YAAY,CAAC,MAAM;gBACnC,aAAa,oBAAQ,OAAO,CAAE;aAC/B,CAAC,EAHI,EAAE,MAAM,OAGZ,EAHiB,MAAM,cAAnB,UAAqB,CAGzB,CAAC;YAEH,uBAAS,GAAG,EAAE,MAAM,IAAK,MAAM,EAAG;QACpC,CAAC;KAAA;IAED;;;;;OAKG;IACU,OAAO,CAClB,MAAkC,EAAE,OAAqC;;YAEzE,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;gBACzE,aAAa,EAAG,EAAE,MAAM,EAAE,OAAO,EAAE;gBACnC,WAAW,EAAK,YAAY,CAAC,OAAO;aACrC,CAAC,CAAC;YAEH,OAAO,mBAAmB,CAAC;QAC7B,CAAC;KAAA;CACF"}
+298
View File
@@ -0,0 +1,298 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { isEmptyObject } from '@web5/common';
import { DwnInterface, getRecordAuthor } from '@web5/agent';
import { Record } from './record.js';
import { dataToBlob } from './utils.js';
import { Protocol } from './protocol.js';
/**
* Interface to interact with DWN Records and Protocols
*/
export class DwnApi {
constructor(options) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* API to interact with DWN protocols (e.g., `dwn.protocols.configure()`).
*/
get protocols() {
return {
/**
* Configure method, used to setup a new protocol (or update) with the passed definitions
*/
configure: (request) => __awaiter(this, void 0, void 0, function* () {
const agentResponse = yield this.agent.processDwnRequest({
author: this.connectedDid,
messageParams: request.message,
messageType: DwnInterface.ProtocolsConfigure,
target: this.connectedDid
});
const { message, messageCid, reply: { status } } = agentResponse;
const response = { status };
if (status.code < 300) {
const metadata = { author: this.connectedDid, messageCid };
response.protocol = new Protocol(this.agent, message, metadata);
}
return response;
}),
/**
* Query the available protocols
*/
query: (request) => __awaiter(this, void 0, void 0, function* () {
const agentRequest = {
author: this.connectedDid,
messageParams: request.message,
messageType: DwnInterface.ProtocolsQuery,
target: request.from || this.connectedDid
};
let agentResponse;
if (request.from) {
agentResponse = yield this.agent.sendDwnRequest(agentRequest);
}
else {
agentResponse = yield this.agent.processDwnRequest(agentRequest);
}
const reply = agentResponse.reply;
const { entries = [], status } = reply;
const protocols = entries.map((entry) => {
const metadata = { author: this.connectedDid };
return new Protocol(this.agent, entry, metadata);
});
return { protocols, status };
})
};
}
/**
* API to interact with DWN records (e.g., `dwn.records.create()`).
*/
get records() {
return {
/**
* Alias for the `write` method
*/
create: (request) => __awaiter(this, void 0, void 0, function* () {
return this.records.write(request);
}),
/**
* Write a record based on an existing one (useful for updating an existing record)
*/
createFrom: (request) => __awaiter(this, void 0, void 0, function* () {
var _a;
const _b = request.record.toJSON(), { author: inheritedAuthor } = _b, inheritedProperties = __rest(_b, ["author"]);
// If `data` is being updated then `dataCid` and `dataSize` must not be present.
if (request.data !== undefined) {
delete inheritedProperties.dataCid;
delete inheritedProperties.dataSize;
}
// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
// will throw an error if `published` is false but `datePublished` is set.
if (((_a = request.message) === null || _a === void 0 ? void 0 : _a.published) === false && inheritedProperties.datePublished !== undefined) {
delete inheritedProperties.datePublished;
delete inheritedProperties.published;
}
// If the request changes the `author` or message `descriptor` then the deterministic `recordId` will change.
// As a result, we will discard the `recordId` if either of these changes occur.
if (!isEmptyObject(request.message) || (request.author && request.author !== inheritedAuthor)) {
delete inheritedProperties.recordId;
}
return this.records.write({
data: request.data,
message: Object.assign(Object.assign({}, inheritedProperties), request.message),
});
}),
/**
* Delete a record
*/
delete: (request) => __awaiter(this, void 0, void 0, function* () {
const agentRequest = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author: this.connectedDid,
messageParams: request.message,
messageType: DwnInterface.RecordsDelete,
/**
* The `target` is the DID of the DWN tenant under which the delete will be executed.
* If `from` is provided, the delete operation will be executed on a remote DWN.
* Otherwise, the record will be deleted on the local DWN.
*/
target: request.from || this.connectedDid
};
let agentResponse;
if (request.from) {
agentResponse = yield this.agent.sendDwnRequest(agentRequest);
}
else {
agentResponse = yield this.agent.processDwnRequest(agentRequest);
}
const { reply: { status } } = agentResponse;
return { status };
}),
/**
* Query a single or multiple records based on the given filter
*/
query: (request) => __awaiter(this, void 0, void 0, function* () {
const agentRequest = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author: this.connectedDid,
messageParams: request.message,
messageType: DwnInterface.RecordsQuery,
/**
* The `target` is the DID of the DWN tenant under which the query will be executed.
* If `from` is provided, the query operation will be executed on a remote DWN.
* Otherwise, the local DWN will be queried.
*/
target: request.from || this.connectedDid
};
let agentResponse;
if (request.from) {
agentResponse = yield this.agent.sendDwnRequest(agentRequest);
}
else {
agentResponse = yield this.agent.processDwnRequest(agentRequest);
}
const reply = agentResponse.reply;
const { entries, status, cursor } = reply;
const records = entries.map((entry) => {
const recordOptions = Object.assign({
/**
* Extract the `author` DID from the record entry since records may be signed by the
* tenant owner or any other entity.
*/
author: getRecordAuthor(entry),
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN even if the record was returned by a query of a remote DWN.
*/
connectedDid: this.connectedDid,
/**
* If the record was returned by a query of a remote DWN, set the `remoteOrigin` to
* the DID of the DWN that returned the record. The `remoteOrigin` property will be used
* to determine which DWN to send subsequent read requests to in the event the data
* payload exceeds the threshold for being returned with queries.
*/
remoteOrigin: request.from }, entry);
const record = new Record(this.agent, recordOptions);
return record;
});
return { records, status, cursor };
}),
/**
* Read a single record based on the given filter
*/
read: (request) => __awaiter(this, void 0, void 0, function* () {
const agentRequest = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author: this.connectedDid,
messageParams: request.message,
messageType: DwnInterface.RecordsRead,
/**
* The `target` is the DID of the DWN tenant under which the read will be executed.
* If `from` is provided, the read operation will be executed on a remote DWN.
* Otherwise, the read will occur on the local DWN.
*/
target: request.from || this.connectedDid
};
let agentResponse;
if (request.from) {
agentResponse = yield this.agent.sendDwnRequest(agentRequest);
}
else {
agentResponse = yield this.agent.processDwnRequest(agentRequest);
}
const { reply: { record: responseRecord, status } } = agentResponse;
let record;
if (200 <= status.code && status.code <= 299) {
const recordOptions = Object.assign({
/**
* Extract the `author` DID from the record since records may be signed by the
* tenant owner or any other entity.
*/
author: getRecordAuthor(responseRecord),
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN even if the record was read from a remote DWN.
*/
connectedDid: this.connectedDid,
/**
* If the record was returned by reading from a remote DWN, set the `remoteOrigin` to
* the DID of the DWN that returned the record. The `remoteOrigin` property will be used
* to determine which DWN to send subsequent read requests to in the event the data
* payload must be read again (e.g., if the data stream is consumed).
*/
remoteOrigin: request.from }, responseRecord);
record = new Record(this.agent, recordOptions);
}
return { record, status };
}),
/**
* Writes a record to the DWN
*
* As a convenience, the Record instance returned will cache a copy of the data. This is done
* to maintain consistency with other DWN methods, like RecordsQuery, that include relatively
* small data payloads when returning RecordsWrite message properties. Regardless of data
* size, methods such as `record.data.stream()` will return the data when called even if it
* requires fetching from the DWN datastore.
*/
write: (request) => __awaiter(this, void 0, void 0, function* () {
var _c;
const { dataBlob, dataFormat } = dataToBlob(request.data, (_c = request.message) === null || _c === void 0 ? void 0 : _c.dataFormat);
const agentResponse = yield this.agent.processDwnRequest({
author: this.connectedDid,
dataStream: dataBlob,
messageParams: Object.assign(Object.assign({}, request.message), { dataFormat }),
messageType: DwnInterface.RecordsWrite,
store: request.store,
target: this.connectedDid
});
const { message: responseMessage, reply: { status } } = agentResponse;
let record;
if (200 <= status.code && status.code <= 299) {
const recordOptions = Object.assign({
/**
* Assume the author is the connected DID since the record was just written to the
* local DWN.
*/
author: this.connectedDid,
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN.
*/
connectedDid: this.connectedDid, encodedData: dataBlob }, responseMessage);
record = new Record(this.agent, recordOptions);
}
return { record, status };
}),
};
}
}
//# sourceMappingURL=dwn-api.js.map
File diff suppressed because one or more lines are too long
+33
View File
@@ -0,0 +1,33 @@
/**
* Making developing with Web5 components at least 5 times easier to work with.
*
* Web5 consists of the following components:
* - Decentralized Identifiers
* - Verifiable Credentials
* - DWeb Node personal datastores
*
* The SDK sets out to gather the most oft used functionality from all three of
* these pillar technologies to provide a simple library that is as close to
* effortless as possible.
*
* The SDK is currently still under active development, but having entered the
* Tech Preview phase there is now a drive to avoid unnecessary changes unless
* backwards compatibility is provided. Additional functionality will be added
* in the lead up to 1.0 final, and modifications will be made to address
* issues and community feedback.
*
* [Link to GitHub Repo](https://github.com/TBD54566975/web5-js)
*
* @packageDocumentation
*/
export * from './did-api.js';
export * from './dwn-api.js';
export * from './protocol.js';
export * from './record.js';
export * from './vc-api.js';
export * from './web5.js';
export * from './tech-preview.js';
export * from './web-features.js';
import * as utils from './utils.js';
export { utils };
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAElC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,CAAC"}
+67
View File
@@ -0,0 +1,67 @@
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
/// <reference types="@tbd54566975/dwn-sdk-js" />
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { DwnInterface } from '@web5/agent';
/**
* Encapsulates a DWN Protocol with its associated metadata and configuration.
*
* This class primarly exists to provide developers with a convenient way to configure/install
* protocols on remote DWNs.
*/
export class Protocol {
/**
* Constructs a new instance of the Protocol class.
*
* @param agent - The Web5Agent instance used for network interactions.
* @param protocolsConfigureMessage - The configuration message containing the protocol details.
* @param metadata - Metadata associated with the protocol, including the author and optional message CID.
*/
constructor(agent, protocolsConfigureMessage, metadata) {
this._agent = agent;
this._metadata = metadata;
this._protocolsConfigureMessage = protocolsConfigureMessage;
}
/**
* Retrieves the protocol definition from the protocol's configuration message.
* @returns The protocol definition.
*/
get definition() {
return this._protocolsConfigureMessage.descriptor.definition;
}
/**
* Serializes the protocol's configuration message to JSON.
* @returns The serialized JSON object of the protocol's configuration message.
*/
toJSON() {
return this._protocolsConfigureMessage;
}
/**
* Sends the protocol configuration to a remote DWN identified by the target DID.
*
* @param target - The DID of the target DWN to which the protocol configuration will be installed.
* @returns A promise that resolves to an object containing the status of the send operation.
*/
send(target) {
return __awaiter(this, void 0, void 0, function* () {
const { reply } = yield this._agent.sendDwnRequest({
author: this._metadata.author,
messageCid: this._metadata.messageCid,
messageType: DwnInterface.ProtocolsConfigure,
target: target,
});
return { status: reply.status };
});
}
}
//# sourceMappingURL=protocol.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,iDAAiD;;;;;;;;;;AAIjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C;;;;;GAKG;AACH,MAAM,OAAO,QAAQ;IAUnB;;;;;;OAMG;IACH,YAAY,KAAgB,EAAE,yBAAsE,EAAE,QAA0B;QAC9H,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,0BAA0B,GAAG,yBAAyB,CAAC;IAC9D,CAAC;IAED;;;OAGG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,UAAU,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,0BAA0B,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACG,IAAI,CAAC,MAAc;;YACvB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;gBACjD,MAAM,EAAQ,IAAI,CAAC,SAAS,CAAC,MAAM;gBACnC,UAAU,EAAI,IAAI,CAAC,SAAS,CAAC,UAAU;gBACvC,WAAW,EAAG,YAAY,CAAC,kBAAkB;gBAC7C,MAAM,EAAQ,MAAM;aACrB,CAAC,CAAC;YAEH,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,CAAC;KAAA;CACF"}
+602
View File
@@ -0,0 +1,602 @@
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
/// <reference types="@tbd54566975/dwn-sdk-js" />
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { getPaginationCursor } from '@web5/agent';
import { DwnInterface } from '@web5/agent';
import { Convert, isEmptyObject, NodeStream, removeUndefinedProperties, Stream } from '@web5/common';
import { dataToBlob, SendCache } from './utils.js';
/**
* Record wrapper class with convenience methods to send and update,
* aside from manipulating and reading the record data.
*
* Note: The `messageTimestamp` of the most recent RecordsWrite message is
* logically equivalent to the date/time at which a Record was most
* recently modified. Since this Record class implementation is
* intended to simplify the developer experience of working with
* logical records (and not individual DWN messages) the
* `messageTimestamp` is mapped to `dateModified`.
*
* @beta
*/
/**
* The `Record` class encapsulates a single record's data and metadata, providing a more
* developer-friendly interface for working with Decentralized Web Node (DWN) records.
*
* Methods are provided to read, update, and manage the record's lifecycle, including writing to
* remote DWNs.
*
* @beta
*/
export class Record {
// Getters for immutable DWN Record properties.
/** Record's signatures attestation */
get attestation() { return this._attestation; }
/** Record's signatures attestation */
get authorization() { return this._authorization; }
/** DID that signed the record. */
get author() { return this._author; }
/** Record's context ID */
get contextId() { return this._contextId; }
/** Record's data format */
get dataFormat() { return this._descriptor.dataFormat; }
/** Record's creation date */
get dateCreated() { return this._descriptor.dateCreated; }
/** Record's encryption */
get encryption() { return this._encryption; }
/** Record's initial write if the record has been updated */
get initialWrite() { return this._initialWrite; }
/** Record's ID */
get id() { return this._recordId; }
/** Interface is always `Records` */
get interface() { return this._descriptor.interface; }
/** Method is always `Write` */
get method() { return this._descriptor.method; }
/** Record's parent ID */
get parentId() { return this._descriptor.parentId; }
/** Record's protocol */
get protocol() { return this._descriptor.protocol; }
/** Record's protocol path */
get protocolPath() { return this._descriptor.protocolPath; }
/** Role under which the author is writing the record */
get protocolRole() { return this._protocolRole; }
/** Record's recipient */
get recipient() { return this._descriptor.recipient; }
/** Record's schema */
get schema() { return this._descriptor.schema; }
// Getters for mutable DWN Record properties.
/** Record's CID */
get dataCid() { return this._descriptor.dataCid; }
/** Record's data size */
get dataSize() { return this._descriptor.dataSize; }
/** Record's modified date */
get dateModified() { return this._descriptor.messageTimestamp; }
/** Record's published date */
get datePublished() { return this._descriptor.datePublished; }
/** Record's published status */
get messageTimestamp() { return this._descriptor.messageTimestamp; }
/** Record's published status (true/false) */
get published() { return this._descriptor.published; }
/** Tags of the record */
get tags() { return this._descriptor.tags; }
/**
* Returns a copy of the raw `RecordsWriteMessage` that was used to create the current `Record` instance.
*/
get rawMessage() {
const message = JSON.parse(JSON.stringify({
contextId: this._contextId,
recordId: this._recordId,
descriptor: this._descriptor,
attestation: this._attestation,
authorization: this._authorization,
encryption: this._encryption,
}));
removeUndefinedProperties(message);
return message;
}
constructor(agent, options) {
this._agent = agent;
// Store the author DID that originally signed the message as a convenience for developers, so
// that they don't have to decode the signer's DID from the JWS.
this._author = options.author;
// Store the currently `connectedDid` so that subsequent message signing is done with the
// connected DID's keys and DWN requests target the connected DID's DWN.
this._connectedDid = options.connectedDid;
// If the record was queried or read from a remote DWN, the `remoteOrigin` DID will be
// defined. This value is used to send subsequent read requests to the same remote DWN in the
// event the record's data payload was too large to be returned in query results. or must be
// read again (e.g., if the data stream is consumed).
this._remoteOrigin = options.remoteOrigin;
// RecordsWriteMessage properties.
this._attestation = options.attestation;
this._authorization = options.authorization;
this._contextId = options.contextId;
this._descriptor = options.descriptor;
this._encryption = options.encryption;
this._initialWrite = options.initialWrite;
this._recordId = options.recordId;
this._protocolRole = options.protocolRole;
if (options.encodedData) {
// If `encodedData` is set, then it is expected that:
// type is Blob if the Record object was instantiated by dwn.records.create()/write().
// type is Base64 URL encoded string if the Record object was instantiated by dwn.records.query().
// If it is a string, we need to Base64 URL decode to bytes and instantiate a Blob.
this._encodedData = (typeof options.encodedData === 'string') ?
new Blob([Convert.base64Url(options.encodedData).toUint8Array()], { type: this.dataFormat }) :
options.encodedData;
}
if (options.data) {
// If the record was created from a RecordsRead reply then it will have a `data` property.
// If the `data` property is a web ReadableStream, convert it to a Node.js Readable.
this._readableStream = Stream.isReadableStream(options.data) ?
NodeStream.fromWebReadable({ readableStream: options.data }) :
options.data;
}
}
/**
* Returns the data of the current record.
* If the record data is not available, it attempts to fetch the data from the DWN.
* @returns a data stream with convenience methods such as `blob()`, `json()`, `text()`, and `stream()`, similar to the fetch API response
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
get data() {
const self = this; // Capture the context of the `Record` instance.
const dataObj = {
/**
* Returns the data of the current record as a `Blob`.
*
* @returns A promise that resolves to a Blob containing the record's data.
* @throws If the record data is not available or cannot be converted to a `Blob`.
*
* @beta
*/
blob() {
return __awaiter(this, void 0, void 0, function* () {
return new Blob([yield NodeStream.consumeToBytes({ readable: yield this.stream() })], { type: self.dataFormat });
});
},
/**
* Returns the data of the current record as a `Uint8Array`.
*
* @returns A Promise that resolves to a `Uint8Array` containing the record's data bytes.
* @throws If the record data is not available or cannot be converted to a byte array.
*
* @beta
*/
bytes() {
return __awaiter(this, void 0, void 0, function* () {
return yield NodeStream.consumeToBytes({ readable: yield this.stream() });
});
},
/**
* Parses the data of the current record as JSON and returns it as a JavaScript object.
*
* @returns A Promise that resolves to a JavaScript object parsed from the record's JSON data.
* @throws If the record data is not available, not in JSON format, or cannot be parsed.
*
* @beta
*/
json() {
return __awaiter(this, void 0, void 0, function* () {
return yield NodeStream.consumeToJson({ readable: yield this.stream() });
});
},
/**
* Returns the data of the current record as a `string`.
*
* @returns A promise that resolves to a `string` containing the record's text data.
* @throws If the record data is not available or cannot be converted to text.
*
* @beta
*/
text() {
return __awaiter(this, void 0, void 0, function* () {
return yield NodeStream.consumeToText({ readable: yield this.stream() });
});
},
/**
* Provides a `Readable` stream containing the record's data.
*
* @returns A promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If the record data is not available in-memory and cannot be fetched.
*
* @beta
*/
stream() {
return __awaiter(this, void 0, void 0, function* () {
if (self._encodedData) {
/** If `encodedData` is set, it indicates that the Record was instantiated by
* `dwn.records.create()`/`dwn.records.write()` or the record's data payload was small
* enough to be returned in `dwn.records.query()` results. In either case, the data is
* already available in-memory and can be returned as a Node.js `Readable` stream. */
self._readableStream = NodeStream.fromWebReadable({ readableStream: self._encodedData.stream() });
}
else if (!NodeStream.isReadable({ readable: self._readableStream })) {
/** If the data stream for this `Record` instance has already been partially or fully
* consumed, then the data must be fetched again from either: */
self._readableStream = self._remoteOrigin ?
// A. ...a remote DWN if the record was originally queried from a remote DWN.
yield self.readRecordData({ target: self._remoteOrigin, isRemote: true }) :
// B. ...a local DWN if the record was originally queried from the local DWN.
yield self.readRecordData({ target: self._connectedDid, isRemote: false });
}
if (!self._readableStream) {
throw new Error('Record data is not available.');
}
return self._readableStream;
});
},
/**
* Attaches callbacks for the resolution and/or rejection of the `Promise` returned by
* `stream()`.
*
* This method is a proxy to the `then` method of the `Promise` returned by `stream()`,
* allowing for a seamless integration with promise-based workflows.
* @param onFulfilled - A function to asynchronously execute when the `stream()` promise
* becomes fulfilled.
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` for the completion of which ever callback is executed.
*/
then(onFulfilled, onRejected) {
return this.stream().then(onFulfilled, onRejected);
},
/**
* Attaches a rejection handler callback to the `Promise` returned by the `stream()` method.
* This method is a shorthand for `.then(undefined, onRejected)`, specifically designed for handling
* rejection cases in the promise chain initiated by accessing the record's data. It ensures that
* errors during data retrieval or processing can be caught and handled appropriately.
*
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` that resolves to the value of the callback if it is called, or to its
* original fulfillment value if the promise is instead fulfilled.
*/
catch(onRejected) {
return this.stream().catch(onRejected);
}
};
return dataObj;
}
/**
* Stores the current record state as well as any initial write to the owner's DWN.
*
* @param importRecord - if true, the record will signed by the owner before storing it to the owner's DWN. Defaults to false.
* @returns the status of the store request
*
* @beta
*/
store(importRecord = false) {
return __awaiter(this, void 0, void 0, function* () {
// if we are importing the record we sign it as the owner
return this.processRecord({ signAsOwner: importRecord, store: true });
});
}
/**
* Signs the current record state as well as any initial write and optionally stores it to the owner's DWN.
* This is useful when importing a record that was signed by someone else into your own DWN.
*
* @param store - if true, the record will be stored to the owner's DWN after signing. Defaults to true.
* @returns the status of the import request
*
* @beta
*/
import(store = true) {
return __awaiter(this, void 0, void 0, function* () {
return this.processRecord({ store, signAsOwner: true });
});
}
/**
* Send the current record to a remote DWN by specifying their DID
* If no DID is specified, the target is assumed to be the owner (connectedDID).
* If an initial write is present and the Record class send cache has no awareness of it, the initial write is sent first
* (vs waiting for the regular DWN sync)
*
* @param target - the optional DID to send the record to, if none is set it is sent to the connectedDid
* @returns the status of the send record request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
send(target) {
return __awaiter(this, void 0, void 0, function* () {
const initialWrite = this._initialWrite;
target !== null && target !== void 0 ? target : (target = this._connectedDid);
// Is there an initial write? Do we know if we've already sent it to this target?
if (initialWrite && !Record._sendCache.check(this._recordId, target)) {
// We do have an initial write, so prepare it for sending to the target.
const rawMessage = Object.assign({}, initialWrite);
removeUndefinedProperties(rawMessage);
// Send the initial write to the target.
yield this._agent.sendDwnRequest({
messageType: DwnInterface.RecordsWrite,
author: this._connectedDid,
target: target,
rawMessage
});
// Set the cache to maintain awareness that we don't need to send the initial write next time.
Record._sendCache.set(this._recordId, target);
}
// Send the current/latest state to the target.
const { reply } = yield this._agent.sendDwnRequest({
messageType: DwnInterface.RecordsWrite,
author: this._connectedDid,
dataStream: yield this.data.blob(),
target: target,
rawMessage: Object.assign({}, this.rawMessage)
});
return reply;
});
}
/**
* Returns a JSON representation of the Record instance.
* It's called by `JSON.stringify(...)` automatically.
*/
toJSON() {
return {
attestation: this.attestation,
author: this.author,
authorization: this.authorization,
contextId: this.contextId,
dataCid: this.dataCid,
dataFormat: this.dataFormat,
dataSize: this.dataSize,
dateCreated: this.dateCreated,
messageTimestamp: this.dateModified,
datePublished: this.datePublished,
encryption: this.encryption,
interface: this.interface,
method: this.method,
parentId: this.parentId,
protocol: this.protocol,
protocolPath: this.protocolPath,
protocolRole: this.protocolRole,
published: this.published,
recipient: this.recipient,
recordId: this.id,
schema: this.schema,
tags: this.tags,
};
}
/**
* Convenience method to return the string representation of the Record instance.
* Called automatically in string concatenation, String() type conversion, and template literals.
*/
toString() {
let str = `Record: {\n`;
str += ` ID: ${this.id}\n`;
str += this.contextId ? ` Context ID: ${this.contextId}\n` : '';
str += this.protocol ? ` Protocol: ${this.protocol}\n` : '';
str += this.schema ? ` Schema: ${this.schema}\n` : '';
str += ` Data CID: ${this.dataCid}\n`;
str += ` Data Format: ${this.dataFormat}\n`;
str += ` Data Size: ${this.dataSize}\n`;
str += ` Created: ${this.dateCreated}\n`;
str += ` Modified: ${this.dateModified}\n`;
str += `}`;
return str;
}
/**
* Returns a pagination cursor for the current record given a sort order.
*
* @param sort the sort order to use for the pagination cursor.
* @returns A promise that resolves to a pagination cursor for the current record.
*/
paginationCursor(sort) {
return __awaiter(this, void 0, void 0, function* () {
return getPaginationCursor(this.rawMessage, sort);
});
}
/**
* Update the current record on the DWN.
* @param params - Parameters to update the record.
* @returns the status of the update request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
update(_a) {
var { dateModified, data } = _a, params = __rest(_a, ["dateModified", "data"]);
return __awaiter(this, void 0, void 0, function* () {
// if there is a parentId, we remove it from the descriptor and set a parentContextId
const _b = this._descriptor, { parentId } = _b, descriptor = __rest(_b, ["parentId"]);
const parentContextId = parentId ? this._contextId.split('/').slice(0, -1).join('/') : undefined;
// Begin assembling the update message.
let updateMessage = Object.assign(Object.assign(Object.assign({}, descriptor), params), { parentContextId, messageTimestamp: dateModified, recordId: this._recordId });
// NOTE: The original Record's tags are copied to the update message, so that the tags are not lost.
// However if a user passes new tags in the `RecordUpdateParams` object, they will overwrite the original tags.
// If the updated tag object is empty or set to null, we remove the tags property to avoid schema validation errors in the DWN SDK.
if (isEmptyObject(updateMessage.tags) || updateMessage.tags === null) {
delete updateMessage.tags;
}
let dataBlob;
if (data !== undefined) {
// If `data` is being updated then `dataCid` and `dataSize` must be undefined and the `data`
// value must be converted to a Blob and later passed as a top-level property to
// `agent.processDwnRequest()`.
delete updateMessage.dataCid;
delete updateMessage.dataSize;
({ dataBlob } = dataToBlob(data, updateMessage.dataFormat));
}
// Throw an error if an attempt is made to modify immutable properties.
// Note: `data` and `dateModified` have already been handled.
const mutableDescriptorProperties = new Set(['data', 'dataCid', 'dataSize', 'datePublished', 'messageTimestamp', 'published', 'tags']);
Record.verifyPermittedMutation(Object.keys(params), mutableDescriptorProperties);
// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
// will throw an error if `published` is false but `datePublished` is set.
if (params.published === false && updateMessage.datePublished !== undefined) {
delete updateMessage.datePublished;
}
const agentResponse = yield this._agent.processDwnRequest({
author: this._connectedDid,
dataStream: dataBlob,
messageParams: Object.assign({}, updateMessage),
messageType: DwnInterface.RecordsWrite,
target: this._connectedDid,
});
const { message, reply: { status } } = agentResponse;
const responseMessage = message;
if (200 <= status.code && status.code <= 299) {
// copy the original raw message to the initial write before we update the values.
if (!this._initialWrite) {
this._initialWrite = Object.assign({}, this.rawMessage);
}
// Only update the local Record instance mutable properties if the record was successfully (over)written.
this._authorization = responseMessage.authorization;
this._protocolRole = params.protocolRole;
mutableDescriptorProperties.forEach(property => {
this._descriptor[property] = responseMessage.descriptor[property];
});
// Cache data.
if (data !== undefined) {
this._encodedData = dataBlob;
}
}
return { status };
});
}
/**
* Handles the various conditions around there being an initial write, whether to store initial/current state,
* and whether to add an owner signature to the initial write to enable storage when protocol rules require it.
*/
processRecord({ store, signAsOwner }) {
return __awaiter(this, void 0, void 0, function* () {
// if there is an initial write and we haven't already processed it, we first process it and marked it as such.
if (this._initialWrite && ((signAsOwner && !this._initialWriteSigned) || (store && !this._initialWriteStored))) {
const initialWriteRequest = {
messageType: DwnInterface.RecordsWrite,
rawMessage: this.initialWrite,
author: this._connectedDid,
target: this._connectedDid,
signAsOwner,
store,
};
// Process the prepared initial write, with the options set for storing and/or signing as the owner.
const agentResponse = yield this._agent.processDwnRequest(initialWriteRequest);
const { message, reply: { status } } = agentResponse;
const responseMessage = message;
// If we are signing as owner, make sure to update the initial write's authorization, because now it will have the owner's signature on it
// set the stored or signed status to true so we don't process it again.
if (200 <= status.code && status.code <= 299) {
if (store)
this._initialWriteStored = true;
if (signAsOwner) {
this._initialWriteSigned = true;
this.initialWrite.authorization = responseMessage.authorization;
}
}
}
// Now that we've processed a potential initial write, we can process the current record state.
const requestOptions = {
messageType: DwnInterface.RecordsWrite,
rawMessage: this.rawMessage,
author: this._connectedDid,
target: this._connectedDid,
dataStream: yield this.data.blob(),
signAsOwner,
store,
};
const agentResponse = yield this._agent.processDwnRequest(requestOptions);
const { message, reply: { status } } = agentResponse;
const responseMessage = message;
if (200 <= status.code && status.code <= 299) {
// If we are signing as the owner, make sure to update the current record state's authorization, because now it will have the owner's signature on it.
if (signAsOwner)
this._authorization = responseMessage.authorization;
}
return { status };
});
}
/**
* Fetches the record's data from the specified DWN.
*
* This private method is called when the record data is not available in-memory
* and needs to be fetched from either a local or a remote DWN.
* It makes a read request to the specified DWN and processes the response to provide
* a Node.js `Readable` stream of the record's data.
*
* @param params - Parameters for fetching the record's data.
* @param params.target - The DID of the DWN to fetch the data from.
* @param params.isRemote - Indicates whether the target DWN is a remote node.
* @returns A Promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If there is an error while fetching or processing the data from the DWN.
*
* @beta
*/
readRecordData({ target, isRemote }) {
return __awaiter(this, void 0, void 0, function* () {
const readRequest = {
author: this._connectedDid,
messageParams: { filter: { recordId: this.id } },
messageType: DwnInterface.RecordsRead,
target,
};
const agentResponsePromise = isRemote ?
this._agent.sendDwnRequest(readRequest) :
this._agent.processDwnRequest(readRequest);
try {
const { reply: { record } } = yield agentResponsePromise;
const dataStream = record.data;
// If the data stream is a web ReadableStream, convert it to a Node.js Readable.
const nodeReadable = Stream.isReadableStream(dataStream) ?
NodeStream.fromWebReadable({ readableStream: dataStream }) :
dataStream;
return nodeReadable;
}
catch (error) {
throw new Error(`Error encountered while attempting to read data: ${error.message}`);
}
});
}
/**
* Verifies if the properties to be mutated are mutable.
*
* This private method is used to ensure that only mutable properties of the `Record` instance
* are being changed. It checks whether the properties specified for mutation are among the
* set of properties that are allowed to be modified. If any of the properties to be mutated
* are not in the set of mutable properties, the method throws an error.
*
* @param propertiesToMutate - An iterable of property names that are intended to be mutated.
* @param mutableDescriptorProperties - A set of property names that are allowed to be mutated.
*
* @throws If any of the properties in `propertiesToMutate` are not in `mutableDescriptorProperties`.
*
* @beta
*/
static verifyPermittedMutation(propertiesToMutate, mutableDescriptorProperties) {
for (const property of propertiesToMutate) {
if (!mutableDescriptorProperties.has(property)) {
throw new Error(`${property} is an immutable property. Its value cannot be changed.`);
}
}
}
}
/**
* Cache to minimize the amount of redundant two-phase commits we do in store() and send()
* Retains awareness of the last 100 records stored/sent for up to 100 target DIDs each.
*/
Record._sendCache = SendCache;
//# sourceMappingURL=record.js.map
File diff suppressed because one or more lines are too long
+65
View File
@@ -0,0 +1,65 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { UniversalResolver, DidDht, DidWeb } from '@web5/dids';
const workerSelf = self;
const DidResolver = new UniversalResolver({ didResolvers: [DidDht, DidWeb] });
const didUrlRegex = /^https?:\/\/dweb\/(([^/]+)\/.*)?$/;
const httpToHttpsRegex = /^http:/;
const trailingSlashRegex = /\/$/;
workerSelf.addEventListener('fetch', event => {
const match = event.request.url.match(didUrlRegex);
if (match) {
event.respondWith((() => __awaiter(void 0, void 0, void 0, function* () {
const normalizedUrl = event.request.url.replace(httpToHttpsRegex, 'https:').replace(trailingSlashRegex, '');
const cachedResponse = yield caches.open('drl').then(cache => cache.match(normalizedUrl));
return cachedResponse || handleEvent(event, match[2], match[1]);
}))());
}
});
function handleEvent(event, did, route) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield DidResolver.resolve(did);
return yield fetchResource(event, result.didDocument, route);
}
catch (error) {
if (error instanceof Response) {
return error;
}
console.log(`Error in DID URL fetch: ${error}`);
return new Response('DID URL fetch error', { status: 500 });
}
});
}
function fetchResource(event, ddo, route) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
let endpoints = (_b = (_a = ddo === null || ddo === void 0 ? void 0 : ddo.service) === null || _a === void 0 ? void 0 : _a.find(service => service.type === 'DecentralizedWebNode')) === null || _b === void 0 ? void 0 : _b.serviceEndpoint;
endpoints = (Array.isArray(endpoints) ? endpoints : [endpoints]).filter(url => url.startsWith('http'));
if (!(endpoints === null || endpoints === void 0 ? void 0 : endpoints.length)) {
throw new Response('DWeb Node resolution failed: no valid endpoints found.', { status: 530 });
}
for (const endpoint of endpoints) {
try {
const response = yield fetch(`${endpoint.replace(trailingSlashRegex, '')}/${route}`, { headers: event.request.headers });
if (response.ok) {
return response;
}
console.log(`DWN endpoint error: ${response.status}`);
return new Response('DWeb Node request failed', { status: response.status });
}
catch (error) {
console.log(`DWN endpoint error: ${error}`);
return new Response('DWeb Node request failed: ' + error, { status: 500 });
}
}
});
}
//# sourceMappingURL=service-worker.js.map
@@ -0,0 +1 @@
{"version":3,"file":"service-worker.js","sourceRoot":"","sources":["../../src/service-worker.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,UAAU,GAAG,IAAW,CAAC;AAC/B,MAAM,WAAW,GAAG,IAAI,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AAC9E,MAAM,WAAW,GAAG,mCAAmC,CAAC;AACxD,MAAM,gBAAgB,GAAG,QAAQ,CAAC;AAClC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAEjC,UAAU,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,KAAK,EAAE;QACT,KAAK,CAAC,WAAW,CAAC,CAAC,GAAS,EAAE;YAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;YAC5G,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;YAC1F,OAAO,cAAc,IAAI,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC,CAAA,CAAC,EAAE,CAAC,CAAC;KACP;AACH,CAAC,CAAC,CAAC;AAEH,SAAe,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK;;QAC1C,IAAI;YACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9C,OAAO,MAAM,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;SAC9D;QACD,OAAM,KAAK,EAAC;YACV,IAAI,KAAK,YAAY,QAAQ,EAAE;gBAC7B,OAAO,KAAK,CAAC;aACd;YACD,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,EAAE,CAAC,CAAC;YAChD,OAAO,IAAI,QAAQ,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;SAC7D;IACH,CAAC;CAAA;AAED,SAAe,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK;;;QAC5C,IAAI,SAAS,GAAG,MAAA,MAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,OAAO,0CAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,sBAAsB,CAAC,0CAAE,eAAe,CAAC;QACxG,SAAS,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACvG,IAAI,CAAC,CAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,MAAM,CAAA,EAAE;YACtB,MAAM,IAAI,QAAQ,CAAC,wDAAwD,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;SAC/F;QAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;YAChC,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;gBACzH,IAAI,QAAQ,CAAC,EAAE,EAAE;oBACf,OAAO,QAAQ,CAAC;iBACjB;gBACD,OAAO,CAAC,GAAG,CAAC,uBAAuB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBACtD,OAAO,IAAI,QAAQ,CAAC,0BAA0B,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;aAC9E;YACD,OAAO,KAAK,EAAE;gBACZ,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,EAAE,CAAC,CAAC;gBAC5C,OAAO,IAAI,QAAQ,CAAC,4BAA4B,GAAG,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;aAC5E;SACF;;CACF"}
+62
View File
@@ -0,0 +1,62 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { utils as didUtils } from '@web5/dids';
/**
* Dynamically selects up to 2 DWN endpoints that are provided
* by default during the Tech Preview period.
*
* @beta
*/
export function getTechPreviewDwnEndpoints() {
return __awaiter(this, void 0, void 0, function* () {
let response;
try {
response = yield fetch('https://dwn.tbddev.org/.well-known/did.json');
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
}
}
catch (error) {
console.warn('failed to get tech preview dwn endpoints:', error.message);
return [];
}
const didDocument = yield response.json();
const [dwnService] = didUtils.getServices({ didDocument, id: '#dwn', type: 'DecentralizedWebNode' });
// allocate up to 2 nodes for a user.
const techPreviewEndpoints = new Set();
if ('serviceEndpoint' in dwnService
&& !Array.isArray(dwnService.serviceEndpoint)
&& typeof dwnService.serviceEndpoint !== 'string'
&& Array.isArray(dwnService.serviceEndpoint.nodes)) {
const dwnUrls = dwnService.serviceEndpoint.nodes;
const numNodesToAllocate = Math.min(dwnUrls.length, 2);
for (let attempts = 0; attempts < dwnUrls.length && techPreviewEndpoints.size < numNodesToAllocate; attempts += 1) {
const nodeIdx = getRandomInt(0, dwnUrls.length);
const dwnUrl = dwnUrls[nodeIdx];
try {
const healthCheck = yield fetch(`${dwnUrl}/health`);
if (healthCheck.ok) {
techPreviewEndpoints.add(dwnUrl);
}
}
catch (error) {
// Ignore healthcheck failures and try the next node.
}
}
}
return Array.from(techPreviewEndpoints);
});
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
//# sourceMappingURL=tech-preview.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"tech-preview.js","sourceRoot":"","sources":["../../src/tech-preview.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,UAAgB,0BAA0B;;QAC9C,IAAI,QAAkB,CAAC;QACvB,IAAI;YACF,QAAQ,GAAG,MAAM,KAAK,CAAC,6CAA6C,CAAC,CAAC;YACtE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;gBAChB,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;aAC1E;SACF;QAAC,OAAM,KAAU,EAAE;YAClB,OAAO,CAAC,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACzE,OAAO,EAAE,CAAC;SACX;QAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC1C,MAAM,CAAE,UAAU,CAAE,GAAG,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAEvG,qCAAqC;QACrC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAC;QAE/C,IAAI,iBAAiB,IAAI,UAAU;eAC5B,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC;eAC1C,OAAO,UAAU,CAAC,eAAe,KAAK,QAAQ;eAC9C,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE;YACtD,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC;YAEjD,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAEvD,KAAK,IAAI,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,IAAI,oBAAoB,CAAC,IAAI,GAAG,kBAAkB,EAAE,QAAQ,IAAI,CAAC,EAAE;gBACjH,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBAChD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;gBAEhC,IAAI;oBACF,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,SAAS,CAAC,CAAC;oBACpD,IAAI,WAAW,CAAC,EAAE,EAAE;wBAClB,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;qBAClC;iBACF;gBAAC,OAAM,KAAc,EAAE;oBACtB,qDAAqD;iBACtD;aACF;SACF;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC1C,CAAC;CAAA;AAED,SAAS,YAAY,CAAC,GAAG,EAAE,GAAG;IAC5B,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACvD,CAAC"}
+120
View File
@@ -0,0 +1,120 @@
import { Convert, universalTypeOf } from '@web5/common';
/**
* Converts various data types to a `Blob` object, automatically detecting the data type or using
* the specified `dataFormat` to set the Blob's MIME type.
*
* This function supports plain text, JSON objects, binary data (Uint8Array, ArrayBuffer), and Blob
* inputs and will attempt to automatically detect the type of the data if `dataFormat` is not
* explicitly provided.
*
* @beta
*
* @example
* ```ts
* // Convert a JSON object to a Blob
* const { dataBlob, dataFormat } = dataToBlob({ key: 'value' }, 'application/json');
*
* // Convert a plain text string to a Blob without specifying dataFormat
* const { dataBlob: textBlob } = dataToBlob('Hello, world!');
*
* // Convert binary data to a Blob
* const binaryData = new Uint8Array([0, 1, 2, 3]);
* const { dataBlob: binaryBlob } = dataToBlob(binaryData);
* ```
*
* @param data - The data to be converted into a `Blob`. This can be a string, an object, binary
* data (Uint8Array or ArrayBuffer), or a Blob.
* @param dataFormat - An optional MIME type string that specifies the format of the data. Common
* types include 'text/plain' for string data, 'application/json' for JSON
* objects, and 'application/octet-stream' for binary data. If not provided, the
* function will attempt to detect the format based on the data type or default
* to 'application/octet-stream'.
* @returns An object containing the `dataBlob`, a Blob representation of the input data, and
* `dataFormat`, the MIME type of the data as determined by the function or specified by the caller.
* @throws An error if the data type is not supported or cannot be converted to a Blob.
*/
export function dataToBlob(data, dataFormat) {
let dataBlob;
// Check for Object or String, and if neither, assume bytes.
const detectedType = universalTypeOf(data);
if (dataFormat === 'text/plain' || detectedType === 'String') {
dataBlob = new Blob([data], { type: 'text/plain' });
}
else if (dataFormat === 'application/json' || detectedType === 'Object') {
const dataBytes = Convert.object(data).toUint8Array();
dataBlob = new Blob([dataBytes], { type: 'application/json' });
}
else if (detectedType === 'Uint8Array' || detectedType === 'ArrayBuffer') {
dataBlob = new Blob([data], { type: 'application/octet-stream' });
}
else if (detectedType === 'Blob') {
dataBlob = data;
}
else {
throw new Error('data type not supported.');
}
dataFormat = dataFormat || dataBlob.type || 'application/octet-stream';
return { dataBlob, dataFormat };
}
/**
* The `SendCache` class provides a static caching mechanism to optimize the process of sending
* records to remote DWN targets by minimizing redundant sends.
*
* It maintains a cache of record IDs and their associated target DIDs to which they have been sent.
* This helps in avoiding unnecessary network requests and ensures efficient data synchronization
* across Decentralized Web Nodes (DWNs).
*
* The cache employs a simple eviction policy to maintain a manageable size, ensuring that the cache
* does not grow indefinitely and consume excessive memory resources.
*
* @beta
*/
export class SendCache {
/**
* Checks if a given record ID has been sent to a specified target DID. This method is used to
* determine whether a send operation is necessary or if it can be skipped to avoid redundancy.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to check against.
* @returns A boolean indicating whether the record has been sent to the target.
*/
static check(id, target) {
let targetCache = SendCache.cache.get(id);
return targetCache ? targetCache.has(target) : false;
}
/**
* Adds or updates an entry in the cache for a given record ID and target DID. If the cache
* exceeds its size limit, the oldest entry is removed. This method ensures that the cache
* reflects the most recent sends.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to which the record has been sent.
*/
static set(id, target) {
let targetCache = SendCache.cache.get(id) || new Set();
SendCache.cache.delete(id);
SendCache.cache.set(id, targetCache);
if (this.cache.size > SendCache.sendCacheLimit) {
const firstRecord = SendCache.cache.keys().next().value;
SendCache.cache.delete(firstRecord);
}
targetCache.delete(target);
targetCache.add(target);
if (targetCache.size > SendCache.sendCacheLimit) {
const firstTarget = targetCache.keys().next().value;
targetCache.delete(firstTarget);
}
}
}
/**
* A private static map that serves as the core storage mechanism for the cache. It maps record
* IDs to a set of target DIDs, indicating which records have been sent to which targets.
*/
SendCache.cache = new Map();
/**
* The maximum number of entries allowed in the cache. Once this limit is exceeded, the oldest
* entries are evicted to make room for new ones. This limit applies both to the number of records
* and the number of targets per record.
*/
SendCache.sendCacheLimit = 100;
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
+30
View File
@@ -0,0 +1,30 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* The VC API is used to issue, present and verify VCs
*
* @beta
*/
export class VcApi {
constructor(options) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* Issues a VC (Not implemented yet)
*/
create() {
return __awaiter(this, void 0, void 0, function* () {
// TODO: implement
throw new Error('Not implemented.');
});
}
}
//# sourceMappingURL=vc-api.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"vc-api.js","sourceRoot":"","sources":["../../src/vc-api.ts"],"names":[],"mappings":";;;;;;;;;AAEA;;;;GAIG;AACH,MAAM,OAAO,KAAK;IAUhB,YAAY,OAAmD;QAC7D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC3C,CAAC;IAED;;OAEG;IACG,MAAM;;YACV,kBAAkB;YAClB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;KAAA;CACF"}
+28
View File
@@ -0,0 +1,28 @@
/**
* Installs the DWeb networking features in the current environment.
*/
export function installNetworkingFeatures(path) {
var _a;
const workerSelf = self;
try {
if (typeof ServiceWorkerGlobalScope !== 'undefined' && workerSelf instanceof ServiceWorkerGlobalScope) {
// Dynamically import service worker code only if we're in a Service Worker context
import('./service-worker.js').catch(error => {
console.error('Error loading service worker module:', error);
});
}
else if ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.navigator) === null || _a === void 0 ? void 0 : _a.serviceWorker) {
if (path)
navigator.serviceWorker.register(path).catch(error => {
console.error('DWeb networking feature installation failed: ', error);
});
}
else {
throw new Error('DWeb networking features are not available for install in this environment');
}
}
catch (error) {
console.error('Error in installing networking features:', error);
}
}
//# sourceMappingURL=web-features.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"web-features.js","sourceRoot":"","sources":["../../src/web-features.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY;;IACpD,MAAM,UAAU,GAAG,IAAW,CAAC;IAE/B,IAAI;QACF,IAAI,OAAO,wBAAwB,KAAK,WAAW,IAAI,UAAU,YAAY,wBAAwB,EAAE;YACrG,mFAAmF;YACnF,MAAM,CAAC,qBAAqB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBAC1C,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;YAC/D,CAAC,CAAC,CAAC;SACJ;aACI,IAAI,MAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,SAAS,0CAAE,aAAa,EAAE;YAC7C,IAAI,IAAI;gBAAE,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;oBAC7D,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;gBACxE,CAAC,CAAC,CAAC;SACJ;aACI;YACH,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;SAC/F;KACF;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;KAClE;AACH,CAAC"}
+127
View File
@@ -0,0 +1,127 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Web5UserAgent } from '@web5/user-agent';
import { VcApi } from './vc-api.js';
import { DwnApi } from './dwn-api.js';
import { DidApi } from './did-api.js';
import { getTechPreviewDwnEndpoints } from './tech-preview.js';
/**
* The main Web5 API interface. It manages the creation of a DID if needed, the connection to the
* local DWN and all the web5 main foundational APIs such as VC, syncing, etc.
*/
export class Web5 {
constructor({ agent, connectedDid }) {
this.agent = agent;
this.connectedDid = connectedDid;
this.did = new DidApi({ agent, connectedDid });
this.dwn = new DwnApi({ agent, connectedDid });
this.vc = new VcApi({ agent, connectedDid });
}
/**
* Connects to a {@link Web5Agent}. Defaults to creating a local {@link Web5UserAgent} if one
* isn't provided.
*
* @param options - Optional overrides that can be provided when calling {@link Web5.connect}.
* @returns A promise that resolves to a {@link Web5} instance and the connected DID.
*/
static connect({ agent, agentVault, connectedDid, password, recoveryPhrase, sync, techPreview } = {}) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (agent === undefined) {
// A custom Web5Agent implementation was not specified, so use default managed user agent.
const userAgent = yield Web5UserAgent.create({ agentVault });
agent = userAgent;
// Warn the developer and application user of the security risks of using a static password.
if (password === undefined) {
password = 'insecure-static-phrase';
console.warn('%cSECURITY WARNING:%c ' +
'You have not set a password, which defaults to a static, guessable value. ' +
'This significantly compromises the security of your data. ' +
'Please configure a secure, unique password.', 'font-weight: bold; color: red;', 'font-weight: normal; color: inherit;');
}
// Initialize, if necessary, and start the agent.
if (yield userAgent.firstLaunch()) {
recoveryPhrase = yield userAgent.initialize({ password, recoveryPhrase });
}
yield userAgent.start({ password });
// TODO: Replace stubbed connection attempt once Connect Protocol has been implemented.
// Attempt to Connect to localhost agent or via Connect Server.
// userAgent.connect();
const notConnected = true;
if ( /* !userAgent.isConnected() */notConnected) {
// Connect attempt failed or was rejected so fallback to local user agent.
let identity;
// Query the Agent's DWN tenant for identity records.
const identities = yield userAgent.identity.list();
// If an existing identity is not found found, create a new one.
const existingIdentityCount = identities.length;
if (existingIdentityCount === 0) {
// Use the specified DWN endpoints or get default tech preview hosted nodes.
const serviceEndpointNodes = (_a = techPreview === null || techPreview === void 0 ? void 0 : techPreview.dwnEndpoints) !== null && _a !== void 0 ? _a : yield getTechPreviewDwnEndpoints();
// Generate a new Identity for the end-user.
identity = yield userAgent.identity.create({
didMethod: 'dht',
metadata: { name: 'Default' },
didOptions: {
services: [
{
id: 'dwn',
type: 'DecentralizedWebNode',
serviceEndpoint: serviceEndpointNodes,
enc: '#enc',
sig: '#sig',
}
],
verificationMethods: [
{
algorithm: 'Ed25519',
id: 'sig',
purposes: ['assertionMethod', 'authentication']
},
{
algorithm: 'secp256k1',
id: 'enc',
purposes: ['keyAgreement']
}
]
}
});
// The User Agent will manage the Identity, which ensures it will be available on future
// sessions.
yield userAgent.identity.manage({ portableIdentity: yield identity.export() });
}
else if (existingIdentityCount === 1) {
// An existing identity was found in the User Agent's tenant.
identity = identities[0];
}
else {
throw new Error(`connect() failed due to unexpected state: Expected 1 but found ${existingIdentityCount} stored identities.`);
}
// Set the stored identity as the connected DID.
connectedDid = identity.did.uri;
}
// Enable sync, unless explicitly disabled.
if (sync !== 'off') {
// First, register the user identity for sync.
yield userAgent.sync.registerIdentity({ did: connectedDid });
// Enable sync using the specified interval or default.
sync !== null && sync !== void 0 ? sync : (sync = '2m');
userAgent.sync.startSync({ interval: sync })
.catch((error) => {
console.error(`Sync failed: ${error}`);
});
}
}
const web5 = new Web5({ agent, connectedDid });
return { web5, did: connectedDid, recoveryPhrase };
});
}
}
//# sourceMappingURL=web5.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"web5.js","sourceRoot":"","sources":["../../src/web5.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAsH/D;;;GAGG;AACH,MAAM,OAAO,IAAI;IAmBf,YAAY,EAAE,KAAK,EAAE,YAAY,EAAc;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,EAAE,GAAG,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAO,OAAO,CAAC,EACnB,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,KACtD,EAAE;;;YACxB,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,0FAA0F;gBAC1F,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;gBAC7D,KAAK,GAAG,SAAS,CAAC;gBAElB,4FAA4F;gBAC5F,IAAI,QAAQ,KAAK,SAAS,EAAE;oBAC1B,QAAQ,GAAG,wBAAwB,CAAC;oBACpC,OAAO,CAAC,IAAI,CACV,wBAAwB;wBACxB,4EAA4E;wBAC5E,4DAA4D;wBAC5D,6CAA6C,EAC7C,gCAAgC,EAChC,sCAAsC,CACvC,CAAC;iBACH;gBAED,iDAAiD;gBACjD,IAAI,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE;oBACjC,cAAc,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;iBAC3E;gBACD,MAAM,SAAS,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAEpC,uFAAuF;gBACvF,+DAA+D;gBAC/D,uBAAuB;gBAEvB,MAAM,YAAY,GAAG,IAAI,CAAC;gBAC1B,KAAI,8BAA+B,YAAY,EAAE;oBAC/C,0EAA0E;oBAC1E,IAAI,QAAwB,CAAC;oBAE7B,qDAAqD;oBACrD,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAEnD,gEAAgE;oBAChE,MAAM,qBAAqB,GAAG,UAAU,CAAC,MAAM,CAAC;oBAChD,IAAI,qBAAqB,KAAK,CAAC,EAAE;wBAC/B,4EAA4E;wBAC5E,MAAM,oBAAoB,GAAG,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,YAAY,mCAAI,MAAM,0BAA0B,EAAE,CAAC;wBAE7F,4CAA4C;wBAC5C,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;4BACzC,SAAS,EAAI,KAAK;4BAClB,QAAQ,EAAK,EAAE,IAAI,EAAE,SAAS,EAAE;4BAChC,UAAU,EAAG;gCACX,QAAQ,EAAE;oCACR;wCACE,EAAE,EAAgB,KAAK;wCACvB,IAAI,EAAc,sBAAsB;wCACxC,eAAe,EAAG,oBAAoB;wCACtC,GAAG,EAAe,MAAM;wCACxB,GAAG,EAAe,MAAM;qCACzB;iCACF;gCACD,mBAAmB,EAAE;oCACnB;wCACE,SAAS,EAAG,SAAS;wCACrB,EAAE,EAAU,KAAK;wCACjB,QAAQ,EAAI,CAAC,iBAAiB,EAAE,gBAAgB,CAAC;qCAClD;oCACD;wCACE,SAAS,EAAG,WAAW;wCACvB,EAAE,EAAU,KAAK;wCACjB,QAAQ,EAAI,CAAC,cAAc,CAAC;qCAC7B;iCACF;6BACF;yBACF,CAAC,CAAC;wBAEH,wFAAwF;wBACxF,YAAY;wBACZ,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;qBAEhF;yBAAM,IAAI,qBAAqB,KAAK,CAAC,EAAE;wBACtC,6DAA6D;wBAC7D,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;qBAE1B;yBAAM;wBACL,MAAM,IAAI,KAAK,CAAC,kEAAkE,qBAAqB,qBAAqB,CAAC,CAAC;qBAC/H;oBAED,gDAAgD;oBAChD,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;iBACjC;gBAED,2CAA2C;gBAC3C,IAAI,IAAI,KAAK,KAAK,EAAE;oBAClB,8CAA8C;oBAC9C,MAAM,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;oBAE7D,uDAAuD;oBACvD,IAAI,aAAJ,IAAI,cAAJ,IAAI,IAAJ,IAAI,GAAK,IAAI,EAAC;oBACd,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;yBACzC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;wBACpB,OAAO,CAAC,KAAK,CAAC,gBAAgB,KAAK,EAAE,CAAC,CAAC;oBACzC,CAAC,CAAC,CAAC;iBACN;aACF;YAED,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YAE/C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;;KACpD;CACF"}
+66
View File
@@ -0,0 +1,66 @@
import type { DidCreateParams, DidMessageResult, DidResolveParams, ResponseStatus, Web5Agent } from '@web5/agent';
import { DidInterface } from '@web5/agent';
/**
* Parameters for creating a DID, specifying the method, options for the DID method, and whether to
* store the DID.
*
* @typeParam method - The DID method to use for creating the DID.
* @typeParam options - Method-specific options for creating the DID.
* @typeParam store - Indicates whether the newly created DID should be stored.
*/
export type DidCreateRequest = Pick<DidCreateParams, 'method' | 'options' | 'store'>;
/**
* The response from a DID creation request, including the operation's status and, if successful,
* the created DID.
*/
export type DidCreateResponse = ResponseStatus & {
/** The result of the DID creation operation, containing the newly created DID, if successful */
did?: DidMessageResult[DidInterface.Create];
};
/**
* The response from resolving a DID, containing the DID Resolution Result.
*
* This type directly maps to the result of a DID resolution operation, providing detailed
* information about the DID document, including any DID document metadata and DID resolution
* metadata,
*/
export type DidResolveResponse = DidMessageResult[DidInterface.Resolve];
/**
* The DID API is used to resolve DIDs.
*
* @beta
*/
export declare class DidApi {
/**
* Holds the instance of a {@link Web5Agent} that represents the current execution context for
* the `DidApi`. This agent is used to process DID requests.
*/
private agent;
/** The DID of the tenant under which DID operations are being performed. */
private connectedDid;
constructor(options: {
agent: Web5Agent;
connectedDid: string;
});
/**
* Initiates the creation of a Decentralized Identifier (DID) using the specified method, options,
* and storage preference.
*
* This method sends a request to the Web5 Agent to create a new DID based on the provided method,
* with method-specific options. It also specifies whether the newly created DID should be stored.
*
* @param request - The request parameters for creating a DID, including the method, options, and
* storage flag.
* @returns A promise that resolves to a `DidCreateResponse`, which includes the operation's
* status and, if successful, the newly created DID.
*/
create(request: DidCreateRequest): Promise<DidCreateResponse>;
/**
* Resolves a DID to a DID Resolution Result.
*
* @param didUri - The DID or DID URL to resolve.
* @returns A promise that resolves to the DID Resolution Result.
*/
resolve(didUri: DidResolveParams['didUri'], options?: DidResolveParams['options']): Promise<DidResolveResponse>;
}
//# sourceMappingURL=did-api.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"did-api.d.ts","sourceRoot":"","sources":["../../src/did-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAElH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC;AAErF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG;IAC/C,gGAAgG;IAChG,GAAG,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;AAExE;;;;GAIG;AACH,qBAAa,MAAM;IACjB;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAY;IAEzB,4EAA4E;IAC5E,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAK/D;;;;;;;;;;;OAWG;IACU,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAS1E;;;;;OAKG;IACU,OAAO,CAClB,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,SAAS,CAAC,GACxE,OAAO,CAAC,kBAAkB,CAAC;CAQ/B"}
+235
View File
@@ -0,0 +1,235 @@
import type { Web5Agent, DwnMessageParams, DwnResponseStatus, DwnPaginationCursor } from '@web5/agent';
import { DwnInterface } from '@web5/agent';
import { Record } from './record.js';
import { Protocol } from './protocol.js';
/**
* Represents the request payload for configuring a protocol on a Decentralized Web Node (DWN).
*
* This request type is used to specify the configuration options for the protocol.
*/
export type ProtocolsConfigureRequest = {
/** Configuration options for the protocol. */
message: Omit<DwnMessageParams[DwnInterface.ProtocolsConfigure], 'signer'>;
};
/**
* Encapsulates the response from a protocol configuration request to a Decentralized Web Node (DWN).
*
* This response type combines the general operation status with the details of the protocol that
* was configured, if the operation was successful.
*
* @beta
*/
export type ProtocolsConfigureResponse = DwnResponseStatus & {
/** The configured protocol, if successful. */
protocol?: Protocol;
};
/**
* Defines the request structure for querying protocols from a Decentralized Web Node (DWN).
*
* This request type is used to specify the target DWN from which protocols should be queried and
* any additional query filters or options. If the `from` property is not provided, the query will
* target the local DWN. If the `from` property is provided, the query will target the specified
* remote DWN.
*/
export type ProtocolsQueryRequest = {
/** Optional DID specifying the remote target DWN tenant to be queried. */
from?: string;
/** Query filters and options that influence the results returned. */
message: Omit<DwnMessageParams[DwnInterface.ProtocolsQuery], 'signer'>;
};
/**
* Wraps the response from a protocols query, including the operation status and the list of
* protocols.
*/
export type ProtocolsQueryResponse = DwnResponseStatus & {
/** Array of protocols matching the query. */
protocols: Protocol[];
};
/**
* Type alias for {@link RecordsWriteRequest}
*/
export type RecordsCreateRequest = RecordsWriteRequest;
/**
* Type alias for {@link RecordsWriteResponse}
*/
export type RecordsCreateResponse = RecordsWriteResponse;
/**
* Represents a request to create a new record based on an existing one.
*
* This request type allows specifying the new data for the record, along with any additional
* message parameters required for the write operation.
*/
export type RecordsCreateFromRequest = {
/** The DID of the entity authoring the record. */
author: string;
/** The new data for the record. */
data: unknown;
/** ptional additional parameters for the record write operation */
message?: Omit<DwnMessageParams[DwnInterface.RecordsWrite], 'signer'>;
/** The existing record instance that is being used as a basis for the new record. */
record: Record;
};
/**
* Defines a request to delete a record from the Decentralized Web Node (DWN).
*
* This request type optionally specifies the target from which the record should be deleted and the
* message parameters for the delete operation. If the `from` property is not provided, the record
* will be deleted from the local DWN.
*/
export type RecordsDeleteRequest = {
/** Optional DID specifying the remote target DWN tenant the record will be deleted from. */
from?: string;
/** The parameters for the delete operation. */
message: Omit<DwnMessageParams[DwnInterface.RecordsDelete], 'signer'>;
};
/**
* Encapsulates a request to query records from a Decentralized Web Node (DWN).
*
* This request type is used to specify the criteria for querying records, including query
* parameters, and optionally the target DWN to query from. If the `from` property is not provided,
* the query will target the local DWN.
*/
export type RecordsQueryRequest = {
/** Optional DID specifying the remote target DWN tenant to query from and return results. */
from?: string;
/** The parameters for the query operation, detailing the criteria for selecting records. */
message: Omit<DwnMessageParams[DwnInterface.RecordsQuery], 'signer'>;
};
/**
* Represents the response from a records query operation, including status, records, and an
* optional pagination cursor.
*/
export type RecordsQueryResponse = DwnResponseStatus & {
/** Array of records matching the query. */
records?: Record[];
/** If there are additional results, the messageCid of the last record will be returned as a pagination cursor. */
cursor?: DwnPaginationCursor;
};
/**
* Represents a request to read a specific record from a Decentralized Web Node (DWN).
*
* This request type is used to specify the target DWN from which the record should be read and any
* additional parameters for the read operation. It's useful for fetching the details of a single
* record by its identifier or other criteria.
*/
export type RecordsReadRequest = {
/** Optional DID specifying the remote target DWN tenant the record will be read from. */
from?: string;
/** The parameters for the read operation, detailing the criteria for selecting the record. */
message: Omit<DwnMessageParams[DwnInterface.RecordsRead], 'signer'>;
};
/**
* Encapsulates the response from a record read operation, combining the general operation status
* with the specific record that was retrieved.
*/
export type RecordsReadResponse = DwnResponseStatus & {
/** The record retrieved by the read operation. */
record: Record;
};
/**
* Defines a request to write (create) a record to a Decentralized Web Node (DWN).
*
* This request type allows specifying the data for the new or updated record, along with any
* additional message parameters required for the write operation, and an optional flag to indicate
* whether the record should be immediately stored.
*
* @param data -
* @param message - , excluding the signer.
* @param store -
*/
export type RecordsWriteRequest = {
/** The data payload for the record, which can be of any type. */
data: unknown;
/** Optional additional parameters for the record write operation. */
message?: Omit<Partial<DwnMessageParams[DwnInterface.RecordsWrite]>, 'signer'>;
/**
* Optional flag indicating whether the record should be immediately stored. If true, the record
* is persisted in the DWN as part of the write operation. If false, the record is created,
* signed, and returned but not persisted.
*/
store?: boolean;
};
/**
* Encapsulates the response from a record write operation to a Decentralized Web Node (DWN).
*
* This request type combines the general operation status with the details of the record that was
* written, if the operation was successful.
*
* The response includes a status object that contains the HTTP-like status code and detail message
* indicating the success or failure of the write operation. If the operation was successful and a
* record was created or updated, the `record` property will contain an instance of the `Record`
* class representing the written record. This allows the caller to access the written record's
* details and perform additional operations using the provided {@link Record} instance methods.
*/
export type RecordsWriteResponse = DwnResponseStatus & {
/**
* The `Record` instance representing the record that was successfully written to the
* DWN as a result of the write operation.
*/
record?: Record;
};
/**
* Interface to interact with DWN Records and Protocols
*/
export declare class DwnApi {
/**
* Holds the instance of a {@link Web5Agent} that represents the current execution context for
* the `DwnApi`. This agent is used to process DWN requests.
*/
private agent;
/** The DID of the DWN tenant under which operations are being performed. */
private connectedDid;
constructor(options: {
agent: Web5Agent;
connectedDid: string;
});
/**
* API to interact with DWN protocols (e.g., `dwn.protocols.configure()`).
*/
get protocols(): {
/**
* Configure method, used to setup a new protocol (or update) with the passed definitions
*/
configure: (request: ProtocolsConfigureRequest) => Promise<ProtocolsConfigureResponse>;
/**
* Query the available protocols
*/
query: (request: ProtocolsQueryRequest) => Promise<ProtocolsQueryResponse>;
};
/**
* API to interact with DWN records (e.g., `dwn.records.create()`).
*/
get records(): {
/**
* Alias for the `write` method
*/
create: (request: RecordsCreateRequest) => Promise<RecordsCreateResponse>;
/**
* Write a record based on an existing one (useful for updating an existing record)
*/
createFrom: (request: RecordsCreateFromRequest) => Promise<RecordsWriteResponse>;
/**
* Delete a record
*/
delete: (request: RecordsDeleteRequest) => Promise<DwnResponseStatus>;
/**
* Query a single or multiple records based on the given filter
*/
query: (request: RecordsQueryRequest) => Promise<RecordsQueryResponse>;
/**
* Read a single record based on the given filter
*/
read: (request: RecordsReadRequest) => Promise<RecordsReadResponse>;
/**
* Writes a record to the DWN
*
* As a convenience, the Record instance returned will cache a copy of the data. This is done
* to maintain consistency with other DWN methods, like RecordsQuery, that include relatively
* small data payloads when returning RecordsWrite message properties. Regardless of data
* size, methods such as `record.data.stream()` will return the data when called even if it
* requires fetching from the DWN datastore.
*/
write: (request: RecordsWriteRequest) => Promise<RecordsWriteResponse>;
};
}
//# sourceMappingURL=dwn-api.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"dwn-api.d.ts","sourceRoot":"","sources":["../../src/dwn-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EAGT,gBAAgB,EAChB,iBAAiB,EAEjB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,8CAA8C;IAC9C,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,CAAC;CAC5E,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,0BAA0B,GAAG,iBAAiB,GAAG;IAC3D,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,qEAAqE;IACrE,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAA;CACvE,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG;IACvD,6CAA6C;IAC7C,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,mEAAmE;IACnE,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,CAAC;IACtE,qFAAqF;IACrF,MAAM,EAAE,MAAM,CAAC;CAChB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,+CAA+C;IAC/C,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,CAAC;CACvE,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,6FAA6F;IAC7F,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,4FAA4F;IAC5F,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,CAAC;CACtE,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG;IACrD,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB,kHAAkH;IAClH,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,8FAA8F;IAC9F,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;CACrE,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG;IACpD,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,iEAAiE;IACjE,IAAI,EAAE,OAAO,CAAC;IAEd,qEAAqE;IACrE,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAE/E;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG;IACrD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAC;AAEF;;GAEG;AACH,qBAAa,MAAM;IACjB;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAY;IAEzB,4EAA4E;IAC5E,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAK/D;;OAEG;IACH,IAAI,SAAS;QAET;;WAEG;6BACwB,yBAAyB,KAAG,QAAQ,0BAA0B,CAAC;QAmB1F;;WAEG;yBACoB,qBAAqB,KAAG,QAAQ,sBAAsB,CAAC;MA2BjF;IAED;;OAEG;IACH,IAAI,OAAO;QAEP;;WAEG;0BACqB,oBAAoB,KAAG,QAAQ,qBAAqB,CAAC;QAI7E;;WAEG;8BACyB,wBAAwB,KAAG,QAAQ,oBAAoB,CAAC;QA+BpF;;WAEG;0BACqB,oBAAoB,KAAG,QAAQ,iBAAiB,CAAC;QA8BzE;;WAEG;yBACoB,mBAAmB,KAAG,QAAQ,oBAAoB,CAAC;QA0D1E;;WAEG;wBACmB,kBAAkB,KAAG,QAAQ,mBAAmB,CAAC;QAyDvE;;;;;;;;WAQG;yBACoB,mBAAmB,KAAG,QAAQ,oBAAoB,CAAC;MAsC7E;CACF"}
+33
View File
@@ -0,0 +1,33 @@
/**
* Making developing with Web5 components at least 5 times easier to work with.
*
* Web5 consists of the following components:
* - Decentralized Identifiers
* - Verifiable Credentials
* - DWeb Node personal datastores
*
* The SDK sets out to gather the most oft used functionality from all three of
* these pillar technologies to provide a simple library that is as close to
* effortless as possible.
*
* The SDK is currently still under active development, but having entered the
* Tech Preview phase there is now a drive to avoid unnecessary changes unless
* backwards compatibility is provided. Additional functionality will be added
* in the lead up to 1.0 final, and modifications will be made to address
* issues and community feedback.
*
* [Link to GitHub Repo](https://github.com/TBD54566975/web5-js)
*
* @packageDocumentation
*/
export * from './did-api.js';
export * from './dwn-api.js';
export * from './protocol.js';
export * from './record.js';
export * from './vc-api.js';
export * from './web5.js';
export * from './tech-preview.js';
export * from './web-features.js';
import * as utils from './utils.js';
export { utils };
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAElC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,CAAC"}
+59
View File
@@ -0,0 +1,59 @@
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
import type { DwnMessage, DwnResponseStatus, Web5Agent } from '@web5/agent';
import { DwnInterface } from '@web5/agent';
/**
* Represents metadata associated with a protocol, including the author and an optional message CID.
*/
export type ProtocolMetadata = {
/** The author of the protocol. */
author: string;
/**
* The Content Identifier (CID) of a ProtocolsConfigure message.
*
* This is an optional field, and is used by {@link Protocol.send}.
*/
messageCid?: string;
};
/**
* Encapsulates a DWN Protocol with its associated metadata and configuration.
*
* This class primarly exists to provide developers with a convenient way to configure/install
* protocols on remote DWNs.
*/
export declare class Protocol {
/** The {@link Web5Agent} instance that handles DWNs requests. */
private _agent;
/** The ProtocolsConfigureMessage containing the detailed configuration for the protocol. */
private _metadata;
/** Metadata associated with the protocol, including the author and optional message CID. */
private _protocolsConfigureMessage;
/**
* Constructs a new instance of the Protocol class.
*
* @param agent - The Web5Agent instance used for network interactions.
* @param protocolsConfigureMessage - The configuration message containing the protocol details.
* @param metadata - Metadata associated with the protocol, including the author and optional message CID.
*/
constructor(agent: Web5Agent, protocolsConfigureMessage: DwnMessage[DwnInterface.ProtocolsConfigure], metadata: ProtocolMetadata);
/**
* Retrieves the protocol definition from the protocol's configuration message.
* @returns The protocol definition.
*/
get definition(): import("@tbd54566975/dwn-sdk-js").ProtocolDefinition;
/**
* Serializes the protocol's configuration message to JSON.
* @returns The serialized JSON object of the protocol's configuration message.
*/
toJSON(): import("@tbd54566975/dwn-sdk-js").ProtocolsConfigureMessage;
/**
* Sends the protocol configuration to a remote DWN identified by the target DID.
*
* @param target - The DID of the target DWN to which the protocol configuration will be installed.
* @returns A promise that resolves to an object containing the status of the send operation.
*/
send(target: string): Promise<DwnResponseStatus>;
}
//# sourceMappingURL=protocol.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,iEAAiE;IACjE,OAAO,CAAC,MAAM,CAAY;IAE1B,4FAA4F;IAC5F,OAAO,CAAC,SAAS,CAAmB;IAEpC,4FAA4F;IAC5F,OAAO,CAAC,0BAA0B,CAA8C;IAEhF;;;;;;OAMG;gBACS,KAAK,EAAE,SAAS,EAAE,yBAAyB,EAAE,UAAU,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,QAAQ,EAAE,gBAAgB;IAMhI;;;OAGG;IACH,IAAI,UAAU,yDAEb;IAED;;;OAGG;IACH,MAAM;IAIN;;;;;OAKG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAUvD"}
+375
View File
@@ -0,0 +1,375 @@
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
import type { Readable } from '@web5/common';
import { Web5Agent, DwnMessage, DwnResponseStatus, DwnMessageDescriptor, DwnDateSort, DwnPaginationCursor } from '@web5/agent';
import { DwnInterface } from '@web5/agent';
/**
* Represents the structured data model of a record, encapsulating the essential fields that define
* the record's metadata and payload within a Decentralized Web Node (DWN).
*
* @beta
*/
export type RecordModel = DwnMessageDescriptor[DwnInterface.RecordsWrite] & Omit<DwnMessage[DwnInterface.RecordsWrite], 'descriptor' | 'recordId'> & {
/** The DID that signed the record. */
author: string;
/** The protocol role under which this record is written. */
protocolRole?: RecordOptions['protocolRole'];
/** The unique identifier of the record. */
recordId?: string;
};
/**
* Options for configuring a {@link Record} instance, extending the base `RecordsWriteMessage` with
* additional properties.
*
* This type combines the standard fields required for writing DWN records with additional metadata
* and configuration options used specifically in the {@link Record} class.
*
* @beta
*/
export type RecordOptions = DwnMessage[DwnInterface.RecordsWrite] & {
/** The DID that signed the record. */
author: string;
/** The DID of the DWN tenant under which record operations are being performed. */
connectedDid: string;
/** The data of the record, either as a Base64 URL encoded string or a Blob. */
encodedData?: string | Blob;
/**
* A stream of data, conforming to the `Readable` or `ReadableStream` interface, providing a
* mechanism to read the record's data sequentially. This is particularly useful for handling
* large datasets that should not be loaded entirely in memory, allowing for efficient, chunked
* processing of the record's data.
*/
data?: Readable | ReadableStream;
/** The initial `RecordsWriteMessage` that represents the initial state/version of the record. */
initialWrite?: DwnMessage[DwnInterface.RecordsWrite];
/** The protocol role under which this record is written. */
protocolRole?: string;
/** The remote tenant DID if the record was queried or read from a remote DWN. */
remoteOrigin?: string;
};
/**
* Parameters for updating a DWN record.
*
* This type specifies the set of properties that can be updated on an existing record. It is used
* to convey the new state or changes to be applied to the record.
*
* @beta
*/
export type RecordUpdateParams = {
/**
* The new data for the record, which can be of any type. This data will replace the existing
* data of the record. It's essential to ensure that this data is compatible with the record's
* schema or data format expectations.
*/
data?: unknown;
/**
* The Content Identifier (CID) of the data. Updating this value changes the reference to the data
* associated with the record.
*/
dataCid?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['dataCid'];
/** The size of the data in bytes. */
dataSize?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['dataSize'];
/** The timestamp indicating when the record was last modified. */
dateModified?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['messageTimestamp'];
/** The timestamp indicating when the record was published. */
datePublished?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['datePublished'];
/** The protocol role under which this record is written. */
protocolRole?: RecordOptions['protocolRole'];
/** The published status of the record. */
published?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['published'];
/** The tags associated with the updated record */
tags?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['tags'];
};
/**
* Record wrapper class with convenience methods to send and update,
* aside from manipulating and reading the record data.
*
* Note: The `messageTimestamp` of the most recent RecordsWrite message is
* logically equivalent to the date/time at which a Record was most
* recently modified. Since this Record class implementation is
* intended to simplify the developer experience of working with
* logical records (and not individual DWN messages) the
* `messageTimestamp` is mapped to `dateModified`.
*
* @beta
*/
/**
* The `Record` class encapsulates a single record's data and metadata, providing a more
* developer-friendly interface for working with Decentralized Web Node (DWN) records.
*
* Methods are provided to read, update, and manage the record's lifecycle, including writing to
* remote DWNs.
*
* @beta
*/
export declare class Record implements RecordModel {
/**
* Cache to minimize the amount of redundant two-phase commits we do in store() and send()
* Retains awareness of the last 100 records stored/sent for up to 100 target DIDs each.
*/
private static _sendCache;
/** The {@link Web5Agent} instance that handles DWNs requests. */
private _agent;
/** The DID of the DWN tenant under which operations are being performed. */
private _connectedDid;
/** Encoded data of the record, if available. */
private _encodedData?;
/** Stream of the record's data. */
private _readableStream?;
/** The origin DID if the record was fetched from a remote DWN. */
private _remoteOrigin?;
/** The DID of the entity that authored the record. */
private _author;
/** Attestation JWS signature. */
private _attestation?;
/** Authorization signature(s). */
private _authorization?;
/** Context ID associated with the record. */
private _contextId?;
/** Descriptor detailing the record's schema, format, and other metadata. */
private _descriptor;
/** Encryption details for the record, if the data is encrypted. */
private _encryption?;
/** Initial state of the record before any updates. */
private _initialWrite;
/** Flag indicating if the initial write has been stored, to prevent duplicates. */
private _initialWriteStored;
/** Flag indicating if the initial write has been signed by the owner. */
private _initialWriteSigned;
/** Unique identifier of the record. */
private _recordId;
/** Role under which the record is written. */
private _protocolRole;
/** Record's signatures attestation */
get attestation(): DwnMessage[DwnInterface.RecordsWrite]['attestation'];
/** Record's signatures attestation */
get authorization(): DwnMessage[DwnInterface.RecordsWrite]['authorization'];
/** DID that signed the record. */
get author(): string;
/** Record's context ID */
get contextId(): string;
/** Record's data format */
get dataFormat(): string;
/** Record's creation date */
get dateCreated(): string;
/** Record's encryption */
get encryption(): DwnMessage[DwnInterface.RecordsWrite]['encryption'];
/** Record's initial write if the record has been updated */
get initialWrite(): RecordOptions['initialWrite'];
/** Record's ID */
get id(): string;
/** Interface is always `Records` */
get interface(): import("@tbd54566975/dwn-sdk-js").DwnInterfaceName.Records;
/** Method is always `Write` */
get method(): import("@tbd54566975/dwn-sdk-js").DwnMethodName.Write;
/** Record's parent ID */
get parentId(): string;
/** Record's protocol */
get protocol(): string;
/** Record's protocol path */
get protocolPath(): string;
/** Role under which the author is writing the record */
get protocolRole(): string;
/** Record's recipient */
get recipient(): string;
/** Record's schema */
get schema(): string;
/** Record's CID */
get dataCid(): string;
/** Record's data size */
get dataSize(): number;
/** Record's modified date */
get dateModified(): string;
/** Record's published date */
get datePublished(): string;
/** Record's published status */
get messageTimestamp(): string;
/** Record's published status (true/false) */
get published(): boolean;
/** Tags of the record */
get tags(): import("@tbd54566975/dwn-sdk-js").RecordsWriteTags;
/**
* Returns a copy of the raw `RecordsWriteMessage` that was used to create the current `Record` instance.
*/
private get rawMessage();
constructor(agent: Web5Agent, options: RecordOptions);
/**
* Returns the data of the current record.
* If the record data is not available, it attempts to fetch the data from the DWN.
* @returns a data stream with convenience methods such as `blob()`, `json()`, `text()`, and `stream()`, similar to the fetch API response
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
get data(): {
/**
* Returns the data of the current record as a `Blob`.
*
* @returns A promise that resolves to a Blob containing the record's data.
* @throws If the record data is not available or cannot be converted to a `Blob`.
*
* @beta
*/
blob(): Promise<Blob>;
/**
* Returns the data of the current record as a `Uint8Array`.
*
* @returns A Promise that resolves to a `Uint8Array` containing the record's data bytes.
* @throws If the record data is not available or cannot be converted to a byte array.
*
* @beta
*/
bytes(): Promise<Uint8Array>;
/**
* Parses the data of the current record as JSON and returns it as a JavaScript object.
*
* @returns A Promise that resolves to a JavaScript object parsed from the record's JSON data.
* @throws If the record data is not available, not in JSON format, or cannot be parsed.
*
* @beta
*/
json(): Promise<any>;
/**
* Returns the data of the current record as a `string`.
*
* @returns A promise that resolves to a `string` containing the record's text data.
* @throws If the record data is not available or cannot be converted to text.
*
* @beta
*/
text(): Promise<string>;
/**
* Provides a `Readable` stream containing the record's data.
*
* @returns A promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If the record data is not available in-memory and cannot be fetched.
*
* @beta
*/
stream(): Promise<Readable>;
/**
* Attaches callbacks for the resolution and/or rejection of the `Promise` returned by
* `stream()`.
*
* This method is a proxy to the `then` method of the `Promise` returned by `stream()`,
* allowing for a seamless integration with promise-based workflows.
* @param onFulfilled - A function to asynchronously execute when the `stream()` promise
* becomes fulfilled.
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` for the completion of which ever callback is executed.
*/
then(onFulfilled?: (value: Readable) => Readable | PromiseLike<Readable>, onRejected?: (reason: any) => PromiseLike<never>): any;
/**
* Attaches a rejection handler callback to the `Promise` returned by the `stream()` method.
* This method is a shorthand for `.then(undefined, onRejected)`, specifically designed for handling
* rejection cases in the promise chain initiated by accessing the record's data. It ensures that
* errors during data retrieval or processing can be caught and handled appropriately.
*
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` that resolves to the value of the callback if it is called, or to its
* original fulfillment value if the promise is instead fulfilled.
*/
catch(onRejected?: (reason: any) => PromiseLike<never>): any;
};
/**
* Stores the current record state as well as any initial write to the owner's DWN.
*
* @param importRecord - if true, the record will signed by the owner before storing it to the owner's DWN. Defaults to false.
* @returns the status of the store request
*
* @beta
*/
store(importRecord?: boolean): Promise<DwnResponseStatus>;
/**
* Signs the current record state as well as any initial write and optionally stores it to the owner's DWN.
* This is useful when importing a record that was signed by someone else into your own DWN.
*
* @param store - if true, the record will be stored to the owner's DWN after signing. Defaults to true.
* @returns the status of the import request
*
* @beta
*/
import(store?: boolean): Promise<DwnResponseStatus>;
/**
* Send the current record to a remote DWN by specifying their DID
* If no DID is specified, the target is assumed to be the owner (connectedDID).
* If an initial write is present and the Record class send cache has no awareness of it, the initial write is sent first
* (vs waiting for the regular DWN sync)
*
* @param target - the optional DID to send the record to, if none is set it is sent to the connectedDid
* @returns the status of the send record request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
send(target?: string): Promise<DwnResponseStatus>;
/**
* Returns a JSON representation of the Record instance.
* It's called by `JSON.stringify(...)` automatically.
*/
toJSON(): RecordModel;
/**
* Convenience method to return the string representation of the Record instance.
* Called automatically in string concatenation, String() type conversion, and template literals.
*/
toString(): string;
/**
* Returns a pagination cursor for the current record given a sort order.
*
* @param sort the sort order to use for the pagination cursor.
* @returns A promise that resolves to a pagination cursor for the current record.
*/
paginationCursor(sort: DwnDateSort): Promise<DwnPaginationCursor>;
/**
* Update the current record on the DWN.
* @param params - Parameters to update the record.
* @returns the status of the update request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
update({ dateModified, data, ...params }: RecordUpdateParams): Promise<DwnResponseStatus>;
/**
* Handles the various conditions around there being an initial write, whether to store initial/current state,
* and whether to add an owner signature to the initial write to enable storage when protocol rules require it.
*/
private processRecord;
/**
* Fetches the record's data from the specified DWN.
*
* This private method is called when the record data is not available in-memory
* and needs to be fetched from either a local or a remote DWN.
* It makes a read request to the specified DWN and processes the response to provide
* a Node.js `Readable` stream of the record's data.
*
* @param params - Parameters for fetching the record's data.
* @param params.target - The DID of the DWN to fetch the data from.
* @param params.isRemote - Indicates whether the target DWN is a remote node.
* @returns A Promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If there is an error while fetching or processing the data from the DWN.
*
* @beta
*/
private readRecordData;
/**
* Verifies if the properties to be mutated are mutable.
*
* This private method is used to ensure that only mutable properties of the `Record` instance
* are being changed. It checks whether the properties specified for mutation are among the
* set of properties that are allowed to be modified. If any of the properties to be mutated
* are not in the set of mutable properties, the method throws an error.
*
* @param propertiesToMutate - An iterable of property names that are intended to be mutated.
* @param mutableDescriptorProperties - A set of property names that are allowed to be mutated.
*
* @throws If any of the properties in `propertiesToMutate` are not in `mutableDescriptorProperties`.
*
* @beta
*/
private static verifyPermittedMutation;
}
//# sourceMappingURL=record.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=service-worker.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"service-worker.d.ts","sourceRoot":"","sources":["../../src/service-worker.ts"],"names":[],"mappings":""}
+8
View File
@@ -0,0 +1,8 @@
/**
* Dynamically selects up to 2 DWN endpoints that are provided
* by default during the Tech Preview period.
*
* @beta
*/
export declare function getTechPreviewDwnEndpoints(): Promise<string[]>;
//# sourceMappingURL=tech-preview.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"tech-preview.d.ts","sourceRoot":"","sources":["../../src/tech-preview.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CA0CpE"}
+85
View File
@@ -0,0 +1,85 @@
/**
* Converts various data types to a `Blob` object, automatically detecting the data type or using
* the specified `dataFormat` to set the Blob's MIME type.
*
* This function supports plain text, JSON objects, binary data (Uint8Array, ArrayBuffer), and Blob
* inputs and will attempt to automatically detect the type of the data if `dataFormat` is not
* explicitly provided.
*
* @beta
*
* @example
* ```ts
* // Convert a JSON object to a Blob
* const { dataBlob, dataFormat } = dataToBlob({ key: 'value' }, 'application/json');
*
* // Convert a plain text string to a Blob without specifying dataFormat
* const { dataBlob: textBlob } = dataToBlob('Hello, world!');
*
* // Convert binary data to a Blob
* const binaryData = new Uint8Array([0, 1, 2, 3]);
* const { dataBlob: binaryBlob } = dataToBlob(binaryData);
* ```
*
* @param data - The data to be converted into a `Blob`. This can be a string, an object, binary
* data (Uint8Array or ArrayBuffer), or a Blob.
* @param dataFormat - An optional MIME type string that specifies the format of the data. Common
* types include 'text/plain' for string data, 'application/json' for JSON
* objects, and 'application/octet-stream' for binary data. If not provided, the
* function will attempt to detect the format based on the data type or default
* to 'application/octet-stream'.
* @returns An object containing the `dataBlob`, a Blob representation of the input data, and
* `dataFormat`, the MIME type of the data as determined by the function or specified by the caller.
* @throws An error if the data type is not supported or cannot be converted to a Blob.
*/
export declare function dataToBlob(data: any, dataFormat?: string): {
/** A Blob representation of the input data. */
dataBlob: Blob;
/** The MIME type of the data. */
dataFormat: string;
};
/**
* The `SendCache` class provides a static caching mechanism to optimize the process of sending
* records to remote DWN targets by minimizing redundant sends.
*
* It maintains a cache of record IDs and their associated target DIDs to which they have been sent.
* This helps in avoiding unnecessary network requests and ensures efficient data synchronization
* across Decentralized Web Nodes (DWNs).
*
* The cache employs a simple eviction policy to maintain a manageable size, ensuring that the cache
* does not grow indefinitely and consume excessive memory resources.
*
* @beta
*/
export declare class SendCache {
/**
* A private static map that serves as the core storage mechanism for the cache. It maps record
* IDs to a set of target DIDs, indicating which records have been sent to which targets.
*/
private static cache;
/**
* The maximum number of entries allowed in the cache. Once this limit is exceeded, the oldest
* entries are evicted to make room for new ones. This limit applies both to the number of records
* and the number of targets per record.
*/
private static sendCacheLimit;
/**
* Checks if a given record ID has been sent to a specified target DID. This method is used to
* determine whether a send operation is necessary or if it can be skipped to avoid redundancy.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to check against.
* @returns A boolean indicating whether the record has been sent to the target.
*/
static check(id: string, target: string): boolean;
/**
* Adds or updates an entry in the cache for a given record ID and target DID. If the cache
* exceeds its size limit, the oldest entry is removed. This method ensures that the cache
* reflects the most recent sends.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to which the record has been sent.
*/
static set(id: string, target: string): void;
}
//# sourceMappingURL=utils.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG;IAC1D,+CAA+C;IAC/C,QAAQ,EAAE,IAAI,CAAC;IACf,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB,CAqBA;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,SAAS;IACpB;;;MAGE;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAkC;IAEtD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc,CAAO;IAEpC;;;;;;;OAOG;WACW,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAKxD;;;;;;;OAOG;WACW,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAepD"}
+24
View File
@@ -0,0 +1,24 @@
import type { Web5Agent } from '@web5/agent';
/**
* The VC API is used to issue, present and verify VCs
*
* @beta
*/
export declare class VcApi {
/**
* Holds the instance of a {@link Web5Agent} that represents the current execution context for
* the `VcApi`. This agent is used to process VC requests.
*/
private agent;
/** The DID of the tenant under which DID operations are being performed. */
private connectedDid;
constructor(options: {
agent: Web5Agent;
connectedDid: string;
});
/**
* Issues a VC (Not implemented yet)
*/
create(): Promise<void>;
}
//# sourceMappingURL=vc-api.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"vc-api.d.ts","sourceRoot":"","sources":["../../src/vc-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;GAIG;AACH,qBAAa,KAAK;IAChB;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAY;IAEzB,4EAA4E;IAC5E,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAK/D;;OAEG;IACG,MAAM;CAIb"}
+5
View File
@@ -0,0 +1,5 @@
/**
* Installs the DWeb networking features in the current environment.
*/
export declare function installNetworkingFeatures(path: string): void;
//# sourceMappingURL=web-features.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"web-features.d.ts","sourceRoot":"","sources":["../../src/web-features.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAqB5D"}
+136
View File
@@ -0,0 +1,136 @@
import type { HdIdentityVault, Web5Agent } from '@web5/agent';
import { VcApi } from './vc-api.js';
import { DwnApi } from './dwn-api.js';
import { DidApi } from './did-api.js';
/** Override defaults configured during the technical preview phase. */
export type TechPreviewOptions = {
/** Override default dwnEndpoints provided for technical preview. */
dwnEndpoints?: string[];
};
/** Optional overrides that can be provided when calling {@link Web5.connect}. */
export type Web5ConnectOptions = {
/**
* Provide a {@link Web5Agent} implementation. Defaults to creating a local
* {@link Web5UserAgent} if one isn't provided
**/
agent?: Web5Agent;
/**
* Provide an instance of a {@link HdIdentityVault} implementation. Defaults to
* a LevelDB-backed store with an insecure, static unlock password if one
* isn't provided. To allow the app user to enter a secure password of
* their choosing, provide an initialized {@link HdIdentityVault} instance.
**/
agentVault?: HdIdentityVault;
/** Specify an existing DID to connect to. */
connectedDid?: string;
/**
* The Web5 app `password` is used to protect data on the device the application is running on.
*
* Only the end user should know this password: it should not be stored on the device or
* transmitted over the network.
*
* This password is crucial for the security of an identity vault that stores the local Agent's
* cryptographic keys and decentralized identifier (DID). The vault's content is encrypted using
* the password, making it accessible only to those who know the password.
*
* App users should be advised to use a strong, unique passphrase that is not shared across
* different services or applications. The password should be kept confidential and not be
* exposed to unauthorized entities. Losing the password may result in irreversible loss of
* access to the vault's contents.
*/
password?: string;
/**
* The `recoveryPhrase` is a unique, secure key for recovering the identity vault.
*
* This phrase is a series of 12 words generated securely and known only to the user. It plays a
* critical role in the security of the identity vault by enabling the recovery of the vault's
* contents, including cryptographic keys and the Agent's decentralized identifier (DID), across
* different devices or if the original device is compromised or lost.
*
* The recovery phrase is akin to a master key, as anyone with access to this phrase can restore
* and access the vault's contents. Its combined with the app `password` to encrypt the vault's
* content.
*
* Unlike a password, the recovery phrase is not intended for regular use but as a secure backup
* method for vault recovery. Losing this phrase can result in permanent loss of access to the
* vault's contents, as it cannot be reset or retrieved if forgotten.
*
* Users should treat the recovery phrase with the highest level of security, ensuring it is
* never shared, stored online, or exposed to potential threats. It is the user's responsibility
* to keep this phrase safe to maintain the integrity and accessibility of their secured data. It
* is recommended to write it down and store it in a secure location, separate from the device and
* digital backups.
*/
recoveryPhrase?: string;
/**
* Enable synchronization of DWN records between local and remote DWNs.
* Sync defaults to running every 2 minutes and can be set to any value accepted by `ms()`.
* To disable sync set to 'off'.
*/
sync?: string;
/**
* Override defaults configured during the technical preview phase.
* See {@link TechPreviewOptions} for available options.
*/
techPreview?: TechPreviewOptions;
};
/**
* Represents the result of the Web5 connection process, including the Web5 instance,
* the connected decentralized identifier (DID), and optionally the recovery phrase used
* during the agent's initialization.
*/
export type Web5ConnectResult = {
/** The Web5 instance, providing access to the agent, DID, DWN, and VC APIs. */
web5: Web5;
/** The DID that has been connected or created during the connection process. */
did: string;
/**
* The first time a Web5 agent is initialized, the recovery phrase that was used to generate the
* agent's DID and keys is returned. This phrase can be used to recover the agent's vault contents
* and should be stored securely by the user.
*/
recoveryPhrase?: string;
};
/**
* Parameters that are passed to Web5 constructor.
*
* @see {@link Web5ConnectOptions}
*/
export type Web5Params = {
/**
* A {@link Web5Agent} instance that handles DIDs, DWNs and VCs requests. The agent manages the
* user keys and identities, and is responsible to sign and verify messages.
*/
agent: Web5Agent;
/** The DID of the tenant under which all DID, DWN, and VC requests are being performed. */
connectedDid: string;
};
/**
* The main Web5 API interface. It manages the creation of a DID if needed, the connection to the
* local DWN and all the web5 main foundational APIs such as VC, syncing, etc.
*/
export declare class Web5 {
/**
* A {@link Web5Agent} instance that handles DIDs, DWNs and VCs requests. The agent manages the
* user keys and identities, and is responsible to sign and verify messages.
*/
agent: Web5Agent;
/** Exposed instance to the DID APIs, allow users to create and resolve DIDs */
did: DidApi;
/** Exposed instance to the DWN APIs, allow users to read/write records */
dwn: DwnApi;
/** Exposed instance to the VC APIs, allow users to issue, present and verify VCs */
vc: VcApi;
/** The DID of the tenant under which DID operations are being performed. */
private connectedDid;
constructor({ agent, connectedDid }: Web5Params);
/**
* Connects to a {@link Web5Agent}. Defaults to creating a local {@link Web5UserAgent} if one
* isn't provided.
*
* @param options - Optional overrides that can be provided when calling {@link Web5.connect}.
* @returns A promise that resolves to a {@link Web5} instance and the connected DID.
*/
static connect({ agent, agentVault, connectedDid, password, recoveryPhrase, sync, techPreview }?: Web5ConnectOptions): Promise<Web5ConnectResult>;
}
//# sourceMappingURL=web5.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"web5.d.ts","sourceRoot":"","sources":["../../src/web5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI9E,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,CAAA;AAED,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;QAGI;IACJ,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;;;QAKI;IACJ,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,+EAA+E;IAC/E,IAAI,EAAE,IAAI,CAAC;IAEX,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB,2FAA2F;IAC3F,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,qBAAa,IAAI;IACf;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IAEZ,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IAEZ,oFAAoF;IACpF,EAAE,EAAE,KAAK,CAAC;IAEV,4EAA4E;IAC5E,OAAO,CAAC,YAAY,CAAS;gBAEjB,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,UAAU;IAQ/C;;;;;;OAMG;WACU,OAAO,CAAC,EACnB,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAC7E,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA0GxD"}
+112
View File
@@ -0,0 +1,112 @@
{
"name": "@web5/api",
"version": "0.9.4",
"description": "SDK for accessing the features and capabilities of Web5",
"type": "module",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"homepage": "https://github.com/TBD54566975/web5-js/tree/main/packages/api#readme",
"bugs": "https://github.com/TBD54566975/web5-js/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/TBD54566975/web5-js.git",
"directory": "packages/api"
},
"license": "Apache-2.0",
"contributors": [
{
"name": "Daniel Buchner",
"url": "https://github.com/csuwildcat"
},
{
"name": "Frank Hinek",
"url": "https://github.com/frankhinek"
},
{
"name": "Moe Jangda",
"url": "https://github.com/mistermoe"
}
],
"files": [
"dist",
"src"
],
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
},
"./browser": {
"types": "./dist/types/index.d.ts",
"import": "./dist/browser.mjs",
"require": "./dist/browser.js"
}
},
"react-native": "./dist/esm/index.js",
"keywords": [
"decentralized",
"decentralized-applications",
"decentralized-identity",
"decentralized-web",
"DID",
"sdk",
"verifiable-credentials",
"web5",
"web5-sdk"
],
"publishConfig": {
"access": "public",
"provenance": true
},
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
"@web5/agent": "0.3.8",
"@web5/common": "1.0.0",
"@web5/crypto": "1.0.0",
"@web5/dids": "1.1.0",
"@web5/user-agent": "0.3.8"
},
"devDependencies": {
"@playwright/test": "1.40.1",
"@tbd54566975/dwn-sdk-js": "0.3.5",
"@types/chai": "4.3.6",
"@types/eslint": "8.56.10",
"@types/mocha": "10.0.1",
"@types/node": "20.11.19",
"@types/sinon": "17.0.2",
"@typescript-eslint/eslint-plugin": "7.9.0",
"@typescript-eslint/parser": "7.9.0",
"@web/test-runner": "0.18.0",
"@web/test-runner-playwright": "0.11.0",
"c8": "9.1.0",
"chai": "4.3.10",
"esbuild": "0.19.8",
"eslint": "9.3.0",
"eslint-plugin-mocha": "10.4.3",
"mocha": "10.2.0",
"mocha-junit-reporter": "2.2.1",
"node-stdlib-browser": "1.2.0",
"playwright": "1.40.1",
"rimraf": "4.4.0",
"sinon": "16.1.3",
"source-map-loader": "4.0.2",
"typescript": "5.1.6"
},
"scripts": {
"clean": "rimraf dist tests/compiled",
"build:esm": "rimraf dist/esm dist/types && pnpm tsc -p tsconfig.json",
"build:cjs": "rimraf dist/cjs && pnpm tsc -p tsconfig.cjs.json && echo '{\"type\": \"commonjs\"}' > ./dist/cjs/package.json",
"build:browser": "rimraf dist/browser.mjs dist/browser.js && node build/bundles.js",
"build:tests:node": "rimraf tests/compiled && pnpm tsc -p tests/tsconfig.json",
"build:tests:browser": "rimraf tests/compiled && node build/esbuild-tests.cjs",
"build": "pnpm clean && pnpm build:esm && pnpm build:cjs && pnpm build:browser",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"test:node": "pnpm build:tests:node && pnpm c8 mocha",
"test:browser": "pnpm build:tests:browser && web-test-runner"
}
}
+90
View File
@@ -0,0 +1,90 @@
import type { DidCreateParams, DidMessageResult, DidResolveParams, ResponseStatus, Web5Agent } from '@web5/agent';
import { DidInterface } from '@web5/agent';
/**
* Parameters for creating a DID, specifying the method, options for the DID method, and whether to
* store the DID.
*
* @typeParam method - The DID method to use for creating the DID.
* @typeParam options - Method-specific options for creating the DID.
* @typeParam store - Indicates whether the newly created DID should be stored.
*/
export type DidCreateRequest = Pick<DidCreateParams, 'method' | 'options' | 'store'>;
/**
* The response from a DID creation request, including the operation's status and, if successful,
* the created DID.
*/
export type DidCreateResponse = ResponseStatus & {
/** The result of the DID creation operation, containing the newly created DID, if successful */
did?: DidMessageResult[DidInterface.Create];
};
/**
* The response from resolving a DID, containing the DID Resolution Result.
*
* This type directly maps to the result of a DID resolution operation, providing detailed
* information about the DID document, including any DID document metadata and DID resolution
* metadata,
*/
export type DidResolveResponse = DidMessageResult[DidInterface.Resolve];
/**
* The DID API is used to resolve DIDs.
*
* @beta
*/
export class DidApi {
/**
* Holds the instance of a {@link Web5Agent} that represents the current execution context for
* the `DidApi`. This agent is used to process DID requests.
*/
private agent: Web5Agent;
/** The DID of the tenant under which DID operations are being performed. */
private connectedDid: string;
constructor(options: { agent: Web5Agent, connectedDid: string }) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* Initiates the creation of a Decentralized Identifier (DID) using the specified method, options,
* and storage preference.
*
* This method sends a request to the Web5 Agent to create a new DID based on the provided method,
* with method-specific options. It also specifies whether the newly created DID should be stored.
*
* @param request - The request parameters for creating a DID, including the method, options, and
* storage flag.
* @returns A promise that resolves to a `DidCreateResponse`, which includes the operation's
* status and, if successful, the newly created DID.
*/
public async create(request: DidCreateRequest): Promise<DidCreateResponse> {
const { result, ...status } = await this.agent.processDidRequest({
messageType : DidInterface.Create,
messageParams : { ...request }
});
return { did: result, ...status };
}
/**
* Resolves a DID to a DID Resolution Result.
*
* @param didUri - The DID or DID URL to resolve.
* @returns A promise that resolves to the DID Resolution Result.
*/
public async resolve(
didUri: DidResolveParams['didUri'], options?: DidResolveParams['options']
): Promise<DidResolveResponse> {
const { result: didResolutionResult } = await this.agent.processDidRequest({
messageParams : { didUri, options },
messageType : DidInterface.Resolve
});
return didResolutionResult;
}
}
+530
View File
@@ -0,0 +1,530 @@
import type {
Web5Agent,
DwnMessage,
DwnResponse,
DwnMessageParams,
DwnResponseStatus,
ProcessDwnRequest,
DwnPaginationCursor,
} from '@web5/agent';
import { isEmptyObject } from '@web5/common';
import { DwnInterface, getRecordAuthor } from '@web5/agent';
import { Record } from './record.js';
import { dataToBlob } from './utils.js';
import { Protocol } from './protocol.js';
/**
* Represents the request payload for configuring a protocol on a Decentralized Web Node (DWN).
*
* This request type is used to specify the configuration options for the protocol.
*/
export type ProtocolsConfigureRequest = {
/** Configuration options for the protocol. */
message: Omit<DwnMessageParams[DwnInterface.ProtocolsConfigure], 'signer'>;
}
/**
* Encapsulates the response from a protocol configuration request to a Decentralized Web Node (DWN).
*
* This response type combines the general operation status with the details of the protocol that
* was configured, if the operation was successful.
*
* @beta
*/
export type ProtocolsConfigureResponse = DwnResponseStatus & {
/** The configured protocol, if successful. */
protocol?: Protocol;
}
/**
* Defines the request structure for querying protocols from a Decentralized Web Node (DWN).
*
* This request type is used to specify the target DWN from which protocols should be queried and
* any additional query filters or options. If the `from` property is not provided, the query will
* target the local DWN. If the `from` property is provided, the query will target the specified
* remote DWN.
*/
export type ProtocolsQueryRequest = {
/** Optional DID specifying the remote target DWN tenant to be queried. */
from?: string;
/** Query filters and options that influence the results returned. */
message: Omit<DwnMessageParams[DwnInterface.ProtocolsQuery], 'signer'>
}
/**
* Wraps the response from a protocols query, including the operation status and the list of
* protocols.
*/
export type ProtocolsQueryResponse = DwnResponseStatus & {
/** Array of protocols matching the query. */
protocols: Protocol[];
}
/**
* Type alias for {@link RecordsWriteRequest}
*/
export type RecordsCreateRequest = RecordsWriteRequest;
/**
* Type alias for {@link RecordsWriteResponse}
*/
export type RecordsCreateResponse = RecordsWriteResponse;
/**
* Represents a request to create a new record based on an existing one.
*
* This request type allows specifying the new data for the record, along with any additional
* message parameters required for the write operation.
*/
export type RecordsCreateFromRequest = {
/** The DID of the entity authoring the record. */
author: string;
/** The new data for the record. */
data: unknown;
/** ptional additional parameters for the record write operation */
message?: Omit<DwnMessageParams[DwnInterface.RecordsWrite], 'signer'>;
/** The existing record instance that is being used as a basis for the new record. */
record: Record;
}
/**
* Defines a request to delete a record from the Decentralized Web Node (DWN).
*
* This request type optionally specifies the target from which the record should be deleted and the
* message parameters for the delete operation. If the `from` property is not provided, the record
* will be deleted from the local DWN.
*/
export type RecordsDeleteRequest = {
/** Optional DID specifying the remote target DWN tenant the record will be deleted from. */
from?: string;
/** The parameters for the delete operation. */
message: Omit<DwnMessageParams[DwnInterface.RecordsDelete], 'signer'>;
}
/**
* Encapsulates a request to query records from a Decentralized Web Node (DWN).
*
* This request type is used to specify the criteria for querying records, including query
* parameters, and optionally the target DWN to query from. If the `from` property is not provided,
* the query will target the local DWN.
*/
export type RecordsQueryRequest = {
/** Optional DID specifying the remote target DWN tenant to query from and return results. */
from?: string;
/** The parameters for the query operation, detailing the criteria for selecting records. */
message: Omit<DwnMessageParams[DwnInterface.RecordsQuery], 'signer'>;
}
/**
* Represents the response from a records query operation, including status, records, and an
* optional pagination cursor.
*/
export type RecordsQueryResponse = DwnResponseStatus & {
/** Array of records matching the query. */
records?: Record[]
/** If there are additional results, the messageCid of the last record will be returned as a pagination cursor. */
cursor?: DwnPaginationCursor;
};
/**
* Represents a request to read a specific record from a Decentralized Web Node (DWN).
*
* This request type is used to specify the target DWN from which the record should be read and any
* additional parameters for the read operation. It's useful for fetching the details of a single
* record by its identifier or other criteria.
*/
export type RecordsReadRequest = {
/** Optional DID specifying the remote target DWN tenant the record will be read from. */
from?: string;
/** The parameters for the read operation, detailing the criteria for selecting the record. */
message: Omit<DwnMessageParams[DwnInterface.RecordsRead], 'signer'>;
}
/**
* Encapsulates the response from a record read operation, combining the general operation status
* with the specific record that was retrieved.
*/
export type RecordsReadResponse = DwnResponseStatus & {
/** The record retrieved by the read operation. */
record: Record;
};
/**
* Defines a request to write (create) a record to a Decentralized Web Node (DWN).
*
* This request type allows specifying the data for the new or updated record, along with any
* additional message parameters required for the write operation, and an optional flag to indicate
* whether the record should be immediately stored.
*
* @param data -
* @param message - , excluding the signer.
* @param store -
*/
export type RecordsWriteRequest = {
/** The data payload for the record, which can be of any type. */
data: unknown;
/** Optional additional parameters for the record write operation. */
message?: Omit<Partial<DwnMessageParams[DwnInterface.RecordsWrite]>, 'signer'>;
/**
* Optional flag indicating whether the record should be immediately stored. If true, the record
* is persisted in the DWN as part of the write operation. If false, the record is created,
* signed, and returned but not persisted.
*/
store?: boolean;
}
/**
* Encapsulates the response from a record write operation to a Decentralized Web Node (DWN).
*
* This request type combines the general operation status with the details of the record that was
* written, if the operation was successful.
*
* The response includes a status object that contains the HTTP-like status code and detail message
* indicating the success or failure of the write operation. If the operation was successful and a
* record was created or updated, the `record` property will contain an instance of the `Record`
* class representing the written record. This allows the caller to access the written record's
* details and perform additional operations using the provided {@link Record} instance methods.
*/
export type RecordsWriteResponse = DwnResponseStatus & {
/**
* The `Record` instance representing the record that was successfully written to the
* DWN as a result of the write operation.
*/
record?: Record
};
/**
* Interface to interact with DWN Records and Protocols
*/
export class DwnApi {
/**
* Holds the instance of a {@link Web5Agent} that represents the current execution context for
* the `DwnApi`. This agent is used to process DWN requests.
*/
private agent: Web5Agent;
/** The DID of the DWN tenant under which operations are being performed. */
private connectedDid: string;
constructor(options: { agent: Web5Agent, connectedDid: string }) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* API to interact with DWN protocols (e.g., `dwn.protocols.configure()`).
*/
get protocols() {
return {
/**
* Configure method, used to setup a new protocol (or update) with the passed definitions
*/
configure: async (request: ProtocolsConfigureRequest): Promise<ProtocolsConfigureResponse> => {
const agentResponse = await this.agent.processDwnRequest({
author : this.connectedDid,
messageParams : request.message,
messageType : DwnInterface.ProtocolsConfigure,
target : this.connectedDid
});
const { message, messageCid, reply: { status }} = agentResponse;
const response: ProtocolsConfigureResponse = { status };
if (status.code < 300) {
const metadata = { author: this.connectedDid, messageCid };
response.protocol = new Protocol(this.agent, message, metadata);
}
return response;
},
/**
* Query the available protocols
*/
query: async (request: ProtocolsQueryRequest): Promise<ProtocolsQueryResponse> => {
const agentRequest: ProcessDwnRequest<DwnInterface.ProtocolsQuery> = {
author : this.connectedDid,
messageParams : request.message,
messageType : DwnInterface.ProtocolsQuery,
target : request.from || this.connectedDid
};
let agentResponse: DwnResponse<DwnInterface.ProtocolsQuery>;
if (request.from) {
agentResponse = await this.agent.sendDwnRequest(agentRequest);
} else {
agentResponse = await this.agent.processDwnRequest(agentRequest);
}
const reply = agentResponse.reply;
const { entries = [], status } = reply;
const protocols = entries.map((entry) => {
const metadata = { author: this.connectedDid };
return new Protocol(this.agent, entry, metadata);
});
return { protocols, status };
}
};
}
/**
* API to interact with DWN records (e.g., `dwn.records.create()`).
*/
get records() {
return {
/**
* Alias for the `write` method
*/
create: async (request: RecordsCreateRequest): Promise<RecordsCreateResponse> => {
return this.records.write(request);
},
/**
* Write a record based on an existing one (useful for updating an existing record)
*/
createFrom: async (request: RecordsCreateFromRequest): Promise<RecordsWriteResponse> => {
const { author: inheritedAuthor, ...inheritedProperties } = request.record.toJSON();
// If `data` is being updated then `dataCid` and `dataSize` must not be present.
if (request.data !== undefined) {
delete inheritedProperties.dataCid;
delete inheritedProperties.dataSize;
}
// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
// will throw an error if `published` is false but `datePublished` is set.
if (request.message?.published === false && inheritedProperties.datePublished !== undefined) {
delete inheritedProperties.datePublished;
delete inheritedProperties.published;
}
// If the request changes the `author` or message `descriptor` then the deterministic `recordId` will change.
// As a result, we will discard the `recordId` if either of these changes occur.
if (!isEmptyObject(request.message) || (request.author && request.author !== inheritedAuthor)) {
delete inheritedProperties.recordId;
}
return this.records.write({
data : request.data,
message : {
...inheritedProperties,
...request.message,
},
});
},
/**
* Delete a record
*/
delete: async (request: RecordsDeleteRequest): Promise<DwnResponseStatus> => {
const agentRequest: ProcessDwnRequest<DwnInterface.RecordsDelete> = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author : this.connectedDid,
messageParams : request.message,
messageType : DwnInterface.RecordsDelete,
/**
* The `target` is the DID of the DWN tenant under which the delete will be executed.
* If `from` is provided, the delete operation will be executed on a remote DWN.
* Otherwise, the record will be deleted on the local DWN.
*/
target : request.from || this.connectedDid
};
let agentResponse: DwnResponse<DwnInterface.RecordsDelete>;
if (request.from) {
agentResponse = await this.agent.sendDwnRequest(agentRequest);
} else {
agentResponse = await this.agent.processDwnRequest(agentRequest);
}
const { reply: { status } } = agentResponse;
return { status };
},
/**
* Query a single or multiple records based on the given filter
*/
query: async (request: RecordsQueryRequest): Promise<RecordsQueryResponse> => {
const agentRequest: ProcessDwnRequest<DwnInterface.RecordsQuery> = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author : this.connectedDid,
messageParams : request.message,
messageType : DwnInterface.RecordsQuery,
/**
* The `target` is the DID of the DWN tenant under which the query will be executed.
* If `from` is provided, the query operation will be executed on a remote DWN.
* Otherwise, the local DWN will be queried.
*/
target : request.from || this.connectedDid
};
let agentResponse: DwnResponse<DwnInterface.RecordsQuery>;
if (request.from) {
agentResponse = await this.agent.sendDwnRequest(agentRequest);
} else {
agentResponse = await this.agent.processDwnRequest(agentRequest);
}
const reply = agentResponse.reply;
const { entries, status, cursor } = reply;
const records = entries.map((entry) => {
const recordOptions = {
/**
* Extract the `author` DID from the record entry since records may be signed by the
* tenant owner or any other entity.
*/
author : getRecordAuthor(entry),
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN even if the record was returned by a query of a remote DWN.
*/
connectedDid : this.connectedDid,
/**
* If the record was returned by a query of a remote DWN, set the `remoteOrigin` to
* the DID of the DWN that returned the record. The `remoteOrigin` property will be used
* to determine which DWN to send subsequent read requests to in the event the data
* payload exceeds the threshold for being returned with queries.
*/
remoteOrigin : request.from,
...entry as DwnMessage[DwnInterface.RecordsWrite]
};
const record = new Record(this.agent, recordOptions);
return record;
});
return { records, status, cursor };
},
/**
* Read a single record based on the given filter
*/
read: async (request: RecordsReadRequest): Promise<RecordsReadResponse> => {
const agentRequest: ProcessDwnRequest<DwnInterface.RecordsRead> = {
/**
* The `author` is the DID that will sign the message and must be the DID the Web5 app is
* connected with and is authorized to access the signing private key of.
*/
author : this.connectedDid,
messageParams : request.message,
messageType : DwnInterface.RecordsRead,
/**
* The `target` is the DID of the DWN tenant under which the read will be executed.
* If `from` is provided, the read operation will be executed on a remote DWN.
* Otherwise, the read will occur on the local DWN.
*/
target : request.from || this.connectedDid
};
let agentResponse: DwnResponse<DwnInterface.RecordsRead>;
if (request.from) {
agentResponse = await this.agent.sendDwnRequest(agentRequest);
} else {
agentResponse = await this.agent.processDwnRequest(agentRequest);
}
const { reply: { record: responseRecord, status } } = agentResponse;
let record: Record;
if (200 <= status.code && status.code <= 299) {
const recordOptions = {
/**
* Extract the `author` DID from the record since records may be signed by the
* tenant owner or any other entity.
*/
author : getRecordAuthor(responseRecord),
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN even if the record was read from a remote DWN.
*/
connectedDid : this.connectedDid,
/**
* If the record was returned by reading from a remote DWN, set the `remoteOrigin` to
* the DID of the DWN that returned the record. The `remoteOrigin` property will be used
* to determine which DWN to send subsequent read requests to in the event the data
* payload must be read again (e.g., if the data stream is consumed).
*/
remoteOrigin : request.from,
...responseRecord,
};
record = new Record(this.agent, recordOptions);
}
return { record, status };
},
/**
* Writes a record to the DWN
*
* As a convenience, the Record instance returned will cache a copy of the data. This is done
* to maintain consistency with other DWN methods, like RecordsQuery, that include relatively
* small data payloads when returning RecordsWrite message properties. Regardless of data
* size, methods such as `record.data.stream()` will return the data when called even if it
* requires fetching from the DWN datastore.
*/
write: async (request: RecordsWriteRequest): Promise<RecordsWriteResponse> => {
const { dataBlob, dataFormat } = dataToBlob(request.data, request.message?.dataFormat);
const agentResponse = await this.agent.processDwnRequest({
author : this.connectedDid,
dataStream : dataBlob,
messageParams : { ...request.message, dataFormat },
messageType : DwnInterface.RecordsWrite,
store : request.store,
target : this.connectedDid
});
const { message: responseMessage, reply: { status } } = agentResponse;
let record: Record;
if (200 <= status.code && status.code <= 299) {
const recordOptions = {
/**
* Assume the author is the connected DID since the record was just written to the
* local DWN.
*/
author : this.connectedDid,
/**
* Set the `connectedDid` to currently connected DID so that subsequent calls to
* {@link Record} instance methods, such as `record.update()` are executed on the
* local DWN.
*/
connectedDid : this.connectedDid,
encodedData : dataBlob,
...responseMessage,
};
record = new Record(this.agent, recordOptions);
}
return { record, status };
},
};
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* Making developing with Web5 components at least 5 times easier to work with.
*
* Web5 consists of the following components:
* - Decentralized Identifiers
* - Verifiable Credentials
* - DWeb Node personal datastores
*
* The SDK sets out to gather the most oft used functionality from all three of
* these pillar technologies to provide a simple library that is as close to
* effortless as possible.
*
* The SDK is currently still under active development, but having entered the
* Tech Preview phase there is now a drive to avoid unnecessary changes unless
* backwards compatibility is provided. Additional functionality will be added
* in the lead up to 1.0 final, and modifications will be made to address
* issues and community feedback.
*
* [Link to GitHub Repo](https://github.com/TBD54566975/web5-js)
*
* @packageDocumentation
*/
export * from './did-api.js';
export * from './dwn-api.js';
export * from './protocol.js';
export * from './record.js';
export * from './vc-api.js';
export * from './web5.js';
export * from './tech-preview.js';
export * from './web-features.js';
import * as utils from './utils.js';
export { utils };
+87
View File
@@ -0,0 +1,87 @@
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
/// <reference types="@tbd54566975/dwn-sdk-js" />
import type { DwnMessage, DwnResponseStatus, Web5Agent } from '@web5/agent';
import { DwnInterface } from '@web5/agent';
/**
* Represents metadata associated with a protocol, including the author and an optional message CID.
*/
export type ProtocolMetadata = {
/** The author of the protocol. */
author: string;
/**
* The Content Identifier (CID) of a ProtocolsConfigure message.
*
* This is an optional field, and is used by {@link Protocol.send}.
*/
messageCid?: string;
};
/**
* Encapsulates a DWN Protocol with its associated metadata and configuration.
*
* This class primarly exists to provide developers with a convenient way to configure/install
* protocols on remote DWNs.
*/
export class Protocol {
/** The {@link Web5Agent} instance that handles DWNs requests. */
private _agent: Web5Agent;
/** The ProtocolsConfigureMessage containing the detailed configuration for the protocol. */
private _metadata: ProtocolMetadata;
/** Metadata associated with the protocol, including the author and optional message CID. */
private _protocolsConfigureMessage: DwnMessage[DwnInterface.ProtocolsConfigure];
/**
* Constructs a new instance of the Protocol class.
*
* @param agent - The Web5Agent instance used for network interactions.
* @param protocolsConfigureMessage - The configuration message containing the protocol details.
* @param metadata - Metadata associated with the protocol, including the author and optional message CID.
*/
constructor(agent: Web5Agent, protocolsConfigureMessage: DwnMessage[DwnInterface.ProtocolsConfigure], metadata: ProtocolMetadata) {
this._agent = agent;
this._metadata = metadata;
this._protocolsConfigureMessage = protocolsConfigureMessage;
}
/**
* Retrieves the protocol definition from the protocol's configuration message.
* @returns The protocol definition.
*/
get definition() {
return this._protocolsConfigureMessage.descriptor.definition;
}
/**
* Serializes the protocol's configuration message to JSON.
* @returns The serialized JSON object of the protocol's configuration message.
*/
toJSON() {
return this._protocolsConfigureMessage;
}
/**
* Sends the protocol configuration to a remote DWN identified by the target DID.
*
* @param target - The DID of the target DWN to which the protocol configuration will be installed.
* @returns A promise that resolves to an object containing the status of the send operation.
*/
async send(target: string): Promise<DwnResponseStatus> {
const { reply } = await this._agent.sendDwnRequest({
author : this._metadata.author,
messageCid : this._metadata.messageCid,
messageType : DwnInterface.ProtocolsConfigure,
target : target,
});
return { status: reply.status };
}
}
+795
View File
@@ -0,0 +1,795 @@
/**
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
* https://github.com/TBD54566975/web5-js/pull/507
*/
/// <reference types="@tbd54566975/dwn-sdk-js" />
import type { Readable } from '@web5/common';
import {
Web5Agent,
DwnMessage,
DwnMessageParams,
DwnResponseStatus,
ProcessDwnRequest,
DwnMessageDescriptor,
getPaginationCursor,
DwnDateSort,
DwnPaginationCursor
} from '@web5/agent';
import { DwnInterface } from '@web5/agent';
import { Convert, isEmptyObject, NodeStream, removeUndefinedProperties, Stream } from '@web5/common';
import { dataToBlob, SendCache } from './utils.js';
/**
* Represents the structured data model of a record, encapsulating the essential fields that define
* the record's metadata and payload within a Decentralized Web Node (DWN).
*
* @beta
*/
export type RecordModel = DwnMessageDescriptor[DwnInterface.RecordsWrite]
& Omit<DwnMessage[DwnInterface.RecordsWrite], 'descriptor' | 'recordId'>
& {
/** The DID that signed the record. */
author: string;
/** The protocol role under which this record is written. */
protocolRole?: RecordOptions['protocolRole'];
/** The unique identifier of the record. */
recordId?: string;
}
/**
* Options for configuring a {@link Record} instance, extending the base `RecordsWriteMessage` with
* additional properties.
*
* This type combines the standard fields required for writing DWN records with additional metadata
* and configuration options used specifically in the {@link Record} class.
*
* @beta
*/
export type RecordOptions = DwnMessage[DwnInterface.RecordsWrite] & {
/** The DID that signed the record. */
author: string;
/** The DID of the DWN tenant under which record operations are being performed. */
connectedDid: string;
/** The data of the record, either as a Base64 URL encoded string or a Blob. */
encodedData?: string | Blob;
/**
* A stream of data, conforming to the `Readable` or `ReadableStream` interface, providing a
* mechanism to read the record's data sequentially. This is particularly useful for handling
* large datasets that should not be loaded entirely in memory, allowing for efficient, chunked
* processing of the record's data.
*/
data?: Readable | ReadableStream;
/** The initial `RecordsWriteMessage` that represents the initial state/version of the record. */
initialWrite?: DwnMessage[DwnInterface.RecordsWrite];
/** The protocol role under which this record is written. */
protocolRole?: string;
/** The remote tenant DID if the record was queried or read from a remote DWN. */
remoteOrigin?: string;
};
/**
* Parameters for updating a DWN record.
*
* This type specifies the set of properties that can be updated on an existing record. It is used
* to convey the new state or changes to be applied to the record.
*
* @beta
*/
export type RecordUpdateParams = {
/**
* The new data for the record, which can be of any type. This data will replace the existing
* data of the record. It's essential to ensure that this data is compatible with the record's
* schema or data format expectations.
*/
data?: unknown;
/**
* The Content Identifier (CID) of the data. Updating this value changes the reference to the data
* associated with the record.
*/
dataCid?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['dataCid'];
/** The size of the data in bytes. */
dataSize?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['dataSize'];
/** The timestamp indicating when the record was last modified. */
dateModified?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['messageTimestamp'];
/** The timestamp indicating when the record was published. */
datePublished?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['datePublished'];
/** The protocol role under which this record is written. */
protocolRole?: RecordOptions['protocolRole'];
/** The published status of the record. */
published?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['published'];
/** The tags associated with the updated record */
tags?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['tags'];
}
/**
* Record wrapper class with convenience methods to send and update,
* aside from manipulating and reading the record data.
*
* Note: The `messageTimestamp` of the most recent RecordsWrite message is
* logically equivalent to the date/time at which a Record was most
* recently modified. Since this Record class implementation is
* intended to simplify the developer experience of working with
* logical records (and not individual DWN messages) the
* `messageTimestamp` is mapped to `dateModified`.
*
* @beta
*/
/**
* The `Record` class encapsulates a single record's data and metadata, providing a more
* developer-friendly interface for working with Decentralized Web Node (DWN) records.
*
* Methods are provided to read, update, and manage the record's lifecycle, including writing to
* remote DWNs.
*
* @beta
*/
export class Record implements RecordModel {
/**
* Cache to minimize the amount of redundant two-phase commits we do in store() and send()
* Retains awareness of the last 100 records stored/sent for up to 100 target DIDs each.
*/
private static _sendCache = SendCache;
// Record instance metadata.
/** The {@link Web5Agent} instance that handles DWNs requests. */
private _agent: Web5Agent;
/** The DID of the DWN tenant under which operations are being performed. */
private _connectedDid: string;
/** Encoded data of the record, if available. */
private _encodedData?: Blob;
/** Stream of the record's data. */
private _readableStream?: Readable;
/** The origin DID if the record was fetched from a remote DWN. */
private _remoteOrigin?: string;
// Private variables for DWN `RecordsWrite` message properties.
/** The DID of the entity that authored the record. */
private _author: string;
/** Attestation JWS signature. */
private _attestation?: DwnMessage[DwnInterface.RecordsWrite]['attestation'];
/** Authorization signature(s). */
private _authorization?: DwnMessage[DwnInterface.RecordsWrite]['authorization'];
/** Context ID associated with the record. */
private _contextId?: string;
/** Descriptor detailing the record's schema, format, and other metadata. */
private _descriptor: DwnMessageDescriptor[DwnInterface.RecordsWrite];
/** Encryption details for the record, if the data is encrypted. */
private _encryption?: DwnMessage[DwnInterface.RecordsWrite]['encryption'];
/** Initial state of the record before any updates. */
private _initialWrite: RecordOptions['initialWrite'];
/** Flag indicating if the initial write has been stored, to prevent duplicates. */
private _initialWriteStored: boolean;
/** Flag indicating if the initial write has been signed by the owner. */
private _initialWriteSigned: boolean;
/** Unique identifier of the record. */
private _recordId: string;
/** Role under which the record is written. */
private _protocolRole: RecordOptions['protocolRole'];
// Getters for immutable DWN Record properties.
/** Record's signatures attestation */
get attestation(): DwnMessage[DwnInterface.RecordsWrite]['attestation'] { return this._attestation; }
/** Record's signatures attestation */
get authorization(): DwnMessage[DwnInterface.RecordsWrite]['authorization'] { return this._authorization; }
/** DID that signed the record. */
get author(): string { return this._author; }
/** Record's context ID */
get contextId() { return this._contextId; }
/** Record's data format */
get dataFormat() { return this._descriptor.dataFormat; }
/** Record's creation date */
get dateCreated() { return this._descriptor.dateCreated; }
/** Record's encryption */
get encryption(): DwnMessage[DwnInterface.RecordsWrite]['encryption'] { return this._encryption; }
/** Record's initial write if the record has been updated */
get initialWrite(): RecordOptions['initialWrite'] { return this._initialWrite; }
/** Record's ID */
get id() { return this._recordId; }
/** Interface is always `Records` */
get interface() { return this._descriptor.interface; }
/** Method is always `Write` */
get method() { return this._descriptor.method; }
/** Record's parent ID */
get parentId() { return this._descriptor.parentId; }
/** Record's protocol */
get protocol() { return this._descriptor.protocol; }
/** Record's protocol path */
get protocolPath() { return this._descriptor.protocolPath; }
/** Role under which the author is writing the record */
get protocolRole() { return this._protocolRole; }
/** Record's recipient */
get recipient() { return this._descriptor.recipient; }
/** Record's schema */
get schema() { return this._descriptor.schema; }
// Getters for mutable DWN Record properties.
/** Record's CID */
get dataCid() { return this._descriptor.dataCid; }
/** Record's data size */
get dataSize() { return this._descriptor.dataSize; }
/** Record's modified date */
get dateModified() { return this._descriptor.messageTimestamp; }
/** Record's published date */
get datePublished() { return this._descriptor.datePublished; }
/** Record's published status */
get messageTimestamp() { return this._descriptor.messageTimestamp; }
/** Record's published status (true/false) */
get published() { return this._descriptor.published; }
/** Tags of the record */
get tags() { return this._descriptor.tags; }
/**
* Returns a copy of the raw `RecordsWriteMessage` that was used to create the current `Record` instance.
*/
private get rawMessage(): DwnMessage[DwnInterface.RecordsWrite] {
const message = JSON.parse(JSON.stringify({
contextId : this._contextId,
recordId : this._recordId,
descriptor : this._descriptor,
attestation : this._attestation,
authorization : this._authorization,
encryption : this._encryption,
}));
removeUndefinedProperties(message);
return message;
}
constructor(agent: Web5Agent, options: RecordOptions) {
this._agent = agent;
// Store the author DID that originally signed the message as a convenience for developers, so
// that they don't have to decode the signer's DID from the JWS.
this._author = options.author;
// Store the currently `connectedDid` so that subsequent message signing is done with the
// connected DID's keys and DWN requests target the connected DID's DWN.
this._connectedDid = options.connectedDid;
// If the record was queried or read from a remote DWN, the `remoteOrigin` DID will be
// defined. This value is used to send subsequent read requests to the same remote DWN in the
// event the record's data payload was too large to be returned in query results. or must be
// read again (e.g., if the data stream is consumed).
this._remoteOrigin = options.remoteOrigin;
// RecordsWriteMessage properties.
this._attestation = options.attestation;
this._authorization = options.authorization;
this._contextId = options.contextId;
this._descriptor = options.descriptor;
this._encryption = options.encryption;
this._initialWrite = options.initialWrite;
this._recordId = options.recordId;
this._protocolRole = options.protocolRole;
if (options.encodedData) {
// If `encodedData` is set, then it is expected that:
// type is Blob if the Record object was instantiated by dwn.records.create()/write().
// type is Base64 URL encoded string if the Record object was instantiated by dwn.records.query().
// If it is a string, we need to Base64 URL decode to bytes and instantiate a Blob.
this._encodedData = (typeof options.encodedData === 'string') ?
new Blob([Convert.base64Url(options.encodedData).toUint8Array()], { type: this.dataFormat }) :
options.encodedData;
}
if (options.data) {
// If the record was created from a RecordsRead reply then it will have a `data` property.
// If the `data` property is a web ReadableStream, convert it to a Node.js Readable.
this._readableStream = Stream.isReadableStream(options.data) ?
NodeStream.fromWebReadable({ readableStream: options.data }) :
options.data;
}
}
/**
* Returns the data of the current record.
* If the record data is not available, it attempts to fetch the data from the DWN.
* @returns a data stream with convenience methods such as `blob()`, `json()`, `text()`, and `stream()`, similar to the fetch API response
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
get data() {
const self = this; // Capture the context of the `Record` instance.
const dataObj = {
/**
* Returns the data of the current record as a `Blob`.
*
* @returns A promise that resolves to a Blob containing the record's data.
* @throws If the record data is not available or cannot be converted to a `Blob`.
*
* @beta
*/
async blob(): Promise<Blob> {
return new Blob([await NodeStream.consumeToBytes({ readable: await this.stream() })], { type: self.dataFormat });
},
/**
* Returns the data of the current record as a `Uint8Array`.
*
* @returns A Promise that resolves to a `Uint8Array` containing the record's data bytes.
* @throws If the record data is not available or cannot be converted to a byte array.
*
* @beta
*/
async bytes(): Promise<Uint8Array> {
return await NodeStream.consumeToBytes({ readable: await this.stream() });
},
/**
* Parses the data of the current record as JSON and returns it as a JavaScript object.
*
* @returns A Promise that resolves to a JavaScript object parsed from the record's JSON data.
* @throws If the record data is not available, not in JSON format, or cannot be parsed.
*
* @beta
*/
async json(): Promise<any> {
return await NodeStream.consumeToJson({ readable: await this.stream() });
},
/**
* Returns the data of the current record as a `string`.
*
* @returns A promise that resolves to a `string` containing the record's text data.
* @throws If the record data is not available or cannot be converted to text.
*
* @beta
*/
async text(): Promise<string> {
return await NodeStream.consumeToText({ readable: await this.stream() });
},
/**
* Provides a `Readable` stream containing the record's data.
*
* @returns A promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If the record data is not available in-memory and cannot be fetched.
*
* @beta
*/
async stream(): Promise<Readable> {
if (self._encodedData) {
/** If `encodedData` is set, it indicates that the Record was instantiated by
* `dwn.records.create()`/`dwn.records.write()` or the record's data payload was small
* enough to be returned in `dwn.records.query()` results. In either case, the data is
* already available in-memory and can be returned as a Node.js `Readable` stream. */
self._readableStream = NodeStream.fromWebReadable({ readableStream: self._encodedData.stream() });
} else if (!NodeStream.isReadable({ readable: self._readableStream })) {
/** If the data stream for this `Record` instance has already been partially or fully
* consumed, then the data must be fetched again from either: */
self._readableStream = self._remoteOrigin ?
// A. ...a remote DWN if the record was originally queried from a remote DWN.
await self.readRecordData({ target: self._remoteOrigin, isRemote: true }) :
// B. ...a local DWN if the record was originally queried from the local DWN.
await self.readRecordData({ target: self._connectedDid, isRemote: false });
}
if (!self._readableStream) {
throw new Error('Record data is not available.');
}
return self._readableStream;
},
/**
* Attaches callbacks for the resolution and/or rejection of the `Promise` returned by
* `stream()`.
*
* This method is a proxy to the `then` method of the `Promise` returned by `stream()`,
* allowing for a seamless integration with promise-based workflows.
* @param onFulfilled - A function to asynchronously execute when the `stream()` promise
* becomes fulfilled.
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` for the completion of which ever callback is executed.
*/
then(onFulfilled?: (value: Readable) => Readable | PromiseLike<Readable>, onRejected?: (reason: any) => PromiseLike<never>) {
return this.stream().then(onFulfilled, onRejected);
},
/**
* Attaches a rejection handler callback to the `Promise` returned by the `stream()` method.
* This method is a shorthand for `.then(undefined, onRejected)`, specifically designed for handling
* rejection cases in the promise chain initiated by accessing the record's data. It ensures that
* errors during data retrieval or processing can be caught and handled appropriately.
*
* @param onRejected - A function to asynchronously execute when the `stream()` promise
* becomes rejected.
* @returns A `Promise` that resolves to the value of the callback if it is called, or to its
* original fulfillment value if the promise is instead fulfilled.
*/
catch(onRejected?: (reason: any) => PromiseLike<never>) {
return this.stream().catch(onRejected);
}
};
return dataObj;
}
/**
* Stores the current record state as well as any initial write to the owner's DWN.
*
* @param importRecord - if true, the record will signed by the owner before storing it to the owner's DWN. Defaults to false.
* @returns the status of the store request
*
* @beta
*/
async store(importRecord: boolean = false): Promise<DwnResponseStatus> {
// if we are importing the record we sign it as the owner
return this.processRecord({ signAsOwner: importRecord, store: true });
}
/**
* Signs the current record state as well as any initial write and optionally stores it to the owner's DWN.
* This is useful when importing a record that was signed by someone else into your own DWN.
*
* @param store - if true, the record will be stored to the owner's DWN after signing. Defaults to true.
* @returns the status of the import request
*
* @beta
*/
async import(store: boolean = true): Promise<DwnResponseStatus> {
return this.processRecord({ store, signAsOwner: true });
}
/**
* Send the current record to a remote DWN by specifying their DID
* If no DID is specified, the target is assumed to be the owner (connectedDID).
* If an initial write is present and the Record class send cache has no awareness of it, the initial write is sent first
* (vs waiting for the regular DWN sync)
*
* @param target - the optional DID to send the record to, if none is set it is sent to the connectedDid
* @returns the status of the send record request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
async send(target?: string): Promise<DwnResponseStatus> {
const initialWrite = this._initialWrite;
target ??= this._connectedDid;
// Is there an initial write? Do we know if we've already sent it to this target?
if (initialWrite && !Record._sendCache.check(this._recordId, target)){
// We do have an initial write, so prepare it for sending to the target.
const rawMessage = {
...initialWrite
};
removeUndefinedProperties(rawMessage);
// Send the initial write to the target.
await this._agent.sendDwnRequest({
messageType : DwnInterface.RecordsWrite,
author : this._connectedDid,
target : target,
rawMessage
});
// Set the cache to maintain awareness that we don't need to send the initial write next time.
Record._sendCache.set(this._recordId, target);
}
// Send the current/latest state to the target.
const { reply } = await this._agent.sendDwnRequest({
messageType : DwnInterface.RecordsWrite,
author : this._connectedDid,
dataStream : await this.data.blob(),
target : target,
rawMessage : { ...this.rawMessage }
});
return reply;
}
/**
* Returns a JSON representation of the Record instance.
* It's called by `JSON.stringify(...)` automatically.
*/
toJSON(): RecordModel {
return {
attestation : this.attestation,
author : this.author,
authorization : this.authorization,
contextId : this.contextId,
dataCid : this.dataCid,
dataFormat : this.dataFormat,
dataSize : this.dataSize,
dateCreated : this.dateCreated,
messageTimestamp : this.dateModified,
datePublished : this.datePublished,
encryption : this.encryption,
interface : this.interface,
method : this.method,
parentId : this.parentId,
protocol : this.protocol,
protocolPath : this.protocolPath,
protocolRole : this.protocolRole,
published : this.published,
recipient : this.recipient,
recordId : this.id,
schema : this.schema,
tags : this.tags,
};
}
/**
* Convenience method to return the string representation of the Record instance.
* Called automatically in string concatenation, String() type conversion, and template literals.
*/
toString() {
let str = `Record: {\n`;
str += ` ID: ${this.id}\n`;
str += this.contextId ? ` Context ID: ${this.contextId}\n` : '';
str += this.protocol ? ` Protocol: ${this.protocol}\n` : '';
str += this.schema ? ` Schema: ${this.schema}\n` : '';
str += ` Data CID: ${this.dataCid}\n`;
str += ` Data Format: ${this.dataFormat}\n`;
str += ` Data Size: ${this.dataSize}\n`;
str += ` Created: ${this.dateCreated}\n`;
str += ` Modified: ${this.dateModified}\n`;
str += `}`;
return str;
}
/**
* Returns a pagination cursor for the current record given a sort order.
*
* @param sort the sort order to use for the pagination cursor.
* @returns A promise that resolves to a pagination cursor for the current record.
*/
async paginationCursor(sort: DwnDateSort): Promise<DwnPaginationCursor> {
return getPaginationCursor(this.rawMessage, sort);
}
/**
* Update the current record on the DWN.
* @param params - Parameters to update the record.
* @returns the status of the update request
* @throws `Error` if the record has already been deleted.
*
* @beta
*/
async update({ dateModified, data, ...params }: RecordUpdateParams): Promise<DwnResponseStatus> {
// if there is a parentId, we remove it from the descriptor and set a parentContextId
const { parentId, ...descriptor } = this._descriptor;
const parentContextId = parentId ? this._contextId.split('/').slice(0, -1).join('/') : undefined;
// Begin assembling the update message.
let updateMessage: DwnMessageParams[DwnInterface.RecordsWrite] = {
...descriptor,
...params,
parentContextId,
messageTimestamp : dateModified, // Map Record class `dateModified` property to DWN SDK `messageTimestamp`
recordId : this._recordId
};
// NOTE: The original Record's tags are copied to the update message, so that the tags are not lost.
// However if a user passes new tags in the `RecordUpdateParams` object, they will overwrite the original tags.
// If the updated tag object is empty or set to null, we remove the tags property to avoid schema validation errors in the DWN SDK.
if (isEmptyObject(updateMessage.tags) || updateMessage.tags === null) {
delete updateMessage.tags;
}
let dataBlob: Blob;
if (data !== undefined) {
// If `data` is being updated then `dataCid` and `dataSize` must be undefined and the `data`
// value must be converted to a Blob and later passed as a top-level property to
// `agent.processDwnRequest()`.
delete updateMessage.dataCid;
delete updateMessage.dataSize;
({ dataBlob } = dataToBlob(data, updateMessage.dataFormat));
}
// Throw an error if an attempt is made to modify immutable properties.
// Note: `data` and `dateModified` have already been handled.
const mutableDescriptorProperties = new Set(['data', 'dataCid', 'dataSize', 'datePublished', 'messageTimestamp', 'published', 'tags']);
Record.verifyPermittedMutation(Object.keys(params), mutableDescriptorProperties);
// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
// will throw an error if `published` is false but `datePublished` is set.
if (params.published === false && updateMessage.datePublished !== undefined) {
delete updateMessage.datePublished;
}
const agentResponse = await this._agent.processDwnRequest({
author : this._connectedDid,
dataStream : dataBlob,
messageParams : { ...updateMessage },
messageType : DwnInterface.RecordsWrite,
target : this._connectedDid,
});
const { message, reply: { status } } = agentResponse;
const responseMessage = message;
if (200 <= status.code && status.code <= 299) {
// copy the original raw message to the initial write before we update the values.
if (!this._initialWrite) {
this._initialWrite = { ...this.rawMessage };
}
// Only update the local Record instance mutable properties if the record was successfully (over)written.
this._authorization = responseMessage.authorization;
this._protocolRole = params.protocolRole;
mutableDescriptorProperties.forEach(property => {
this._descriptor[property] = responseMessage.descriptor[property];
});
// Cache data.
if (data !== undefined) {
this._encodedData = dataBlob;
}
}
return { status };
}
/**
* Handles the various conditions around there being an initial write, whether to store initial/current state,
* and whether to add an owner signature to the initial write to enable storage when protocol rules require it.
*/
private async processRecord({ store, signAsOwner }:{ store: boolean, signAsOwner: boolean }): Promise<DwnResponseStatus> {
// if there is an initial write and we haven't already processed it, we first process it and marked it as such.
if (this._initialWrite && ((signAsOwner && !this._initialWriteSigned) || (store && !this._initialWriteStored))) {
const initialWriteRequest: ProcessDwnRequest<DwnInterface.RecordsWrite> = {
messageType : DwnInterface.RecordsWrite,
rawMessage : this.initialWrite,
author : this._connectedDid,
target : this._connectedDid,
signAsOwner,
store,
};
// Process the prepared initial write, with the options set for storing and/or signing as the owner.
const agentResponse = await this._agent.processDwnRequest(initialWriteRequest);
const { message, reply: { status } } = agentResponse;
const responseMessage = message;
// If we are signing as owner, make sure to update the initial write's authorization, because now it will have the owner's signature on it
// set the stored or signed status to true so we don't process it again.
if (200 <= status.code && status.code <= 299) {
if (store) this._initialWriteStored = true;
if (signAsOwner) {
this._initialWriteSigned = true;
this.initialWrite.authorization = responseMessage.authorization;
}
}
}
// Now that we've processed a potential initial write, we can process the current record state.
const requestOptions: ProcessDwnRequest<DwnInterface.RecordsWrite> = {
messageType : DwnInterface.RecordsWrite,
rawMessage : this.rawMessage,
author : this._connectedDid,
target : this._connectedDid,
dataStream : await this.data.blob(),
signAsOwner,
store,
};
const agentResponse = await this._agent.processDwnRequest(requestOptions);
const { message, reply: { status } } = agentResponse;
const responseMessage = message;
if (200 <= status.code && status.code <= 299) {
// If we are signing as the owner, make sure to update the current record state's authorization, because now it will have the owner's signature on it.
if (signAsOwner) this._authorization = responseMessage.authorization;
}
return { status };
}
/**
* Fetches the record's data from the specified DWN.
*
* This private method is called when the record data is not available in-memory
* and needs to be fetched from either a local or a remote DWN.
* It makes a read request to the specified DWN and processes the response to provide
* a Node.js `Readable` stream of the record's data.
*
* @param params - Parameters for fetching the record's data.
* @param params.target - The DID of the DWN to fetch the data from.
* @param params.isRemote - Indicates whether the target DWN is a remote node.
* @returns A Promise that resolves to a Node.js `Readable` stream of the record's data.
* @throws If there is an error while fetching or processing the data from the DWN.
*
* @beta
*/
private async readRecordData({ target, isRemote }: { target: string, isRemote: boolean }) {
const readRequest: ProcessDwnRequest<DwnInterface.RecordsRead> = {
author : this._connectedDid,
messageParams : { filter: { recordId: this.id } },
messageType : DwnInterface.RecordsRead,
target,
};
const agentResponsePromise = isRemote ?
this._agent.sendDwnRequest(readRequest) :
this._agent.processDwnRequest(readRequest);
try {
const { reply: { record }} = await agentResponsePromise;
const dataStream: ReadableStream | Readable = record.data;
// If the data stream is a web ReadableStream, convert it to a Node.js Readable.
const nodeReadable = Stream.isReadableStream(dataStream) ?
NodeStream.fromWebReadable({ readableStream: dataStream }) :
dataStream;
return nodeReadable;
} catch (error) {
throw new Error(`Error encountered while attempting to read data: ${error.message}`);
}
}
/**
* Verifies if the properties to be mutated are mutable.
*
* This private method is used to ensure that only mutable properties of the `Record` instance
* are being changed. It checks whether the properties specified for mutation are among the
* set of properties that are allowed to be modified. If any of the properties to be mutated
* are not in the set of mutable properties, the method throws an error.
*
* @param propertiesToMutate - An iterable of property names that are intended to be mutated.
* @param mutableDescriptorProperties - A set of property names that are allowed to be mutated.
*
* @throws If any of the properties in `propertiesToMutate` are not in `mutableDescriptorProperties`.
*
* @beta
*/
private static verifyPermittedMutation(propertiesToMutate: Iterable<string>, mutableDescriptorProperties: Set<string>) {
for (const property of propertiesToMutate) {
if (!mutableDescriptorProperties.has(property)) {
throw new Error(`${property} is an immutable property. Its value cannot be changed.`);
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
import { UniversalResolver, DidDht, DidWeb } from '@web5/dids';
const workerSelf = self as any;
const DidResolver = new UniversalResolver({ didResolvers: [DidDht, DidWeb] });
const didUrlRegex = /^https?:\/\/dweb\/(([^/]+)\/.*)?$/;
const httpToHttpsRegex = /^http:/;
const trailingSlashRegex = /\/$/;
workerSelf.addEventListener('fetch', event => {
const match = event.request.url.match(didUrlRegex);
if (match) {
event.respondWith((async () => {
const normalizedUrl = event.request.url.replace(httpToHttpsRegex, 'https:').replace(trailingSlashRegex, '');
const cachedResponse = await caches.open('drl').then(cache => cache.match(normalizedUrl));
return cachedResponse || handleEvent(event, match[2], match[1]);
})());
}
});
async function handleEvent(event, did, route){
try {
const result = await DidResolver.resolve(did);
return await fetchResource(event, result.didDocument, route);
}
catch(error){
if (error instanceof Response) {
return error;
}
console.log(`Error in DID URL fetch: ${error}`);
return new Response('DID URL fetch error', { status: 500 });
}
}
async function fetchResource(event, ddo, route) {
let endpoints = ddo?.service?.find(service => service.type === 'DecentralizedWebNode')?.serviceEndpoint;
endpoints = (Array.isArray(endpoints) ? endpoints : [endpoints]).filter(url => url.startsWith('http'));
if (!endpoints?.length) {
throw new Response('DWeb Node resolution failed: no valid endpoints found.', { status: 530 });
}
for (const endpoint of endpoints) {
try {
const response = await fetch(`${endpoint.replace(trailingSlashRegex, '')}/${route}`, { headers: event.request.headers });
if (response.ok) {
return response;
}
console.log(`DWN endpoint error: ${response.status}`);
return new Response('DWeb Node request failed', { status: response.status });
}
catch (error) {
console.log(`DWN endpoint error: ${error}`);
return new Response('DWeb Node request failed: ' + error, { status: 500 });
}
}
}
+57
View File
@@ -0,0 +1,57 @@
import { utils as didUtils } from '@web5/dids';
/**
* Dynamically selects up to 2 DWN endpoints that are provided
* by default during the Tech Preview period.
*
* @beta
*/
export async function getTechPreviewDwnEndpoints(): Promise<string[]> {
let response: Response;
try {
response = await fetch('https://dwn.tbddev.org/.well-known/did.json');
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
}
} catch(error: any) {
console.warn('failed to get tech preview dwn endpoints:', error.message);
return [];
}
const didDocument = await response.json();
const [ dwnService ] = didUtils.getServices({ didDocument, id: '#dwn', type: 'DecentralizedWebNode' });
// allocate up to 2 nodes for a user.
const techPreviewEndpoints = new Set<string>();
if ('serviceEndpoint' in dwnService
&& !Array.isArray(dwnService.serviceEndpoint)
&& typeof dwnService.serviceEndpoint !== 'string'
&& Array.isArray(dwnService.serviceEndpoint.nodes)) {
const dwnUrls = dwnService.serviceEndpoint.nodes;
const numNodesToAllocate = Math.min(dwnUrls.length, 2);
for (let attempts = 0; attempts < dwnUrls.length && techPreviewEndpoints.size < numNodesToAllocate; attempts += 1) {
const nodeIdx = getRandomInt(0, dwnUrls.length);
const dwnUrl = dwnUrls[nodeIdx];
try {
const healthCheck = await fetch(`${dwnUrl}/health`);
if (healthCheck.ok) {
techPreviewEndpoints.add(dwnUrl);
}
} catch(error: unknown) {
// Ignore healthcheck failures and try the next node.
}
}
}
return Array.from(techPreviewEndpoints);
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
+128
View File
@@ -0,0 +1,128 @@
import { Convert, universalTypeOf } from '@web5/common';
/**
* Converts various data types to a `Blob` object, automatically detecting the data type or using
* the specified `dataFormat` to set the Blob's MIME type.
*
* This function supports plain text, JSON objects, binary data (Uint8Array, ArrayBuffer), and Blob
* inputs and will attempt to automatically detect the type of the data if `dataFormat` is not
* explicitly provided.
*
* @beta
*
* @example
* ```ts
* // Convert a JSON object to a Blob
* const { dataBlob, dataFormat } = dataToBlob({ key: 'value' }, 'application/json');
*
* // Convert a plain text string to a Blob without specifying dataFormat
* const { dataBlob: textBlob } = dataToBlob('Hello, world!');
*
* // Convert binary data to a Blob
* const binaryData = new Uint8Array([0, 1, 2, 3]);
* const { dataBlob: binaryBlob } = dataToBlob(binaryData);
* ```
*
* @param data - The data to be converted into a `Blob`. This can be a string, an object, binary
* data (Uint8Array or ArrayBuffer), or a Blob.
* @param dataFormat - An optional MIME type string that specifies the format of the data. Common
* types include 'text/plain' for string data, 'application/json' for JSON
* objects, and 'application/octet-stream' for binary data. If not provided, the
* function will attempt to detect the format based on the data type or default
* to 'application/octet-stream'.
* @returns An object containing the `dataBlob`, a Blob representation of the input data, and
* `dataFormat`, the MIME type of the data as determined by the function or specified by the caller.
* @throws An error if the data type is not supported or cannot be converted to a Blob.
*/
export function dataToBlob(data: any, dataFormat?: string): {
/** A Blob representation of the input data. */
dataBlob: Blob;
/** The MIME type of the data. */
dataFormat: string;
} {
let dataBlob: Blob;
// Check for Object or String, and if neither, assume bytes.
const detectedType = universalTypeOf(data);
if (dataFormat === 'text/plain' || detectedType === 'String') {
dataBlob = new Blob([data], { type: 'text/plain' });
} else if (dataFormat === 'application/json' || detectedType === 'Object') {
const dataBytes = Convert.object(data).toUint8Array();
dataBlob = new Blob([dataBytes], { type: 'application/json' });
} else if (detectedType === 'Uint8Array' || detectedType === 'ArrayBuffer') {
dataBlob = new Blob([data], { type: 'application/octet-stream' });
} else if (detectedType === 'Blob') {
dataBlob = data;
} else {
throw new Error('data type not supported.');
}
dataFormat = dataFormat || dataBlob.type || 'application/octet-stream';
return { dataBlob, dataFormat };
}
/**
* The `SendCache` class provides a static caching mechanism to optimize the process of sending
* records to remote DWN targets by minimizing redundant sends.
*
* It maintains a cache of record IDs and their associated target DIDs to which they have been sent.
* This helps in avoiding unnecessary network requests and ensures efficient data synchronization
* across Decentralized Web Nodes (DWNs).
*
* The cache employs a simple eviction policy to maintain a manageable size, ensuring that the cache
* does not grow indefinitely and consume excessive memory resources.
*
* @beta
*/
export class SendCache {
/**
* A private static map that serves as the core storage mechanism for the cache. It maps record
* IDs to a set of target DIDs, indicating which records have been sent to which targets.
*/
private static cache = new Map<string, Set<string>>();
/**
* The maximum number of entries allowed in the cache. Once this limit is exceeded, the oldest
* entries are evicted to make room for new ones. This limit applies both to the number of records
* and the number of targets per record.
*/
private static sendCacheLimit = 100;
/**
* Checks if a given record ID has been sent to a specified target DID. This method is used to
* determine whether a send operation is necessary or if it can be skipped to avoid redundancy.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to check against.
* @returns A boolean indicating whether the record has been sent to the target.
*/
public static check(id: string, target: string): boolean {
let targetCache = SendCache.cache.get(id);
return targetCache ? targetCache.has(target) : false;
}
/**
* Adds or updates an entry in the cache for a given record ID and target DID. If the cache
* exceeds its size limit, the oldest entry is removed. This method ensures that the cache
* reflects the most recent sends.
*
* @param id - The unique identifier of the record.
* @param target - The DID of the target to which the record has been sent.
*/
public static set(id: string, target: string): void {
let targetCache = SendCache.cache.get(id) || new Set();
SendCache.cache.delete(id);
SendCache.cache.set(id, targetCache);
if (this.cache.size > SendCache.sendCacheLimit) {
const firstRecord = SendCache.cache.keys().next().value;
SendCache.cache.delete(firstRecord);
}
targetCache.delete(target);
targetCache.add(target);
if (targetCache.size > SendCache.sendCacheLimit) {
const firstTarget = targetCache.keys().next().value;
targetCache.delete(firstTarget);
}
}
}
+30
View File
@@ -0,0 +1,30 @@
import type { Web5Agent } from '@web5/agent';
/**
* The VC API is used to issue, present and verify VCs
*
* @beta
*/
export class VcApi {
/**
* Holds the instance of a {@link Web5Agent} that represents the current execution context for
* the `VcApi`. This agent is used to process VC requests.
*/
private agent: Web5Agent;
/** The DID of the tenant under which DID operations are being performed. */
private connectedDid: string;
constructor(options: { agent: Web5Agent, connectedDid: string }) {
this.agent = options.agent;
this.connectedDid = options.connectedDid;
}
/**
* Issues a VC (Not implemented yet)
*/
async create() {
// TODO: implement
throw new Error('Not implemented.');
}
}
+28
View File
@@ -0,0 +1,28 @@
declare const ServiceWorkerGlobalScope: any;
/**
* Installs the DWeb networking features in the current environment.
*/
export function installNetworkingFeatures(path: string): void {
const workerSelf = self as any;
try {
if (typeof ServiceWorkerGlobalScope !== 'undefined' && workerSelf instanceof ServiceWorkerGlobalScope) {
// Dynamically import service worker code only if we're in a Service Worker context
import('./service-worker.js').catch(error => {
console.error('Error loading service worker module:', error);
});
}
else if (globalThis?.navigator?.serviceWorker) {
if (path) navigator.serviceWorker.register(path).catch(error => {
console.error('DWeb networking feature installation failed: ', error);
});
}
else {
throw new Error('DWeb networking features are not available for install in this environment');
}
} catch (error) {
console.error('Error in installing networking features:', error);
}
}
+272
View File
@@ -0,0 +1,272 @@
import type { BearerIdentity, HdIdentityVault, Web5Agent } from '@web5/agent';
import { Web5UserAgent } from '@web5/user-agent';
import { VcApi } from './vc-api.js';
import { DwnApi } from './dwn-api.js';
import { DidApi } from './did-api.js';
import { getTechPreviewDwnEndpoints } from './tech-preview.js';
/** Override defaults configured during the technical preview phase. */
export type TechPreviewOptions = {
/** Override default dwnEndpoints provided for technical preview. */
dwnEndpoints?: string[];
}
/** Optional overrides that can be provided when calling {@link Web5.connect}. */
export type Web5ConnectOptions = {
/**
* Provide a {@link Web5Agent} implementation. Defaults to creating a local
* {@link Web5UserAgent} if one isn't provided
**/
agent?: Web5Agent;
/**
* Provide an instance of a {@link HdIdentityVault} implementation. Defaults to
* a LevelDB-backed store with an insecure, static unlock password if one
* isn't provided. To allow the app user to enter a secure password of
* their choosing, provide an initialized {@link HdIdentityVault} instance.
**/
agentVault?: HdIdentityVault;
/** Specify an existing DID to connect to. */
connectedDid?: string;
/**
* The Web5 app `password` is used to protect data on the device the application is running on.
*
* Only the end user should know this password: it should not be stored on the device or
* transmitted over the network.
*
* This password is crucial for the security of an identity vault that stores the local Agent's
* cryptographic keys and decentralized identifier (DID). The vault's content is encrypted using
* the password, making it accessible only to those who know the password.
*
* App users should be advised to use a strong, unique passphrase that is not shared across
* different services or applications. The password should be kept confidential and not be
* exposed to unauthorized entities. Losing the password may result in irreversible loss of
* access to the vault's contents.
*/
password?: string;
/**
* The `recoveryPhrase` is a unique, secure key for recovering the identity vault.
*
* This phrase is a series of 12 words generated securely and known only to the user. It plays a
* critical role in the security of the identity vault by enabling the recovery of the vault's
* contents, including cryptographic keys and the Agent's decentralized identifier (DID), across
* different devices or if the original device is compromised or lost.
*
* The recovery phrase is akin to a master key, as anyone with access to this phrase can restore
* and access the vault's contents. Its combined with the app `password` to encrypt the vault's
* content.
*
* Unlike a password, the recovery phrase is not intended for regular use but as a secure backup
* method for vault recovery. Losing this phrase can result in permanent loss of access to the
* vault's contents, as it cannot be reset or retrieved if forgotten.
*
* Users should treat the recovery phrase with the highest level of security, ensuring it is
* never shared, stored online, or exposed to potential threats. It is the user's responsibility
* to keep this phrase safe to maintain the integrity and accessibility of their secured data. It
* is recommended to write it down and store it in a secure location, separate from the device and
* digital backups.
*/
recoveryPhrase?: string;
/**
* Enable synchronization of DWN records between local and remote DWNs.
* Sync defaults to running every 2 minutes and can be set to any value accepted by `ms()`.
* To disable sync set to 'off'.
*/
sync?: string;
/**
* Override defaults configured during the technical preview phase.
* See {@link TechPreviewOptions} for available options.
*/
techPreview?: TechPreviewOptions;
}
/**
* Represents the result of the Web5 connection process, including the Web5 instance,
* the connected decentralized identifier (DID), and optionally the recovery phrase used
* during the agent's initialization.
*/
export type Web5ConnectResult = {
/** The Web5 instance, providing access to the agent, DID, DWN, and VC APIs. */
web5: Web5;
/** The DID that has been connected or created during the connection process. */
did: string;
/**
* The first time a Web5 agent is initialized, the recovery phrase that was used to generate the
* agent's DID and keys is returned. This phrase can be used to recover the agent's vault contents
* and should be stored securely by the user.
*/
recoveryPhrase?: string;
};
/**
* Parameters that are passed to Web5 constructor.
*
* @see {@link Web5ConnectOptions}
*/
export type Web5Params = {
/**
* A {@link Web5Agent} instance that handles DIDs, DWNs and VCs requests. The agent manages the
* user keys and identities, and is responsible to sign and verify messages.
*/
agent: Web5Agent;
/** The DID of the tenant under which all DID, DWN, and VC requests are being performed. */
connectedDid: string;
};
/**
* The main Web5 API interface. It manages the creation of a DID if needed, the connection to the
* local DWN and all the web5 main foundational APIs such as VC, syncing, etc.
*/
export class Web5 {
/**
* A {@link Web5Agent} instance that handles DIDs, DWNs and VCs requests. The agent manages the
* user keys and identities, and is responsible to sign and verify messages.
*/
agent: Web5Agent;
/** Exposed instance to the DID APIs, allow users to create and resolve DIDs */
did: DidApi;
/** Exposed instance to the DWN APIs, allow users to read/write records */
dwn: DwnApi;
/** Exposed instance to the VC APIs, allow users to issue, present and verify VCs */
vc: VcApi;
/** The DID of the tenant under which DID operations are being performed. */
private connectedDid: string;
constructor({ agent, connectedDid }: Web5Params) {
this.agent = agent;
this.connectedDid = connectedDid;
this.did = new DidApi({ agent, connectedDid });
this.dwn = new DwnApi({ agent, connectedDid });
this.vc = new VcApi({ agent, connectedDid });
}
/**
* Connects to a {@link Web5Agent}. Defaults to creating a local {@link Web5UserAgent} if one
* isn't provided.
*
* @param options - Optional overrides that can be provided when calling {@link Web5.connect}.
* @returns A promise that resolves to a {@link Web5} instance and the connected DID.
*/
static async connect({
agent, agentVault, connectedDid, password, recoveryPhrase, sync, techPreview
}: Web5ConnectOptions = {}): Promise<Web5ConnectResult> {
if (agent === undefined) {
// A custom Web5Agent implementation was not specified, so use default managed user agent.
const userAgent = await Web5UserAgent.create({ agentVault });
agent = userAgent;
// Warn the developer and application user of the security risks of using a static password.
if (password === undefined) {
password = 'insecure-static-phrase';
console.warn(
'%cSECURITY WARNING:%c ' +
'You have not set a password, which defaults to a static, guessable value. ' +
'This significantly compromises the security of your data. ' +
'Please configure a secure, unique password.',
'font-weight: bold; color: red;',
'font-weight: normal; color: inherit;'
);
}
// Initialize, if necessary, and start the agent.
if (await userAgent.firstLaunch()) {
recoveryPhrase = await userAgent.initialize({ password, recoveryPhrase });
}
await userAgent.start({ password });
// TODO: Replace stubbed connection attempt once Connect Protocol has been implemented.
// Attempt to Connect to localhost agent or via Connect Server.
// userAgent.connect();
const notConnected = true;
if (/* !userAgent.isConnected() */ notConnected) {
// Connect attempt failed or was rejected so fallback to local user agent.
let identity: BearerIdentity;
// Query the Agent's DWN tenant for identity records.
const identities = await userAgent.identity.list();
// If an existing identity is not found found, create a new one.
const existingIdentityCount = identities.length;
if (existingIdentityCount === 0) {
// Use the specified DWN endpoints or get default tech preview hosted nodes.
const serviceEndpointNodes = techPreview?.dwnEndpoints ?? await getTechPreviewDwnEndpoints();
// Generate a new Identity for the end-user.
identity = await userAgent.identity.create({
didMethod : 'dht',
metadata : { name: 'Default' },
didOptions : {
services: [
{
id : 'dwn',
type : 'DecentralizedWebNode',
serviceEndpoint : serviceEndpointNodes,
enc : '#enc',
sig : '#sig',
}
],
verificationMethods: [
{
algorithm : 'Ed25519',
id : 'sig',
purposes : ['assertionMethod', 'authentication']
},
{
algorithm : 'secp256k1',
id : 'enc',
purposes : ['keyAgreement']
}
]
}
});
// The User Agent will manage the Identity, which ensures it will be available on future
// sessions.
await userAgent.identity.manage({ portableIdentity: await identity.export() });
} else if (existingIdentityCount === 1) {
// An existing identity was found in the User Agent's tenant.
identity = identities[0];
} else {
throw new Error(`connect() failed due to unexpected state: Expected 1 but found ${existingIdentityCount} stored identities.`);
}
// Set the stored identity as the connected DID.
connectedDid = identity.did.uri;
}
// Enable sync, unless explicitly disabled.
if (sync !== 'off') {
// First, register the user identity for sync.
await userAgent.sync.registerIdentity({ did: connectedDid });
// Enable sync using the specified interval or default.
sync ??= '2m';
userAgent.sync.startSync({ interval: sync })
.catch((error: any) => {
console.error(`Sync failed: ${error}`);
});
}
}
const web5 = new Web5({ agent, connectedDid });
return { web5, did: connectedDid, recoveryPhrase };
}
}