From 9b1aafbcf241d268e4f365ed99005458dda1a05a Mon Sep 17 00:00:00 2001 From: Travis Vachon Date: Tue, 12 Dec 2023 16:06:12 -0800 Subject: [PATCH] fix: support storing ArrayBuffers in conf (#1236) I made a change to use ArrayBuffers in AgentDataExport, which was not supported by our JSON.stringify function. Tweak the stringify replacer to handle this cleanly and add a test to verify it works as expected. --- packages/access-client/package.json | 4 +- packages/access-client/src/utils/json.js | 2 + .../test/stores/store-conf.node.test.js | 78 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 packages/access-client/test/stores/store-conf.node.test.js diff --git a/packages/access-client/package.json b/packages/access-client/package.json index 36101122b..951563cca 100644 --- a/packages/access-client/package.json +++ b/packages/access-client/package.json @@ -18,11 +18,11 @@ "lint": "tsc --build && eslint '**/*.{js,ts}' && prettier --check '**/*.{js,ts,yml,json}' --ignore-path ../../.gitignore", "build": "tsc --build", "check": "tsc --build", - "test": "pnpm -r run build && npm run test:node && npm run test:browser", + "test": "pnpm -r run build && pnpm run test:node && pnpm run test:browser", "test:node": "mocha 'test/**/!(*.browser).test.js' -n experimental-vm-modules -n no-warnings", "test:browser": "playwright-test 'test/**/!(*.node).test.js'", "testw": "watch 'pnpm test' src test --interval 1", - "rc": "npm version prerelease --preid rc" + "rc": "pnpm version prerelease --preid rc" }, "exports": { ".": { diff --git a/packages/access-client/src/utils/json.js b/packages/access-client/src/utils/json.js index 588c49645..1e4e8c341 100644 --- a/packages/access-client/src/utils/json.js +++ b/packages/access-client/src/utils/json.js @@ -11,6 +11,8 @@ export const replacer = (k, v) => { return { $map: [...v.entries()] } } else if (v instanceof Uint8Array) { return { $bytes: [...v.values()] } + } else if (v instanceof ArrayBuffer) { + return { $bytes: [...new Uint8Array(v).values()] } } else if (v?.type === 'Buffer' && Array.isArray(v.data)) { return { $bytes: v.data } } diff --git a/packages/access-client/test/stores/store-conf.node.test.js b/packages/access-client/test/stores/store-conf.node.test.js new file mode 100644 index 000000000..24a63821c --- /dev/null +++ b/packages/access-client/test/stores/store-conf.node.test.js @@ -0,0 +1,78 @@ +import assert from 'assert' +import { top } from '@web3-storage/capabilities/top' +import { Signer as EdSigner } from '@ucanto/principal/ed25519' +import * as RSASigner from '@ucanto/principal/rsa' +import { AgentData } from '../../src/agent-data.js' +import { StoreConf } from '../../src/stores/store-conf.js' + +describe('Conf store', () => { + it('should create and load data', async () => { + const data = await AgentData.create({ + principal: await RSASigner.generate({ extractable: false }), + }) + + const store = new StoreConf({ profile: 'test-access-db-' + Date.now() }) + await store.open() + await store.save(data.export()) + + const exportData = await store.load() + assert(exportData) + + // no accounts or delegations yet + assert.equal(exportData.spaces.size, 0) + assert.equal(exportData.delegations.size, 0) + + // default meta + assert.equal(exportData.meta.name, 'agent') + assert.equal(exportData.meta.type, 'device') + }) + + it('should round trip delegations', async () => { + const store = new StoreConf({ profile: 'test-access-db-' + Date.now() }) + await store.open() + + const data0 = await AgentData.create() + const signer = await EdSigner.generate() + const del0 = await top.delegate({ + issuer: signer, + audience: data0.principal, + with: signer.did(), + expiration: Infinity, + }) + + await data0.addDelegation(del0, { + audience: { name: 'test', type: 'device' }, + }) + await store.save(data0.export()) + + const exportData1 = await store.load() + assert(exportData1) + + const data1 = AgentData.fromExport(exportData1) + + const { delegation: del1 } = + data1.delegations.get(del0.cid.toString()) ?? {} + assert(del1) + assert.equal(del1.cid.toString(), del0.cid.toString()) + assert.equal(del1.issuer.did(), del0.issuer.did()) + assert.equal(del1.audience.did(), del0.audience.did()) + assert.equal(del1.capabilities[0].can, del0.capabilities[0].can) + assert.equal(del1.capabilities[0].with, del0.capabilities[0].with) + }) + + it('should be resettable', async () => { + const principal = await RSASigner.generate({ extractable: false }) + const data = await AgentData.create({ principal }) + + const store = new StoreConf({ profile: 'test-access-db-' + Date.now() }) + await store.open() + await store.save(data.export()) + + const exportData = await store.load() + assert.equal(exportData?.principal.id, principal.did()) + + await store.reset() + const resetExportData = await store.load() + assert.equal(resetExportData?.principal.id, undefined) + }) +})