Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ar-wrapper

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ar-wrapper - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

20

index.d.ts

@@ -14,2 +14,8 @@ declare module "ar-wrapper" {

interface FetchSettingsI {
maxRetries: number
verifiedOnly: boolean
maxResults: number
}
export interface BlockDocument {

@@ -30,5 +36,7 @@ name: string

export const DEFAULT_OPTIONS: OptionsI
interface Serializable {
toString: (any) => string
}
export class Document {
export class Document<T = string> {
txID: string

@@ -40,3 +48,3 @@ client: ArweaveClient

name: string
content: any
content: T
version: number

@@ -62,5 +70,7 @@ tags: Record<string, string>

pollForConfirmation(txId: string, maxRetries?: number): Promise<BlockStatusI>
getDocumentByName(name: string, version?: number, maxRetries?: number, verifiedOnly?: boolean): Promise<Document>
getDocumentByTxId(txId: string, maxRetries?: number, verifiedOnly?: boolean): Promise<Document>
executeQuery(names: string[], versions: number[], userTags: Record<string, string>, userOptions?: Partial<FetchSettingsI>)
getDocumentsByName(name: string, version?: number, tags?: Record<string, string>, options?: Partial<FetchSettingsI>): Promise<Document[]>
getDocumentsByTags(tags: Record<string, string>, options?: Partial<FetchSettingsI>): Promise<Document[]>
getDocumentByTxId(txId: string, userOptions?: Partial<FetchSettingsI>): Promise<Document>
}
}

@@ -6,3 +6,3 @@ const ArweaveLib = require('arweave')

const DEFAULT_OPTIONS = {
const DEFAULT_ARWEAVE_OPTIONS = {
host: 'arweave.net',

@@ -15,2 +15,8 @@ port: 443,

const DEFAULT_FETCH_OPTIONS = {
maxRetries: 10,
verifiedOnly: true,
maxResults: 25,
}
// A single managed document containing arbitrary content.

@@ -80,2 +86,3 @@ // Should not be constructed manually, do this through the `ArweaveClient`

const NAME = "DOC_NAME"
const META = "DOC_META"

@@ -98,3 +105,3 @@ // Thin wrapper client around Arweave for versioned document/data management.

// Options are identical to the ones supported by the official `arweave-js` library.
constructor(adminAddress, keyFile, cacheSize = 500, options = DEFAULT_OPTIONS) {
constructor(adminAddress, keyFile, cacheSize = 500, options = DEFAULT_ARWEAVE_OPTIONS) {
this.#key = JSON.parse(keyFile)

@@ -116,2 +123,7 @@ this.adminAddr = adminAddress

// add user defined metadata
Object.entries(doc.tags).forEach(([tag, content]) => {
tx.addTag(`${META}_${tag}`, content)
})
// sign + send tx

@@ -129,3 +141,3 @@ await this.client.transactions.sign(tx, this.#key)

doc.posted = true
this.cache.set(doc.name, doc)
this.cache.set(doc.txID, doc)
return doc

@@ -136,4 +148,4 @@ }

// Optionally define desired version to match against.
isCached(documentName, desiredVersion) {
const inCache = this.cache.has(documentName)
isCached(txId, desiredVersion) {
const inCache = this.cache.has(txId)
if (!inCache) {

@@ -143,3 +155,3 @@ return false

const cached = this.cache.get(documentName)
const cached = this.cache.get(txId)
const versionMatch = desiredVersion !== undefined ? cached.version === desiredVersion : true

@@ -159,3 +171,3 @@ return cached.posted && versionMatch

// check if cache has latest version of document
if (this.isCached(document.name, document.version)) {
if (this.isCached(document.txID, document.version)) {
return document

@@ -166,3 +178,2 @@ }

await this.#insert(document)
this.cache.set(document.name, document)
return document

@@ -177,2 +188,6 @@ }

if (this.cache.has(txId)) {
return true
}
return await backOff(async () => {

@@ -193,8 +208,17 @@ const txStatus = await this.client.transactions.getStatus(txId)

// all submitted TXs (including ones from non-admin wallet accounts)
#queryBuilder(names, versions, verifiedOnly = true) {
const tags = [`{
name: "${NAME}",
values: ${JSON.stringify(names)},
}`]
#queryBuilder(names, versions, userTags, verifiedOnly = true, cursor = undefined) {
// parse use defined tags
const tags = Object.entries(userTags).map(([k, v]) => `{
name: "${META}_${k}",
values: ["${v}"]
}`)
// add name tag
if (names.length > 0) {
tags.push(`{
name: "${NAME}",
values: ${JSON.stringify(names)},
}`)
}
// versions is an optional field

@@ -208,3 +232,2 @@ if (versions.length > 0) {

// TODO: handle pagination/cursor here
return {

@@ -215,5 +238,7 @@ query: `

tags: [${tags.join(",")}],
${verifiedOnly ? `owners: ["${this.adminAddr}"]` : ""}
${verifiedOnly ? `owners: ["${this.adminAddr}"],` : ""}
${cursor ? `after: "${cursor}",` : ""}
) {
edges {
cursor
node {

@@ -236,48 +261,80 @@ id

// Return a document object via lookup by name
async getDocumentByName(name, version, maxRetries = 10, verifiedOnly = true) {
// check if doc is in cache and entry is up to date (and correct version)
if (this.isCached(name, version)) {
return this.cache.get(name)
async executeQuery(names, versions, userTags, userOptions = DEFAULT_FETCH_OPTIONS) {
const options = {
...DEFAULT_FETCH_OPTIONS,
...userOptions,
}
// otherwise, fetch latest to cache
// build query to lookup by name (and optionally version) and send request to arweave graphql server
const query = this.#queryBuilder([name], version === undefined ? [] : [version], verifiedOnly)
const req = await fetch('https://arweave.net/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(query),
})
const json = await req.json()
const fetchQuery = async (cursor) => {
// fetch latest to cache
// build query to lookup by name (and optionally version) and send request to arweave graphql server
const query = this.#queryBuilder(names, versions, userTags, options.verifiedOnly, cursor)
const req = await fetch('https://arweave.net/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(query),
})
const json = await req.json()
return json.data.transactions
}
const resultEdges = []
let nResults = 1
let cursor = undefined
while (nResults > 0 && resultEdges.length < options.maxResults) {
const newEdges = await fetchQuery(cursor)
nResults = newEdges.edges.length
resultEdges.push(...newEdges.edges)
cursor = newEdges.cursor
}
// safe to get first item as we specify specific tags in the query building stage
const txId = version !== undefined ?
json.data.transactions.edges[0]?.node.id :
json.data.transactions.edges.sort((a, b) => {
const txIds = versions.length === 0 ?
resultEdges.map(e => e.node.id) :
resultEdges.sort((a, b) => {
// we reverse sort edges if version is not defined to get latest version
const getVersion = (edge) => edge.node.tags.find(tag => tag.name === VERSION).value || 0
return getVersion(b) - getVersion(a)
})[0]?.node.id
if (!txId) {
return Promise.reject(`No transaction with name ${name} found`)
}
}).map(e => e.node.id)
// fetch document, update cache
const doc = await this.getDocumentByTxId(txId, maxRetries)
this.cache.set(doc.name, doc)
return doc
const promises = txIds.map(txId => this.getDocumentByTxId(txId, options))
const docs = (await Promise.allSettled(promises))
.filter(p => p.status === "fulfilled")
.map(p => p.value)
.slice(0, userOptions.maxResults)
docs.forEach(doc => this.cache.set(doc.name, doc))
return docs
}
// Return a document object via lookup by transaction ID. Not cached.
async getDocumentByTxId(txId, maxRetries = 10, verifiedOnly = true) {
// Return a list of document objects by their tags
async getDocumentsByTags(tags, options = DEFAULT_FETCH_OPTIONS) {
return this.executeQuery([] ,[], tags, options)
}
// Return a document object via lookup by name
async getDocumentsByName(name, version, tags = [], options = DEFAULT_FETCH_OPTIONS) {
return this.executeQuery([name], version === undefined ? [] : [version], tags, options)
}
// Return a document object via lookup by transaction ID
async getDocumentByTxId(txId, userOptions = DEFAULT_FETCH_OPTIONS) {
const options = {
...DEFAULT_FETCH_OPTIONS,
...userOptions
}
if (this.cache.has(txId)) {
return this.cache.get(txId)
}
// ensure block with tx is confirmed (do not assume it is in cache)
const txStatus = await this.pollForConfirmation(txId, maxRetries)
const txStatus = await this.pollForConfirmation(txId, options.maxRetries)
// fetch tx metadata
const transactionMetadata = await this.client.transactions.get(txId)
if (verifiedOnly && transactionMetadata.owner !== this.#key.n) {
if (options.verifiedOnly && transactionMetadata.owner !== this.#key.n) {
return Promise.reject(`Document is not verified. Owner address mismatched! Got: ${transactionMetadata.owner}`)

@@ -284,0 +341,0 @@ }

{
"name": "ar-wrapper",
"version": "1.0.1",
"version": "1.1.0",
"description": "Thin wrapper around arweave-js for versioned permaweb document management",

@@ -5,0 +5,0 @@ "main": "index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc