Socket
Socket
Sign inDemoInstall

arbase

Package Overview
Dependencies
25
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.5 to 0.2.0

src/types/block.js

4

package.json
{
"name": "arbase",
"version": "0.1.5",
"version": "0.2.0",
"description": "Arbase is a tool to create object-based APIs on top of arweave in mere minutes",

@@ -31,3 +31,3 @@ "main": "src/index.js",

"@hapi/joi": "^16.1.7",
"arlang": "^0.1.3",
"arlang": "^0.1.4",
"arweave": "^1.4.1",

@@ -34,0 +34,0 @@ "base-x": "^3.0.7",

@@ -7,3 +7,3 @@ 'use strict'

const { decodeAndValidate, decodeAndValidateList, ListEventType, decodeTxData } = require('./process')
const { decodeAndValidate, decodeTxData } = require('./process')

@@ -35,73 +35,233 @@ const queue = require('../queue')()

function validateListEntry (entry, listEntry, {data, tags}) {
// TODO: return false if invalid
return {data, tags}
const typeRE = /(@([a-z0-9]+)\/)?([a-z0-9]+)/i
function parseType (str) {
const [,, ns, name] = str.match(typeRE)
return {ns, name}
}
function joinListOplog (data, idMap, tx) {
// TODO: add
const lexer = require('arlang/src/lexer')
/*
function parser (query, config = {}, e) {
const tokens = lexer(query)
op is either append or delete
target is a blockId
let block = 'select'
*/
let i = 0
switch (data.type) {
case ListEventType.APPEND: {
idMap[data.target] = data.push(data.target)
break
query = { /* tags: {}, */ order: [] }
function expect (type, value) {
const token = tokens[i]
if (token.type !== type) throw new TypeError('Expected ' + type + ', got ' + token.type)
if (value) {
if (token.value !== value) throw new TypeError('Expected ' + value + ', got ' + token.value)
}
case ListEventType.DELETE: {
delete data[idMap[data.target]]
break
}
function assume (type, value) {
const token = tokens[i]
return (token.type === type && ((!value) || (token.value === value)))
}
// TODO: handle deepRefs such as x.y
while (tokens[i]) {
switch (block) {
case 'select': {
expect('literal', 'select')
i++
block = 'single'
break
}
case 'single': {
if (assume('literal', 'single')) {
query.single = true
i++
}
block = 'type'
break
}
case 'type': {
expect('literal')
query.type = parseType(tokens[i].value)
query.typeObj = e.entry[query.type.ns || null][query.type.name]
block = 'where'
i++
break
}
case 'where': {
if (assume('literal', 'where')) {
block = 'whereInner'
i++
} else {
block = 'order'
}
break
}
case 'whereInner': {
if (assume('literal', 'order')) {
block = 'orderInner'
i++
break
}
// TODO v2: expect "=" eq, "<" lt, ">" gt, "<=" lteq, ">=" gteq, "LIKE" (like)
// TODO v1: expect "=" eq, "!=" not eq
// TODO v0: just arlang query
// query is string
query.arql = arlang(tokens[i].value, {lang: config.arqlLang || 'sym', params: config.params})
i++
break
}
case 'where2Inner': {
// where query on data AFTER processing
/*
// TODO: expect literal or string
const tag = token.value
i++
const op = token.value
i++
// expect literal, integer, string
const comp = token.value
i++
out.tags[tag] = {
op,
comp
} */
break
}
case 'order': {
if (assume('literal', 'order')) {
i++
expect('literal', 'by')
i++
block = 'orderInner'
} else {
block = 'eof'
}
break
}
case 'orderInner': {
// TODO: expect literal or string
const key = tokens[i].value
i++
// TODO: expect literal val asc/desc
const type = tokens[i].value
i++
query.order.push([key, type])
break
}
case 'eof': {
expect('(no further tokens expected)')
break
}
default: {
throw new TypeError(block)
}
}
default: {
throw new TypeError(data.op)
}
}
return query
}
module.exports = (arweave) => {
/* const OPs = {
eq: (val, comp) => val === comp,
lt: (val, comp) => val > comp,
gt: (val, comp) => val < comp,
lteq: (val, comp) => val >= comp,
gteq: (val, comp) => val <= comp,
like: (val, comp) => null // TODO: check for strings via substr, for numbers via compare
} */
module.exports = (arweave, e) => {
async function fetchTransaction (id) {
return decodeTxData(await arweave.transactions.get(id))
const tx = await arweave.transactions.get(id)
const tags = {}
tx.get('tags').forEach(tag => {
const key = tag.get('name', {decode: true, string: true})
const value = tag.get('value', {decode: true, string: true})
tags[key] = value
})
const time = 0 // TODO: fix this because time blargh
return {
data: decodeTxData(tx),
tags,
time,
owner: tx.owner, // TODO: check if works
id
}
}
const f = {
list: async (entry, listEntry, id, parse) => {
let data = []
let idMap = {}
const {data: txs, live} = await arweave.arql($arql('& (= block $1) (= child $2)', id, String(listEntry.id)))
// SELECT topic WHERE parent = 'someTopic' -> get topic ORDER BY createdAt
// SELECT SINGLE topic WHERE rid = 'rid' -> get single
// single indicates it should just yield a single element (validation is whether or not rid exists, so only do )
//
// TODO: all tags must be the same for all changes, otherwise querying breaks horribly
// TODO: possible make tags hex but store binary?
// TODO: tags should be processed in the same way as attributes, just instead their "tags" named and no modify perms
// TODO: lists are trash now
// TODO: acls would need a "what is our previous element" reference helper, to say that for ex "p" is previous element tag and then read that
// TODO: better queuing
queue.init(id, 3, 50)
// TODO: make one where query, but seperate tags into arql and data into where2
const txLog = txs.reverse().map(() => queue(id, async () =>
decodeAndValidateList(await fetchTransaction(id))))
// TODO: flatened ACLs (isHirarchy$rootId, hirarchyPosition=$actualId)
// board $id -> topic $id
// board acl=isHirarchy$board
// topic acl=isHirarchy$board, isHirarchy$board
// tree is determined by ACL parent resolution
// acl resolution would ask for topic acl using isHirarchy$root and then filter out valid board acl after parsing all using hirarchy=$topicId
for (let i = txLog.length; i > -1; i--) {
const tx = await txLog[i]
if (tx) {
data = joinListOplog(data, idMap, tx)
}
}
// aclv2:
// or(equals(h, $board), equals(h, $topic), equals(h, $subtopic)) - this would give us ACLs for the entire hirarchy, now just need efficient way to fetch hirarchy
if (!parse) {
return {data: data.filter(Boolean), live}
}
// TODO: should we instead enforce parent as a tag and make it a tree?!
const { offset, limit } = parse
query: async function query (query, _qconf) {
query = typeof query === 'string' ? parser(query, _qconf, e) : query
// TODO: use id as cursor
const total = data.filter(Boolean).length
const range = data.filter(Boolean).reverse().slice(offset, offset + limit)
const {typeObj: entry} = query
return {
data: range,
total,
live
const el = {}
const {data: txs, live} = await arweave.arql(query.arql)
for (let i = txs.length; i < txs.length; i--) {
const {data, tags, time, owner, id} = await fetchTransaction(txs[i])
// TODO: acl
if (tags.a === 'c') {
el[tags.i] = await decodeAndValidate(entry, data, false)
el[tags.i].id = tags.i
el[tags.i].tx = [id]
el[tags.i].createdAt = time
el[tags.i].owner = owner
} else if (tags.a === 'e') {
el[tags.i] = joinOplog(el[tags.i], await decodeAndValidate(entry, data, true)) // joinOplog(obj, await decodeAndValidate(entry, data, true))
el[tags.i].tx.push(id)
el[tags.i].modifiedAt = time
} else if (tags.a === 'd') {
delete el[tags.i]
// TODO: instead set .deletedAt and do soft delete? (we could also do a='r' to restore)
}
}
// TODO: handle single flag
return {data: el, live}
},

@@ -133,9 +293,4 @@ entry: async function fetchEntry (entry, id) {

const txLog = txs.reverse().map(() => queue(id, async () => {
const fetched = await fetchTransaction(id)
return validateEntry(entry, fetched, false)
}))
for (let i = txLog.length; i > -1; i--) {
const tx = await txLog[i]
for (let i = txs.length; i > -1; i--) {
const tx = await txs[i]
if (tx) {

@@ -147,2 +302,4 @@ const data = await fetchTransaction(tx)

obj.id = id
return {data: obj, live}

@@ -149,0 +306,0 @@ }

'use strict'
module.exports = (arweave) => {
module.exports = (arweave, e) => {
return {
read: require('./fetch')(arweave),
write: require('./update')(arweave)
read: require('./fetch')(arweave, e),
write: require('./update')(arweave, e)
}
}

@@ -5,2 +5,4 @@ 'use strict'

// TODO: attempt compression using $algo and use multiprefix, then uses that if smaller in total
const Joi = require('@hapi/joi')

@@ -11,17 +13,2 @@

const protons = require('protons')
const { ListEvent, ListEventType } = protons(`
enum ListEventType {
APPEND = 1;
DELETE = 2;
}
message ListEvent {
ListEventType type = 1;
bytes blockId = 2;
}
`)
async function decodeAndValidate (entry, data, half) {

@@ -77,33 +64,2 @@ const decoded = entry.message.decode(data)

const listValidator = Joi.object({
type: Joi.number().integer().required(),
blockId: Joi.string().regex(/[a-zA-Z0-9_-]{43}/).required()
})
async function decodeAndValidateList (data) {
const decoded = ListEvent.decode(data)
decoded.blockId = b.encode(decoded.blockId)
const {error, value} = listValidator.validate(data)
if (error) {
throw error
}
return value
}
async function validateAndEncodeList (data) {
const {error, value} = listValidator.validate(data)
value.blockId = b.decode(value.blockId)
if (error) {
throw error
}
return ListEvent.encode(value)
}
function decodeTxData (tx) {

@@ -121,6 +77,2 @@ return Buffer.from(tx.get('data', {decode: true}))

decodeAndValidateList,
validateAndEncodeList,
ListEventType,
decodeTxData,

@@ -127,0 +79,0 @@ encodeTxData,

'use strict'
const { validateAndEncode, validateAndEncodeList, ListEventType, encodeTxData } = require('./process')
const crypto = require('crypto')
const getRandomID = () => crypto.randomBytes(16).toString('hex')
const { validateAndEncode, encodeTxData } = require('./process')
async function createTx (data, arweave) {

@@ -13,20 +16,25 @@ return arweave.createTransaction({

async function entryCreate (arweave, entry, val) {
async function entryCreate (arweave, entry, tags, val) {
const tx = await createTx(await validateAndEncode(entry, val), arweave)
const rid = getRandomID()
for (const tag in tags) { // eslint-disable-line guard-for-in
tx.addTag(tag, tags[tag])
}
tx.addTag('i', rid)
tx.addTag('a', 'c')
return tx
}
async function entryModify (arweave, entry, id, diff) {
async function entryModify (arweave, entry, rid, tags, diff) {
const tx = await createTx(await validateAndEncode(entry, diff, true), arweave)
tx.addTag('block', id)
tx.addTag('child', '#')
return tx
}
for (const tag in tags) { // eslint-disable-line guard-for-in
tx.addTag(tag, tags[tag])
}
async function entryDelete (arweave, entry, id, diff) {
const tx = await createTx(/* TODO */ '', arweave)
tx.addTag('block', id)
tx.addTag('child', '#')
tx.addTag('i', rid)
tx.addTag('a', 'e')

@@ -36,15 +44,11 @@ return tx

// TODO: rewrite below
async function listAppend (arweave, entry, listEntry, id, blockId) {
const tx = await createTx(await validateAndEncode({ type: ListEventType.APPEND, blockId }), arweave)
tx.addTag('block', id)
tx.addTag('child', String(listEntry.id))
async function entryDelete (arweave, entry, rid, tags) {
const tx = await createTx(Buffer.from(''), arweave) // TODO: add contents
return tx
}
for (const tag in tags) { // eslint-disable-line guard-for-in
tx.addTag(tag, tags[tag])
}
async function listRemove (arweave, entry, listEntry, id, blockId) {
const tx = await createTx(await validateAndEncode({ type: ListEventType.DELETE, blockId }), arweave)
tx.addTag('block', id)
tx.addTag('child', String(listEntry.id))
tx.addTag('i', rid)
tx.addTag('a', 'd')

@@ -54,9 +58,7 @@ return tx

module.exports = (arweave) => {
module.exports = (arweave, e) => {
const out = {
entryCreate,
entryModify,
entryDelete,
listAppend,
listRemove
entryDelete
}

@@ -63,0 +65,0 @@

'use strict'
module.exports = {
file: require('./file'),
block: require('./block'),
number: require('./number'),
string: require('./string')
string: require('./string'),
jsonb: require('./jsonb')
}
SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc