From 12f44a0edc456ecdddbe9cdf9b1068812f7bef24 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 13 Nov 2022 08:45:00 -0800 Subject: [PATCH] chore: remove store package (#161) --- .github/release-please-config.json | 3 +- .github/release-please-manifest.json | 3 +- .github/workflows/store.yml | 31 --- packages/store/package.json | 87 -------- packages/store/readme.md | 165 --------------- packages/store/src/accounting/lib.js | 60 ------ packages/store/src/identity/capability.js | 85 -------- packages/store/src/identity/lib.js | 80 -------- packages/store/src/identity/provider.js | 187 ----------------- packages/store/src/lib.js | 6 - packages/store/src/signer/lib.js | 34 --- packages/store/src/signer/type.js | 0 packages/store/src/signer/type.ts | 10 - packages/store/src/store/capability.js | 61 ------ packages/store/src/store/lib.js | 81 -------- packages/store/src/store/provider.js | 114 ---------- packages/store/src/type.js | 0 packages/store/src/type.ts | 6 - packages/store/src/type/accounting.ts | 43 ---- packages/store/src/type/error.ts | 27 --- packages/store/src/type/identity.ts | 55 ----- packages/store/src/type/sketch.ts | 150 -------------- packages/store/src/type/store.ts | 83 -------- packages/store/test/fixtures.js | 18 -- packages/store/test/lib.spec.js | 240 ---------------------- packages/store/test/signer.test.js | 33 --- packages/store/test/test.js | 7 - packages/store/tsconfig.json | 10 - 28 files changed, 2 insertions(+), 1677 deletions(-) delete mode 100644 .github/workflows/store.yml delete mode 100644 packages/store/package.json delete mode 100644 packages/store/readme.md delete mode 100644 packages/store/src/accounting/lib.js delete mode 100644 packages/store/src/identity/capability.js delete mode 100644 packages/store/src/identity/lib.js delete mode 100644 packages/store/src/identity/provider.js delete mode 100644 packages/store/src/lib.js delete mode 100644 packages/store/src/signer/lib.js delete mode 100644 packages/store/src/signer/type.js delete mode 100644 packages/store/src/signer/type.ts delete mode 100644 packages/store/src/store/capability.js delete mode 100644 packages/store/src/store/lib.js delete mode 100644 packages/store/src/store/provider.js delete mode 100644 packages/store/src/type.js delete mode 100644 packages/store/src/type.ts delete mode 100644 packages/store/src/type/accounting.ts delete mode 100644 packages/store/src/type/error.ts delete mode 100644 packages/store/src/type/identity.ts delete mode 100644 packages/store/src/type/sketch.ts delete mode 100644 packages/store/src/type/store.ts delete mode 100644 packages/store/test/fixtures.js delete mode 100644 packages/store/test/lib.spec.js delete mode 100644 packages/store/test/signer.test.js delete mode 100644 packages/store/test/test.js delete mode 100644 packages/store/tsconfig.json diff --git a/.github/release-please-config.json b/.github/release-please-config.json index 4959779e8..91e689753 100644 --- a/.github/release-please-config.json +++ b/.github/release-please-config.json @@ -4,7 +4,6 @@ "packages/access": {}, "packages/access-api": {}, "packages/upload-client": {}, - "packages/wallet": {}, - "packages/store": {} + "packages/wallet": {} } } diff --git a/.github/release-please-manifest.json b/.github/release-please-manifest.json index 723a0c58c..e0d4b4168 100644 --- a/.github/release-please-manifest.json +++ b/.github/release-please-manifest.json @@ -1,6 +1,5 @@ { "packages/wallet": "1.0.0", "packages/access": "4.0.2", - "packages/access-api": "3.0.0", - "packages/store": "2.0.0" + "packages/access-api": "3.0.0" } diff --git a/.github/workflows/store.yml b/.github/workflows/store.yml deleted file mode 100644 index f0b95e312..000000000 --- a/.github/workflows/store.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Store API - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - 'packages/store/**' - - '.github/workflows/store.yml' - - 'pnpm-lock.yaml' - pull_request: - paths: - - 'packages/store/**' - - '.github/workflows/store.yml' - - 'pnpm-lock.yaml' -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.3 - with: - version: 7 - - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: 'pnpm' - - run: pnpm install - - run: pnpm -r --filter @web3-storage/store run lint - - run: pnpm -r --filter @web3-storage/store run test diff --git a/packages/store/package.json b/packages/store/package.json deleted file mode 100644 index e1cb19ef0..000000000 --- a/packages/store/package.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "name": "@web3-storage/store", - "description": "web3.storage upload service", - "version": "2.0.0", - "types": "./dist/src/lib.d.ts", - "main": "./src/lib.js", - "keywords": [ - "web3.storage" - ], - "files": [ - "src", - "dist/src" - ], - "repository": { - "type": "git", - "url": "https://github.com/web3-storage/w3-protocol.git", - "directory": "packages/store" - }, - "homepage": "https://github.com/web3-storage/w3-protocol/tree/main/packages/store", - "scripts": { - "check": "tsc --build", - "testsss": "npm run test:node", - "test:node": "mocha test", - "test:browser": "pw-test test", - "testw": "watch 'pnpm test' src test --interval 1", - "lintss": "tsc --build && eslint '**/*.{js,ts}' && prettier --check '**/*.{js,ts,yml,json}' --ignore-path ../../.gitignore" - }, - "dependencies": { - "@ucanto/client": "^3.0.1", - "@ucanto/core": "^3.0.1", - "@ucanto/interface": "^3.0.0", - "@ucanto/principal": "^3.0.0", - "@ucanto/server": "^3.0.1", - "@ucanto/transport": "^3.0.1", - "@ucanto/validator": "^3.0.1", - "@web-std/fetch": "^4.1.0", - "@web3-storage/sigv4": "^1.0.0", - "multiformats": "^10.0.2" - }, - "devDependencies": { - "@types/chai": "^4.3.0", - "@types/chai-subset": "^1.3.3", - "@types/mocha": "^10.0.0", - "chai": "^4.3.6", - "chai-subset": "^1.6.0", - "hd-scripts": "^3.0.2", - "mocha": "^10.1.0", - "playwright-test": "^8.1.1", - "typescript": "^4.8.4" - }, - "exports": { - ".": { - "types": "./dist/src/lib.d.ts", - "import": "./src/lib.js" - }, - "./identity": { - "types": "./dist/src/identity/lib.d.ts", - "import": "./src/identity/lib.js" - }, - "./accounting": { - "types": "./dist/src/accounting/lib.d.ts", - "import": "./src/accounting/lib.js" - } - }, - "eslintConfig": { - "extends": [ - "./node_modules/hd-scripts/eslint/index.js" - ], - "parserOptions": { - "project": "./tsconfig.json" - }, - "rules": { - "unicorn/no-null": "off", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "off", - "unicorn/no-empty-file": "off" - }, - "env": { - "mocha": true - }, - "ignorePatterns": [ - "dist" - ] - }, - "type": "module", - "license": "(Apache-2.0 AND MIT)" -} diff --git a/packages/store/readme.md b/packages/store/readme.md deleted file mode 100644 index 6d415ced7..000000000 --- a/packages/store/readme.md +++ /dev/null @@ -1,165 +0,0 @@ -# w3-store - -This package provides implementation of uploads v2 service that can be via HTTP server. It requires several components to do it's job which must be passed during instantiation: - -1. [Accounting.Provider](./store/src/type/accounting.ts#L16-L35) used to: - - 1. Persist user `DID -> CID` relationships. - 2. Query those relations. - 3. Check if CAR file for `${cid}/data` exists in file store. - - This is an interface by which "uploads" service interacts with "accounting" service through a **trusted** channel (meaning "accounting" performs no access control). - - Library includes reference implementation (not fit for production use) of this interface which can be instantiated as follows: - - ```ts - import { Accounting } from 'w3-store' - - const accounting = Accounting.create() - ``` - - Please not above instantiation uses in-memory store and will not presist data - across sessions. In serverless environments that means no data persisted - across requests. You may provide presistance layer by passing optional `db` - and `cars` options, in which case they will be used to persist state - - ```ts - import { Accounting } from "w3-store" - - const accounting = Accounting.create({ - // Some key value store that can persist data across sessions - db: { - /** - * Takes DID in string representation and expects - * a map of CID string -> CID` - */ - async get(key:string): Promise> { - // your implementation here - } - async set(key:string, links:Map>):Promise { - // your implementation here - } - }, - // store that can check if car file is in the file store - cars: { - async has(key:string): Promise { - // .... - } - } - }) - ``` - - Reference implementation is not fit for production, instead you should write - your own [Accounting.Provider](./store/src/type/accounting.ts#L16-L35) - implementation. - -2. "ucanto connection" to an [Identity](./store/src/type/identity.ts#L11-L38) - service, used to: - - 1. Identify whether users with specific DID has a registered account. - 2. Redirect `identity/*` capabilities to `Identity` service. - - This is an interface by which "uploads" service interacts with "identity" service through an **untrusted** channel (meaning that "identity" service verifies - UCANs and deny service if not authorized). - - Library includes reference implementation (not fit for production use) of the - "identity" service which you can create and obtain in-process "ucanto connection" to it: - - ```ts - import { Identity } from 'w3-store' - - const identity = Identity.create({ - // base64url encoded Ed25519 keypair - keypair: process.env.W3_ID_KEYPAIR, - }) - - const connection = identity.connect() - ``` - - However above instantiation will use in-memory store, which will not presist state - across sessions. In serverless environments that would mean no state persisted - across requests. You may provied your own persistance layer by passing optional `db` store and `email` service for sending out tokens to verified accounts: - - ```ts - import { Identity } from 'w3-store' - - const identity = Identity.create({ - // base64url encoded Ed25519 keypair - id: process.env.W3_ID_KEYPAIR, - // persistent store - db: { - // reads from the underlying store - get(id: string): Promise<{ account: string; proof: CID } | null> { - //... - }, - // stores account info - set( - id: string, - value: { account: string; proof: CID } - ): Promise { - //... - }, - }, - email: { - send(to:string, ucanToken:string) Promise { - // ... - } - } - }) - ``` - - In production it is very likely that "upload" and "identity" services will run on different nodes. To create "ucanto connection" to an "identity" service running on different node you just need to provide service DID and URL it is listening on: - - ```ts - import { Identity } from 'w3-store' - - const identity = Identity.connect({ - // something like did:key:z6MkqJLaQH7VNbn4d8cNZiiABK2uzMCThzMWtgU7vyrFJRe1 - id: process.env.W3_ID_DID, - url: new URL('http://localhost:8080'), - }) - ``` - -Putting all the pieces together you can create a service as follows: - -```ts -import { Store, Identity, Accounting } from 'w3-store' -const service = Store.create({ - // "MgCZ+Sw7psm7xsVmvIqToSJSKcwNUextBonLkTaAycDlCVe0BoSdzzwY8vi0gpTGo7EjcTGqvWEjBOQGreE0TWpDPbWo=" - keypair: process.env.W3_STORE_KEYPAIR, - identity: Identity.create({ - // MgCYfUUr1JN+q9mX1JEp5hteX7v+Xe2LqRCFa4iPMIgVrf+0BLGRATq2sd8qCBXb6IvKw7mi+8oKZ20gCHKtjaPPzl20= - keypair: process.env.W3_ID_KEYPAIR, - }), - accounting: Accounting.create(), - signingOptions: { - accessKeyId: process.env.S3_ACCESS_KEY_ID, - secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, - bucket: process.env.S3_BUCKET, - region: process.env.AWS_REGION, - }, -}) -``` - -Finally you can expose this service over HTTP using node as follows: - -```ts -import HTTP from 'node:http' -const server = HTTP.createServer(async (request, response) => { - const chunks = [] - for await (const chunk of request) { - chunks.push(chunk) - } - - const { headers, body } = await provider.handleRequest({ - // @ts-ignore - node type is Record - headers: request.headers, - body: Buffer.concat(chunks), - }) - - response.writeHead(200, headers) - response.write(body) - response.end() -}) -server.listen() -``` diff --git a/packages/store/src/accounting/lib.js b/packages/store/src/accounting/lib.js deleted file mode 100644 index bb03f32de..000000000 --- a/packages/store/src/accounting/lib.js +++ /dev/null @@ -1,60 +0,0 @@ -import * as API from '../type.js' - -/** - * @typedef {{has(path:string): API.Await}} FileStore - * @typedef {Map} CARSet - * @typedef {{ - * set(id:API.DID, value:CARSet):API.Await - * get(id:API.DID): API.Await - * }} MetadataStore - * @param {{db?:MetadataStore, cars?:FileStore}} options - * @returns {API.Accounting.Provider} - */ -export const create = ({ db = new Map(), cars = new Map() } = {}) => ({ - /** - * @param {API.DID} group - * @param {API.Accounting.Link} link - * @param {API.UCANLink} proof - */ - async add(group, link, proof) { - const [members, have] = await Promise.all([ - db.get(group), - cars.has(`${link}/data`), - ]) - - if (members) { - members.set(`${link}`, link) - db.set(group, members) - } else { - db.set(group, new Map([[`${link}`, link]])) - } - - return have ? { status: 'in-s3' } : { status: 'not-in-s3' } - }, - /** - * @param {API.DID} group - * @param {API.Accounting.Link} link - * @param {API.UCANLink} proof - */ - async remove(group, link, proof) { - const members = await db.get(group) - if (members) { - members.set(`${link}`, null) - } - return null - }, - /** - * @param {API.DID} group - * @param {API.UCANLink} proof - */ - async list(group, proof) { - const members = await db.get(group) - const links = [] - for (const member of members?.values() || []) { - if (member) { - links.push(member) - } - } - return links - }, -}) diff --git a/packages/store/src/identity/capability.js b/packages/store/src/identity/capability.js deleted file mode 100644 index 69ec16468..000000000 --- a/packages/store/src/identity/capability.js +++ /dev/null @@ -1,85 +0,0 @@ -import * as Server from '@ucanto/server' -import { capability, URI, Failure } from '@ucanto/server' -import * as API from '../type.js' -import * as Store from '../store/capability.js' - -/** - * Checks that `with` on claimed capability is the same as `with` - * in delegated capability. Note this will ignore `can` field. - * - * @template {Server.ParsedCapability} T - * @template {Server.ParsedCapability} U - * @param {T} claimed - * @param {U} delegated - */ -const equalWith = (claimed, delegated) => - claimed.uri.href === delegated.uri.href || - new Failure( - `Can not derive ${claimed.can} with ${claimed.uri.href} from ${delegated.uri.href}` - ) - -/** - * @param {string} claimed - * @param {string} delegated - */ -const derivesURIPattern = (claimed, delegated) => { - if (delegated.endsWith('*')) { - return claimed.startsWith(delegated.slice(0, -1)) - ? true - : new Failure(`${claimed} does not match ${delegated}`) - } - - return claimed === delegated - ? true - : new Failure(`${claimed} is differnt from ${delegated}`) -} - -export const Validate = capability({ - can: 'identity/validate', - with: URI.match({ protocol: 'did:' }), - caveats: { - as: URI.string({ protocol: 'mailto:' }), - }, - derives: (claimed, delegated) => - derivesURIPattern(claimed.caveats.as, delegated.caveats.as) && - equalWith(claimed, delegated), -}) - -export const Register = capability({ - can: 'identity/register', - with: URI.match({ protocol: 'mailto:' }), - caveats: { - as: URI.string({ protocol: 'did:' }), - }, - derives: (claimed, delegated) => - derivesURIPattern(claimed.caveats.as, delegated.caveats.as) && - derivesURIPattern(claimed.with, delegated.with), -}) - -export const Link = capability({ - can: 'identity/link', - with: URI.match({ protocol: 'did:' }), - caveats: { - as: URI.string({ protocol: 'did:' }), - }, - derives: equalWith, -}) - -/** - * `identity/identify` can be derived from any of the `store/*` - * capability that has matichng `with`. This allows store service - * to identify account based on any user request. - */ -export const Identify = Store.Capability.derive({ - to: capability({ - can: 'identity/identify', - with: URI.match({ protocol: 'did:' }), - derives: equalWith, - }), - derives: equalWith, -}) - -/** - * Represents `identity/*` capability. - */ -export const Capability = Register.or(Link).or(Identify) diff --git a/packages/store/src/identity/lib.js b/packages/store/src/identity/lib.js deleted file mode 100644 index 6621d8a7c..000000000 --- a/packages/store/src/identity/lib.js +++ /dev/null @@ -1,80 +0,0 @@ -import * as Provider from './provider.js' -import * as API from '../type.js' -import { SigningPrincipal, Principal } from '@ucanto/principal' -import * as CAR from '@ucanto/transport/car' -import * as CBOR from '@ucanto/transport/cbor' -import * as HTTP from '@ucanto/transport/http' -import * as Service from '@ucanto/server' -import * as Client from '@ucanto/client' -import webfetch from '@web-std/fetch' -export * from './capability.js' - -/** - * @param {object} options - * @param {string} options.keypair - * @param {Provider.DB} [options.db] - * @param {Provider.Email} [options.email] - */ -export const create = ({ keypair, db = new Map(), email = mail }) => { - const id = SigningPrincipal.parse(keypair) - const provider = Provider.create({ - id, - db, - email, - }) - - const service = Service.create({ - id, - service: provider, - encoder: CBOR, - decoder: CAR, - }) - - return Object.assign(service, { - handleRequest: service.request.bind(service), - connect: () => - Client.connect({ - id: id.principal, - encoder: CAR, - decoder: CBOR, - channel: service, - }), - }) -} - -/** - * @param {object} options - * @param {API.DID} options.id - * @param {URL} options.url - * @param {string} [options.method] - * @param {HTTP.Fetcher} [options.fetch] - * @param {API.OutpboundTranpsortOptions} [options.transport] - * @returns {API.ConnectionView<{identity: API.Identity.Identity}>} - */ -export const connect = ({ - id, - url, - transport = { encoder: CAR, decoder: CBOR }, - // @ts-ignore - fetch = webfetch, - method, -}) => - Client.connect({ - id: Principal.parse(id), - ...transport, - channel: HTTP.open({ - url, - fetch, - method, - }), - }) - -/** @type {Provider.Email} */ -const mail = { - send(to, token) { - // eslint-disable-next-line no-console - console.log( - `Emailing registration token to mailto:${to}?subject=Verification&body=${token}\n` - ) - }, -} diff --git a/packages/store/src/identity/provider.js b/packages/store/src/identity/provider.js deleted file mode 100644 index 9505fadc0..000000000 --- a/packages/store/src/identity/provider.js +++ /dev/null @@ -1,187 +0,0 @@ -import * as Capability from './capability.js' -import * as Server from '@ucanto/server' -import { delegate } from '@ucanto/client' -import * as API from '../type.js' -import { Principal } from '@ucanto/server' - -/** - * @typedef {{account:API.DID, proof:Server.UCANLink}} AccountLink - * @typedef {{ - * set: (key:string, value:AccountLink) => API.Await - * get: (key:string) => API.Await - * }} DB - * @typedef {{ - * send: (to:string, token:string) => API.Await - * }} Email - * @typedef {{ - * id: API.SigningPrincipal - * db: DB - * email: Email - * }} Context - * - * @param {Context} context - * @returns {{identity: API.Identity.Identity}} - */ -export const create = ({ id, db, email }) => ({ - identity: { - validate: Server.provide(Capability.Validate, async ({ capability }) => { - const delegation = await delegate({ - issuer: id, - audience: Principal.parse(capability.with), - capabilities: [ - { - can: 'identity/register', - with: capability.caveats.as, - as: capability.uri.href, - }, - ], - }) - - await email.send( - capability.caveats.as.slice('mailto:'.length), - Server.UCAN.format(delegation.data) - ) - - return null - }), - register: Server.provide( - Capability.Register, - async ({ capability, invocation }) => { - const result = await associate( - db, - capability.caveats.as, - capability.uri.href, - invocation.cid, - true - ) - if (result) { - return null - } else { - throw new Error(`registering account should never fail`) - } - } - ), - link: Server.provide( - Capability.Link, - async ({ capability, invocation }) => { - const id = /** @type {API.Identity.ID} */ (capability.uri.href) - return (await associate( - db, - capability.caveats.as, - id, - invocation.cid, - false - )) - ? null - : new NotRegistered([invocation.issuer.did(), id]) - } - ), - identify: Server.provide(Capability.Identify, async ({ capability }) => { - const id = /** @type {API.Identity.ID} */ (capability.uri.href) - const account = await resolve(db, id) - return account || new NotRegistered([id]) - }), - }, -}) - -/** - * @param {DB} db - * @param {API.Identity.ID} from - * @param {API.Identity.ID} to - * @param {API.UCANLink} proof - * @param {boolean} create - * @returns {Promise} - */ -const associate = async (db, from, to, proof, create) => { - const [fromAccount, toAccount] = await Promise.all([ - resolve(db, from), - resolve(db, to), - ]) - - // So it could be that no did is linked with an account, one of the dids is - // linked with an account or both dids are linked with accounts. If no did - // is linked we just create a new account and link both did's with it. If - // one of the dids is linked with the account we link other with the same - // account if both are linked to a differnt accounts we create new joint - // account and link all them together. - if (!fromAccount && !toAccount) { - if (create) { - const account = /** @type {API.DID} */ (`did:ipld:${proof}`) - await Promise.all([ - db.set(to, { account, proof }), - db.set(from, { account, proof }), - ]) - } else { - return false - } - } else if (toAccount) { - await db.set(from, { account: toAccount, proof }) - } else if (fromAccount) { - await db.set(to, { account: fromAccount, proof }) - } else if (fromAccount !== toAccount) { - const account = /** @type {API.DID} */ (`did:ipld:${proof}`) - await Promise.all([ - db.set(toAccount, { account, proof }), - db.set(fromAccount, { account, proof }), - ]) - } - - return true -} - -/** - * Resolves memeber account. If member is not linked with any account returns - * `null` otherwise returns DID of the account which will have a - * `did:ipld:bafy...hash` form. - * - * @param {DB} db - * @param {API.Identity.ID} member - * @returns {Promise} - */ -const resolve = async (db, member) => { - let group = await db.get(member) - while (group) { - const parent = await db.get(group.account) - if (parent) { - group = parent - } else { - return group.account - } - } - return null -} - -/** - * @implements {API.Identity.NotRegistered} - */ -export class NotRegistered { - /** - * @param {[API.Identity.ID, ...API.Identity.ID[]]} ids - */ - constructor(ids) { - this.ids = ids - } - - get message() { - return this.ids.length > 1 - ? `No account is registered with such identifiers:\n - ${this.ids.join( - '\n - ' - )}` - : `No account is registered for ${this.ids[0]}` - } - - get error() { - return /** @type {true} */ (true) - } - - /** @type {"NotRegistered"} */ - get name() { - return 'NotRegistered' - } - - toJSON() { - const { name, message, ids, error } = this - - return { name, message, ids, error } - } -} diff --git a/packages/store/src/lib.js b/packages/store/src/lib.js deleted file mode 100644 index 4ca995f70..000000000 --- a/packages/store/src/lib.js +++ /dev/null @@ -1,6 +0,0 @@ -export * from './type.js' -export * as Identity from './identity/lib.js' -export * as Accounting from './accounting/lib.js' -export * as Store from './store/lib.js' -export * as Signer from './signer/lib.js' -export * as Type from './type.js' diff --git a/packages/store/src/signer/lib.js b/packages/store/src/signer/lib.js deleted file mode 100644 index 6eac79939..000000000 --- a/packages/store/src/signer/lib.js +++ /dev/null @@ -1,34 +0,0 @@ -import { base64pad } from 'multiformats/bases/base64' -import { SigV4 } from '@web3-storage/sigv4' -import * as API from './type.js' - -/** - * @param {API.Link} link - * @param {API.SignOptions} options - * @returns {{url:URL, headers:Record}} - */ -export const sign = (link, { bucket, expires = 1000, ...options }) => { - // sigv4 - const sig = new SigV4({ - accessKeyId: options.accessKeyId, - secretAccessKey: options.secretAccessKey, - region: options.region, - // @ts-ignore - sessionToken: options.sessionToken, - }) - - const checksum = base64pad.baseEncode(link.multihash.digest) - const url = sig.sign({ - key: `${link}/${link}.car`, - checksum, - bucket, - expires, - }) - - return { - url, - headers: { - 'x-amz-checksum-sha256': checksum, - }, - } -} diff --git a/packages/store/src/signer/type.js b/packages/store/src/signer/type.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/store/src/signer/type.ts b/packages/store/src/signer/type.ts deleted file mode 100644 index e8ff93626..000000000 --- a/packages/store/src/signer/type.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type { Link } from '@ucanto/interface' - -export interface SignOptions { - accessKeyId: string - secretAccessKey: string - sessionToken?: string - region: string - bucket: string - expires?: number -} diff --git a/packages/store/src/store/capability.js b/packages/store/src/store/capability.js deleted file mode 100644 index 1a9e1fb43..000000000 --- a/packages/store/src/store/capability.js +++ /dev/null @@ -1,61 +0,0 @@ -import * as Server from '@ucanto/server' -import { capability, Link, URI, Failure } from '@ucanto/server' -import * as API from '../type.js' - -/** - * @template {Server.ParsedCapability<"store/add"|"store/remove", Server.API.URI<'did:'>, {link?: API.Store.Link}>} T - * @param {T} claimed - * @param {T} delegated - * @returns - */ -const derives = (claimed, delegated) => { - if (claimed.uri.href !== delegated.uri.href) { - return new Failure( - `Expected 'with: "${delegated.uri.href}"' instead got '${claimed.uri.href}'` - ) - } else if ( - delegated.caveats.link && - `${delegated.caveats.link}` !== `${claimed.caveats.link}` - ) { - return new Failure( - `Link ${ - claimed.caveats.link == null ? '' : `${claimed.caveats.link} ` - }violates imposed ${delegated.caveats.link} constraint` - ) - } else { - return true - } -} - -export const Add = capability({ - can: 'store/add', - with: URI.match({ protocol: 'did:' }), - caveats: { - link: Link.optional(), - }, - derives, -}) - -export const Remove = capability({ - can: 'store/remove', - with: URI.match({ protocol: 'did:' }), - caveats: { - link: Link.optional(), - }, - derives, -}) - -export const List = capability({ - can: 'store/list', - with: URI.match({ protocol: 'did:' }), - derives: (claimed, delegated) => { - if (claimed.uri.href !== delegated.uri.href) { - return new Failure( - `Expected 'with: "${delegated.uri.href}"' instead got '${claimed.uri.href}'` - ) - } - return true - }, -}) - -export const Capability = Add.or(Remove).or(List) diff --git a/packages/store/src/store/lib.js b/packages/store/src/store/lib.js deleted file mode 100644 index 2aa6be6b8..000000000 --- a/packages/store/src/store/lib.js +++ /dev/null @@ -1,81 +0,0 @@ -import { Principal, SigningPrincipal } from '@ucanto/principal' -import * as Client from '@ucanto/client' -import * as Service from '@ucanto/server' -import * as CAR from '@ucanto/transport/car' -import * as CBOR from '@ucanto/transport/cbor' -import * as HTTP from '@ucanto/transport/http' -import webfetch from '@web-std/fetch' -import * as API from '../type.js' -import * as Provider from './provider.js' -export * from './capability.js' - -/** - * @param {object} options - * @param {string} options.keypair - * @param {API.ConnectionView<{identity: API.Identity.Identity}>} options.identity - * @param {API.Accounting.Provider} options.accounting - * @param {API.SignOptions} options.signingOptions - * @param {API.InboundTransportOptions} [options.transport] - * @param {API.ValidatorOptions} [options.validator] - */ -export const create = ({ - keypair, - identity, - accounting, - signingOptions, - transport = { decoder: CAR, encoder: CBOR }, - validator = {}, -}) => { - const id = SigningPrincipal.parse(keypair) - const provider = Provider.create({ - id, - identity, - accounting, - signingOptions, - }) - - const service = Service.create({ - ...transport, - ...validator, - id: id.principal, - service: provider, - }) - - return Object.assign(service, { - handleRequest: service.request.bind(service), - connect: () => { - Client.connect({ - id: id.principal, - encoder: CAR, - decoder: CBOR, - channel: service, - }) - }, - }) -} - -/** - * @param {object} options - * @param {API.DID} options.id - * @param {URL} options.url - * @param {string} [options.method] - * @param {HTTP.Fetcher} [options.fetch] - * @param {API.OutpboundTranpsortOptions} [options.transport] - * @returns {API.ConnectionView<{store: API.Store.Store, identity: API.Identity.Identity }>} - */ -export const connect = ({ - id, - url, - transport = { encoder: CAR, decoder: CBOR }, - fetch = webfetch, - method, -}) => - Client.connect({ - id: Principal.parse(id), - ...transport, - channel: HTTP.open({ - url, - fetch, - method, - }), - }) diff --git a/packages/store/src/store/provider.js b/packages/store/src/store/provider.js deleted file mode 100644 index 5e16777c4..000000000 --- a/packages/store/src/store/provider.js +++ /dev/null @@ -1,114 +0,0 @@ -import { provide, MalformedCapability, Failure } from '@ucanto/server' -import * as API from '../type.js' -import * as Identity from '../identity/capability.js' -import * as Capability from './capability.js' -import * as Signer from '../signer/lib.js' - -/** - * @param {API.Store.ServiceOptions} options - * @returns {{store: API.Store.Store, identity:API.Identity.Identity}} - */ -export const create = ({ id, identity, accounting, signingOptions }) => ({ - store: { - add: provide(Capability.Add, async ({ capability, invocation }) => { - const link = /** @type {API.Store.CARLink|undefined} */ ( - capability.caveats.link - ) - if (!link) { - return new MalformedCapability( - invocation.capabilities[0], - new Failure(`No link was provided in the capability`) - ) - } - - const group = /** @type {API.DID} */ (capability.with) - // First we need to check if we have an account associted with a DID - // car is been added to. - const account = await Identity.Identify.invoke({ - issuer: id, - audience: identity.id, - with: group, - proofs: [invocation], - }).execute(identity) - - // If we failed to resolve an account we deny access by returning n error. - if (account.error) { - return account - } - - const result = await accounting.add(group, link, invocation.cid) - if (result.error) { - return result - } - - if (result.status === 'not-in-s3') { - const { url, headers } = await Signer.sign(link, signingOptions) - return { - status: 'upload', - with: group, - link, - url: url.href, - headers, - } - } else { - return { - status: 'done', - with: group, - link, - } - } - }), - remove: provide(Capability.Remove, async ({ capability, invocation }) => { - const { link } = capability.caveats - if (!link) { - return new MalformedCapability( - invocation.capabilities[0], - new Failure(`No link was provided in the capability`) - ) - } - - const group = /** @type {API.DID} */ (capability.with) - await accounting.remove(group, link, invocation.cid) - return link - }), - list: provide(Capability.List, async ({ capability, invocation }) => { - const group = /** @type {API.DID} */ (capability.with) - return await accounting.list(group, invocation.cid) - }), - }, - // this is a boilerplate that redelegates all the `identity/*` capabilities - // to the `identity` service. - identity: { - validate: (invocation) => - Identity.Validate.invoke({ - issuer: id, - audience: identity.id, - with: invocation.capabilities[0].with, - caveats: { as: invocation.capabilities[0].as }, - proofs: [invocation], - }).execute(identity), - register: (invocation) => - Identity.Register.invoke({ - issuer: id, - audience: identity.id, - with: invocation.capabilities[0].with, - caveats: { as: invocation.capabilities[0].as }, - proofs: [invocation], - }).execute(identity), - link: (invocation) => - Identity.Link.invoke({ - issuer: id, - audience: identity.id, - with: invocation.capabilities[0].with, - caveats: { as: invocation.capabilities[0].as }, - proofs: [invocation], - }).execute(identity), - identify: (invocation) => - Identity.Identify.invoke({ - issuer: id, - audience: identity.id, - with: invocation.capabilities[0].with, - proofs: [invocation], - }).execute(identity), - }, -}) diff --git a/packages/store/src/type.js b/packages/store/src/type.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/store/src/type.ts b/packages/store/src/type.ts deleted file mode 100644 index 9ddd20975..000000000 --- a/packages/store/src/type.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from '@ucanto/interface' -export * as Identity from './type/identity.js' -export * as Accounting from './type/accounting.js' -export * as Store from './type/store.js' -export * as Error from './type/error.js' -export * from './signer/type.js' diff --git a/packages/store/src/type/accounting.ts b/packages/store/src/type/accounting.ts deleted file mode 100644 index 967e687c2..000000000 --- a/packages/store/src/type/accounting.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { DID, UCANLink, Result, Await } from '@ucanto/interface' -import * as API from '@ucanto/interface' -import { ServiceError } from './error.js' -export type Error = QuotaViolationError - -export interface QuotaViolationError - extends ServiceError<'QuotaViolationError', QuotaViolationError> {} - -export interface Link< - T extends unknown = unknown, - C extends number = number, - A extends number = number, - V extends 0 | 1 = 0 | 1 -> extends API.Link {} - -export interface Provider { - /** - * Upload service will call this once it verified the UCAN and checked that - * `group` is associated with some account. Provider will record link to - * group association for the future accounting. - * - * @param group - * @param link - * @param proof - */ - add: ( - group: DID, - link: Link, - proof: UCANLink - ) => Await> - - remove: ( - group: DID, - link: Link, - proof: UCANLink - ) => Await> - - list: (group: DID, proof: UCANLink) => Await> -} - -interface LinkState { - status: 'in-s3' | 'not-in-s3' -} diff --git a/packages/store/src/type/error.ts b/packages/store/src/type/error.ts deleted file mode 100644 index 8df00c4de..000000000 --- a/packages/store/src/type/error.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface ServiceError< - Name extends string, - JSON extends { name: Name; error: true; message: string } -> { - readonly name: Name - readonly message: string - readonly error: true - toJSON: () => JSONObject -} - -export type ToJSON = T extends undefined - ? never - : T extends number | null | string | boolean - ? T - : T extends { toJSON: () => infer U } - ? ToJSON - : T extends Array - ? Array> - : T extends (...args: any[]) => any - ? never - : T extends object - ? { [K in keyof T]: ToJSON } - : never - -export type JSONObject = { - [K in keyof T as ToJSON extends never ? never : K]: ToJSON -} diff --git a/packages/store/src/type/identity.ts b/packages/store/src/type/identity.ts deleted file mode 100644 index e25201a65..000000000 --- a/packages/store/src/type/identity.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { Capability, ServiceMethod, DID } from '@ucanto/server' -import type { ServiceError } from './error' - -export interface Identity { - /** - * Associates two UserIDs with one another. Order of UserIDs does not matter - * as semantically account is a set of UserIDs and this operation is join of - * sets that each UserID belongs to. If neither UserID belogs to an account - * (set) this MUST produce `NotRegistered`. If both UserIDs belong to two - * different accounts they get joined into a single joint account (set). - */ - link: ServiceMethod - /** - * This is equivalent of `link` operation, only difference is unlike `link` - * this MUST create a new account (set) if neither belong to a any account - * (set). - */ - register: ServiceMethod - - validate: ServiceMethod - /** - * Resolves account DID associated with a given DID. Returns either account - * did (which will have form of `did:ipld:bafy...hash`) or fails with - * `NotRegistered` error if no account exists for provided `UserID`. - * - * Please note that account did is not static and it will change when two - * accounts are joined into one. New account DID will correspond to proof CID - * provided in link/register request. - */ - identify: ServiceMethod -} - -export type MailtoID = `mailto:${string}` -export type ID = `did:${string}` | MailtoID - -export interface Register extends Capability<'identity/register', MailtoID> { - as: `did:${string}` -} - -export interface Validate extends Capability<'identity/validate', DID> { - as: MailtoID -} - -export interface Link extends Capability<'identity/link', DID> { - as: DID -} - -export interface Identify extends Capability<'identity/identify', DID> {} - -export interface NotRegistered - extends ServiceError<'NotRegistered', NotRegistered> { - ids: [ID, ...ID[]] -} - -export type Error = NotRegistered diff --git a/packages/store/src/type/sketch.ts b/packages/store/src/type/sketch.ts deleted file mode 100644 index a21199599..000000000 --- a/packages/store/src/type/sketch.ts +++ /dev/null @@ -1,150 +0,0 @@ -import type { - Ability, - Await, - Capability, - CapabilityMatch, - Caveats, - Invocation, - InvocationError, - Match, - ParsedCapability, - ServiceMethod, - TheCapabilityParser, - URI, -} from '@ucanto/interface' -import { ProviderContext } from '@ucanto/server' - -import * as Store from '../store/capability.js' - -interface Method< - I extends Capability = Capability, - O extends unknown = unknown, - X extends { error: true } = { error: true } -> extends ServiceMethod { - capability: I - can: I['can'] -} - -interface Route< - T extends ParsedCapability = ParsedCapability, - O extends unknown = unknown, - CTX extends {} = {} -> { - capability: TheCapabilityParser> - handler: CapabilityHandler -} - -type CapabilityHandler< - T extends ParsedCapability, - O extends unknown = unknown, - CTX extends {} = {} -> = ( - input: { - capability: T - invocation: Invocation & T['caveats']> - }, - context: CTX -) => Await - -interface Service { - routes: T - - provide: ( - capability: TheCapabilityParser>, - handler: CapabilityHandler - ) => Service }> - - invoke: >( - capability: C, - context: ServiceContext - ) => ReturnType - - capability: ServiceCapability - context: ServiceContext -} - -type ServiceCapability = { - [Can in keyof T]: T[Can] extends Route - ? Capability & C['caveats'] - : never -}[keyof T] - -type ServiceContext = UnionToIntersection< - { - [Can in keyof T]: T[Can] extends Route ? CTX : never - }[keyof T] -> - -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I -) => void - ? I - : never - -interface Server { - internal: T - - provide: < - A extends Ability, - C extends Caveats, - R extends URI, - U extends unknown - >( - capability: TheCapabilityParser>, - handler: (input: ProviderContext) => Await - ) => Server< - T & { - [key in A]: Method< - Capability, - Exclude, - | (Exclude> & { error: true }) - | InvocationError - > - } - > - - capability: InferCapability - - invoke: >( - capbility: C - ) => ReturnType -} - -type InferCapability = { - [Key in keyof T]: T[Key] extends Method ? C : never -}[keyof T] - -type MatchMetchod = { - [Key in keyof T]: T[Key] extends Method ? T[Key] : never -}[keyof T] - -declare function service(): Service<{}> - -const s = service() - .provide( - Store.Add, - ({ capability, invocation }, context: { secret: Uint8Array }) => { - return { - status: 'done', - with: capability.with, - link: capability.caveats.link, - } - } - ) - .provide( - Store.Remove, - ({ capability, invocation }, context: { name: string }) => { - return null - } - ) - -const ctx = { ...s.context } -const routes = { ...s.routes } - -const m = s.invoke( - { - can: 'store/add', - with: 'did:key:zAlice', - }, - { secret: new Uint8Array(), name: 'me' } -) diff --git a/packages/store/src/type/store.ts b/packages/store/src/type/store.ts deleted file mode 100644 index ba41ebf35..000000000 --- a/packages/store/src/type/store.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { - ConnectionView, - InvocationError, - MalformedCapability, - SigningPrincipal, -} from '@ucanto/interface' -import * as API from '@ucanto/interface' -import type { Capability, DID, ServiceMethod } from '@ucanto/server' -import * as Server from '@ucanto/server' -import * as CAR from '@ucanto/transport/car' -import * as Signer from '../signer/type.js' -import * as Accounting from './accounting.js' -import * as Identity from './identity.js' - -export interface Link< - T extends unknown = unknown, - C extends number = number, - A extends number = number, - V extends 0 | 1 = 0 | 1 -> extends API.Link {} - -export interface StoreService { - start: (options: ServiceOptions) => Server.ServerView -} -export interface ServiceOptions { - id: SigningPrincipal - identity: ConnectionView<{ identity: Identity.Identity }> - - accounting: Accounting.Provider - - signingOptions: Signer.SignOptions -} - -export interface Options { - transport: Server.InboundTransportOptions - validation?: Server.ValidatorOptions - context: ServiceOptions -} - -export interface Store { - add: ServiceMethod< - Add, - AddState, - | Identity.NotRegistered - | Accounting.Error - | MalformedCapability - // It may fail to reach identity service - | InvocationError - > - remove: ServiceMethod - - list: ServiceMethod -} - -export type AddState = AddDone | UploadRequired - -export interface AddDone { - status: 'done' - with: DID - link: Link -} - -export interface UploadRequired { - status: 'upload' - with: DID - link: Link - url: string - headers: Record -} - -export type CARLink = Link - -export interface Add extends Capability<'store/add', DID> { - link?: Link -} - -export interface Remove extends Capability<'store/remove', DID> { - link?: Link -} - -export interface List extends Capability<'store/list', DID> {} - -export type Action = Add | Remove | List diff --git a/packages/store/test/fixtures.js b/packages/store/test/fixtures.js deleted file mode 100644 index 197b8d98a..000000000 --- a/packages/store/test/fixtures.js +++ /dev/null @@ -1,18 +0,0 @@ -import { SigningPrincipal } from '@ucanto/principal' - -/** did:key:z6Mkqa4oY9Z5Pf5tUcjLHLUsDjKwMC95HGXdE1j22jkbhz6r */ -export const alice = SigningPrincipal.parse( - 'MgCZT5vOnYZoVAeyjnzuJIVY9J4LNtJ+f8Js0cTPuKUpFne0BVEDJjEu6quFIU8yp91/TY/+MYK8GvlKoTDnqOCovCVM=' -) -/** did:key:z6MkffDZCkCTWreg8868fG1FGFogcJj5X6PY93pPcWDn9bob */ -export const bob = SigningPrincipal.parse( - 'MgCYbj5AJfVvdrjkjNCxB3iAUwx7RQHVQ7H1sKyHy46Iose0BEevXgL1V73PD9snOCIoONgb+yQ9sycYchQC8kygR4qY=' -) -/** did:key:z6MktafZTREjJkvV5mfJxcLpNBoVPwDLhTuMg9ng7dY4zMAL */ -export const mallory = SigningPrincipal.parse( - 'MgCYtH0AvYxiQwBG6+ZXcwlXywq9tI50G2mCAUJbwrrahkO0B0elFYkl3Ulf3Q3A/EvcVY0utb4etiSE8e6pi4H0FEmU=' -) - -export const service = SigningPrincipal.parse( - 'MgCYKXoHVy7Vk4/QjcEGi+MCqjntUiasxXJ8uJKY0qh11e+0Bs8WsdqGK7xothgrDzzWD0ME7ynPjz2okXDh8537lId8=' -) diff --git a/packages/store/test/lib.spec.js b/packages/store/test/lib.spec.js deleted file mode 100644 index a25800100..000000000 --- a/packages/store/test/lib.spec.js +++ /dev/null @@ -1,240 +0,0 @@ -import { test, assert } from './test.js' -import * as Client from '@ucanto/client' -import * as CAR from '@ucanto/transport/car' -import * as CBOR from '@ucanto/transport/cbor' -import { SigningPrincipal } from '@ucanto/principal' -import { Store, Identity, Accounting } from '../src/lib.js' -import { alice, bob, service as validator } from './fixtures.js' -import HTTP from 'node:http' - -test('main', async () => { - const s3 = new Map() - const w3 = await SigningPrincipal.generate() - - // start w3-identity service - const identityService = Identity.create({ - keypair: SigningPrincipal.format(w3), - }) - const identityServer = await listen(identityService) - - // start w3-store service - const storeService = Store.create({ - keypair: SigningPrincipal.format(await SigningPrincipal.generate()), - identity: Identity.connect({ - id: w3.did(), - url: identityServer.url, - }), - accounting: Accounting.create({ cars: s3 }), - signingOptions: { - accessKeyId: 'id', - secretAccessKey: 'secret', - region: 'us-east-2', - bucket: 'my-test-bucket', - }, - }) - - const storeServer = await listen(storeService) - - try { - // This is something that client like CLI will do - const store = Store.connect({ - id: storeService.id.did(), - url: storeServer.url, - }) - - const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world' })], - }) - - // errors if not registered - { - const result = await Store.Add.invoke({ - issuer: alice, - audience: store.id, - with: alice.did(), - caveats: { link: car.cid }, - }).execute(store) - - assert.containSubset(result, { - error: true, - name: 'NotRegistered', - message: `No account is registered for ${alice.did()}`, - }) - } - - // can not register without a proof - { - // service delegates to the validator - const validatorToken = await Client.delegate({ - issuer: w3, - audience: validator, - capabilities: [ - { - can: 'identity/register', - with: 'mailto:*', - as: 'did:*', - }, - ], - }) - - // validator after validation delegates to alice - const registrationToken = await Client.delegate({ - issuer: validator, - audience: alice, - capabilities: [ - { - can: 'identity/register', - with: 'mailto:alice@web.mail', - as: alice.did(), - }, - ], - proofs: [validatorToken], - }) - - const result = await Identity.Register.invoke({ - issuer: alice, - audience: store.id, - with: 'mailto:alice@web.mail', - caveats: { - as: alice.did(), - }, - proofs: [registrationToken], - }).execute(store) - - assert.deepEqual(result, null) - } - - // alice should be able to check her identity - { - const result = await Identity.Identify.invoke({ - issuer: alice, - audience: store.id, - with: alice.did(), - }).execute(store) - - assert.match(String(result), /did:ipld:bafy/) - } - - // now that alice is registered she can add a car file - { - const result = await Store.Add.invoke({ - issuer: alice, - audience: store.id, - with: alice.did(), - caveats: { link: car.cid }, - }).execute(store) - - assert.containSubset(result, { - status: 'upload', - with: alice.did(), - link: car.cid, - }) - - // eslint-disable-next-line unicorn/new-for-builtins - assert.match(Object(result).url, /https:.*s3.*amazon/) - } - - // if alice adds a car that is already in s3 no upload will be needed - { - const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ another: 'car' })], - }) - - // add car to S3 - s3.set(`${car.cid}/data`, true) - - const result = await Store.Add.invoke({ - issuer: alice, - audience: store.id, - with: alice.did(), - caveats: { link: car.cid }, - }).execute(store) - - assert.containSubset(result, { - status: 'done', - with: alice.did(), - link: car.cid, - url: undefined, - }) - } - - // bob can not store/add into alice's group - { - const result = await Store.Add.invoke({ - issuer: bob, - audience: store.id, - with: alice.did(), - caveats: { - link: car.cid, - }, - }).execute(store) - - assert.containSubset(result, { - error: true, - name: 'Unauthorized', - }) - } - - // but if alice delegates capability to bob we can add to alice's group - { - const result = await Store.Add.invoke({ - issuer: bob, - audience: store.id, - with: alice.did(), - caveats: { link: car.cid }, - proofs: [ - await Client.delegate({ - issuer: alice, - audience: bob, - capabilities: [ - { - can: 'store/add', - with: alice.did(), - }, - ], - }), - ], - }).execute(store) - - assert.containSubset(result, { - with: alice.did(), - link: car.cid, - }) - } - } finally { - storeServer.close() - identityServer.close() - } -}) - -/** - * @typedef {{headers:Record, body:Uint8Array}} Payload - * @param {{handleRequest(request:Payload):Client.Await}} service - */ - -const listen = async (service) => { - const server = HTTP.createServer(async (request, response) => { - const chunks = [] - for await (const chunk of request) { - chunks.push(chunk) - } - - const { headers, body } = await service.handleRequest({ - // @ts-ignore - node type is Record - headers: request.headers, - body: Buffer.concat(chunks), - }) - - response.writeHead(200, headers) - response.write(body) - response.end() - }) - await new Promise((resolve) => server.listen(resolve)) - - { - // @ts-ignore - this is actually what it returns on http - const port = server.address().port - - return Object.assign(server, { url: new URL(`http://localhost:${port}`) }) - } -} diff --git a/packages/store/test/signer.test.js b/packages/store/test/signer.test.js deleted file mode 100644 index 09a7fe192..000000000 --- a/packages/store/test/signer.test.js +++ /dev/null @@ -1,33 +0,0 @@ -import { assert, test } from './test.js' -import { Signer } from '../src/lib.js' -import { parseLink } from '@ucanto/core' -import { base64pad } from 'multiformats/bases/base64' - -test('should signer', () => { - const link = parseLink( - 'bafybeiczcw2d5ej66e6kh4ae4a7extcuiukdngukf65pf5fphndd4likvy' - ) - - const { url, headers } = Signer.sign(link, { - accessKeyId: 'id', - secretAccessKey: 'secret', - bucket: 'my-bucket', - region: 'eu-central-1', - }) - - const params = url.searchParams - - assert.equal(url instanceof URL, true) - assert.equal(url.host, 'my-bucket.s3.eu-central-1.amazonaws.com') - assert.equal(url.pathname, `/${link}/${link}.car`) - assert.equal(params.get('X-Amz-SignedHeaders'), 'host;x-amz-checksum-sha256') - assert.equal(params.get('X-Amz-Expires'), '1000') - assert.equal(params.get('X-Amz-Algorithm'), 'AWS4-HMAC-SHA256') - assert.match(params.get('X-Amz-Credential') || '', /aws4_request/) - assert.equal((params.get('X-Amz-Signature') || '').length > 2, true) - - assert.equal( - headers['x-amz-checksum-sha256'], - base64pad.baseEncode(link.multihash.digest) - ) -}) diff --git a/packages/store/test/test.js b/packages/store/test/test.js deleted file mode 100644 index 54dc0ff24..000000000 --- a/packages/store/test/test.js +++ /dev/null @@ -1,7 +0,0 @@ -import { use } from 'chai' -import subset from 'chai-subset' -use(subset) - -export const test = it - -export { assert } from 'chai' diff --git a/packages/store/tsconfig.json b/packages/store/tsconfig.json deleted file mode 100644 index ecfc111e3..000000000 --- a/packages/store/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "lib": ["ESNext"], - "noUnusedLocals": false - }, - "include": ["src", "scripts", "test", "package.json"], - "exclude": ["**/node_modules/**"] -}