@wovin/connect-nftstorage 0.1.35 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-32YGF2Q4.js +2 -0
- package/dist/chunk-32YGF2Q4.js.map +1 -0
- package/dist/chunk-LQCSTNWI.js +2 -0
- package/dist/chunk-LQCSTNWI.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/retrieve.js +2 -0
- package/dist/store.js +2 -0
- package/package.json +12 -11
- package/src/index.ts +72 -0
- package/src/retrieve.ts +18 -0
- package/src/store.ts +89 -0
- package/dist/chunk-AQFODBKK.min.js +0 -82
- package/dist/chunk-AQFODBKK.min.js.map +0 -1
- package/dist/chunk-BKKH6BET.min.js +0 -3
- package/dist/chunk-BKKH6BET.min.js.map +0 -1
- package/dist/chunk-PRWLUGGV.min.js +0 -16
- package/dist/chunk-PRWLUGGV.min.js.map +0 -1
- package/dist/index.min.js +0 -2
- package/dist/index.min.js.map +0 -1
- package/dist/retrieve.min.js +0 -2
- package/dist/store.min.js +0 -2
- /package/dist/{retrieve.min.js.map → retrieve.js.map} +0 -0
- /package/dist/{store.min.js.map → store.js.map} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{retrieveThread as i}from"@wovin/connect-gateway";import{Logger as r}from"besonders-logger";var{WARN:s,LOG:g,DEBUG:d,VERBOSE:S,ERROR:l}=r.setup(r.INFO),o="https://nftstorage.link";async function m(t,{readOnly:e=!0,pinnedCID:n}={}){return i(t,{readOnly:e,pinnedCID:n,gateways:[{url:o}]})}export{o as a,m as b};
|
|
2
|
+
//# sourceMappingURL=chunk-32YGF2Q4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/retrieve.ts"],"sourcesContent":["import { retrieveThread as retrieveThreadGW } from '@wovin/connect-gateway'\nimport { CidString, IpnsString } from '@wovin/core/applog'\nimport { Logger } from 'besonders-logger'\n\nconst { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars\n\nexport const NFTSTORAGE_GATEWAY = `https://nftstorage.link`\n\nexport async function retrieveThread(\n\tpubID: IpnsString | CidString | null,\n\t{ readOnly = true, pinnedCID }: { readOnly?: boolean; pinnedCID?: CidString } = {},\n) {\n\treturn retrieveThreadGW(pubID, {\n\t\treadOnly,\n\t\tpinnedCID,\n\t\tgateways: [{ url: NFTSTORAGE_GATEWAY }],\n\t})\n}\n"],"mappings":"AAAA,OAAS,kBAAkBA,MAAwB,yBAEnD,OAAS,UAAAC,MAAc,mBAEvB,GAAM,CAAE,KAAAC,EAAM,IAAAC,EAAK,MAAAC,EAAO,QAAAC,EAAS,MAAAC,CAAM,EAAIL,EAAO,MAAMA,EAAO,IAAI,EAExDM,EAAqB,0BAElC,eAAsBC,EACrBC,EACA,CAAE,SAAAC,EAAW,GAAM,UAAAC,CAAU,EAAmD,CAAC,EAChF,CACD,OAAOX,EAAiBS,EAAO,CAC9B,SAAAC,EACA,UAAAC,EACA,SAAU,CAAC,CAAE,IAAKJ,CAAmB,CAAC,CACvC,CAAC,CACF","names":["retrieveThreadGW","Logger","WARN","LOG","DEBUG","VERBOSE","ERROR","NFTSTORAGE_GATEWAY","retrieveThread","pubID","readOnly","pinnedCID"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as o from"@ucans/ucans";import{Logger as n}from"besonders-logger";import{CID as i}from"multiformats";var{WARN:d,LOG:p,DEBUG:s,VERBOSE:f,ERROR:g}=n.setup(n.INFO);async function l(e,t){let r=typeof t!="string",a=await(await fetch("https://api.nft.storage/upload",{method:"POST",headers:{Authorization:`Bearer ${r?o.encode(t):t}`,"Content-Type":"application/car","x-agent-did":r?t.payload.iss:void 0},body:e})).json();if(s("[storeCar] result",{result:a,isUcan:r}),!a.ok)throw new Error("Failed to upload"+JSON.stringify(a.error));return i.parse(a.value.cid)}async function h(e,t,r){s("[createRequestToken] ",{keypair:e,ucanToken:r});let a=r.payload;return o.build({issuer:e,audience:t,capabilities:a.att,proofs:[o.encode(r)]})}async function y(e,t){s("Registering",{did:t});let r=await(await fetch("https://api.nft.storage/user/did",{method:"POST",headers:{Authorization:`Bearer ${e}`},body:JSON.stringify({did:t})})).json();if(s("Register DID result",r),!r.ok)throw new Error("Failed to register DID "+JSON.stringify(r.error))}async function w(e){s("Get root UCAN",{apiKey:e});let t=await(await fetch("https://api.nft.storage/ucan/token",{method:"POST",headers:{Authorization:`Bearer ${e}`}})).json();if(s("Get root UCAN result",t),!t.ok)throw new Error("Failed get root UCAN "+JSON.stringify(t.error));return o.validate(t.value)}export{l as a,h as b,y as c,w as d};
|
|
2
|
+
//# sourceMappingURL=chunk-LQCSTNWI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/store.ts"],"sourcesContent":["import * as ucans from '@ucans/ucans'\nimport { Logger } from 'besonders-logger'\nimport { CID } from 'multiformats'\n\nconst { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars\n\nexport async function storeCar(car: Blob, token: ucans.Ucan | string): Promise<CID> {\n\t// const ucanInfo = await ucans.validate(ucanToken)\n\tconst isUcan = typeof token !== 'string'\n\n\t// Docs: https://nft.storage/api-docs/#operations-NFT_Storage-upload\n\tconst result = await (await fetch('https://api.nft.storage/upload', {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Authorization': `Bearer ${isUcan ? ucans.encode(token) : token}`,\n\t\t\t'Content-Type': `application/car`,\n\t\t\t'x-agent-did': isUcan ? token.payload.iss : undefined,\n\t\t},\n\t\tbody: car,\n\t})).json()\n\tDEBUG('[storeCar] result', { result, isUcan })\n\tif (!result.ok) throw new Error(`Failed to upload` + JSON.stringify(result.error))\n\treturn CID.parse(result.value.cid)\n}\n\nexport async function createRequestToken(\n\tkeypair: ucans.DidableKey,\n\tserviceDID: string,\n\tucanToken: ucans.Ucan,\n) {\n\tDEBUG(`[createRequestToken] `, { keypair, ucanToken })\n\t// we want to include the capabilities of the parent token in our request token\n\t// so we validate the parent token to extract the payload and copy over the capabilities\n\t// // the `att` field contains the capabilities we need for uploading\n\tconst payload = ucanToken.payload\n\treturn ucans.build({\n\t\tissuer: keypair,\n\t\taudience: serviceDID,\n\t\tcapabilities: (payload as any).att, // .map(att => ucans.capability.parse(att)),\n\t\tproofs: [ucans.encode(ucanToken)],\n\t})\n}\nexport async function registerDid(apiKey: string, did: string) {\n\tDEBUG(`Registering`, { did })\n\tconst result = await (await fetch('https://api.nft.storage/user/did', {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Authorization': `Bearer ${apiKey}`,\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tdid,\n\t\t}),\n\t})).json()\n\tDEBUG(`Register DID result`, result)\n\tif (!result.ok) throw new Error(`Failed to register DID ` + JSON.stringify(result.error))\n}\nexport async function getRootUcan(apiKey: string) {\n\tDEBUG(`Get root UCAN`, { apiKey })\n\tconst result = await (await fetch('https://api.nft.storage/ucan/token', {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Authorization': `Bearer ${apiKey}`,\n\t\t},\n\t})).json()\n\tDEBUG(`Get root UCAN result`, result)\n\tif (!result.ok) throw new Error(`Failed get root UCAN ` + JSON.stringify(result.error))\n\treturn ucans.validate(result.value)\n}\n\n// import { build } from 'ucan-storage/ucan-storage'\n\n// // The DID for the storage service. In real code, you should obtain this from the service you're targetting.\n// const serviceDID = 'did:key:a-fake-service-did'\n\n// async function createRequestToken(parentUCAN, issuerKeyPair) {\n// \t// we want to include the capabilities of the parent token in our request token\n// \t// so we validate the parent token to extract the payload and copy over the capabilities\n// \tconst { payload } = await validate(parentUCAN)\n\n// \t// the `att` field contains the capabilities we need for uploading\n// \tconst { att } = payload\n\n// \treturn build({\n// \t\tissuer: issuerKeyPair,\n// \t\taudience: serviceDID,\n// \t\tcapabilities: att,\n// \t\tproofs: [parentUcan],\n// \t})\n// }\n"],"mappings":"AAAA,UAAYA,MAAW,eACvB,OAAS,UAAAC,MAAc,mBACvB,OAAS,OAAAC,MAAW,eAEpB,GAAM,CAAE,KAAAC,EAAM,IAAAC,EAAK,MAAAC,EAAO,QAAAC,EAAS,MAAAC,CAAM,EAAIN,EAAO,MAAMA,EAAO,IAAI,EAErE,eAAsBO,EAASC,EAAWC,EAA0C,CAEnF,IAAMC,EAAS,OAAOD,GAAU,SAG1BE,EAAS,MAAO,MAAM,MAAM,iCAAkC,CACnE,OAAQ,OACR,QAAS,CACR,cAAiB,UAAUD,EAAe,SAAOD,CAAK,EAAIA,CAAK,GAC/D,eAAgB,kBAChB,cAAeC,EAASD,EAAM,QAAQ,IAAM,MAC7C,EACA,KAAMD,CACP,CAAC,GAAG,KAAK,EAET,GADAJ,EAAM,oBAAqB,CAAE,OAAAO,EAAQ,OAAAD,CAAO,CAAC,EACzC,CAACC,EAAO,GAAI,MAAM,IAAI,MAAM,mBAAqB,KAAK,UAAUA,EAAO,KAAK,CAAC,EACjF,OAAOV,EAAI,MAAMU,EAAO,MAAM,GAAG,CAClC,CAEA,eAAsBC,EACrBC,EACAC,EACAC,EACC,CACDX,EAAM,wBAAyB,CAAE,QAAAS,EAAS,UAAAE,CAAU,CAAC,EAIrD,IAAMC,EAAUD,EAAU,QAC1B,OAAa,QAAM,CAClB,OAAQF,EACR,SAAUC,EACV,aAAeE,EAAgB,IAC/B,OAAQ,CAAO,SAAOD,CAAS,CAAC,CACjC,CAAC,CACF,CACA,eAAsBE,EAAYC,EAAgBC,EAAa,CAC9Df,EAAM,cAAe,CAAE,IAAAe,CAAI,CAAC,EAC5B,IAAMR,EAAS,MAAO,MAAM,MAAM,mCAAoC,CACrE,OAAQ,OACR,QAAS,CACR,cAAiB,UAAUO,CAAM,EAClC,EACA,KAAM,KAAK,UAAU,CACpB,IAAAC,CACD,CAAC,CACF,CAAC,GAAG,KAAK,EAET,GADAf,EAAM,sBAAuBO,CAAM,EAC/B,CAACA,EAAO,GAAI,MAAM,IAAI,MAAM,0BAA4B,KAAK,UAAUA,EAAO,KAAK,CAAC,CACzF,CACA,eAAsBS,EAAYF,EAAgB,CACjDd,EAAM,gBAAiB,CAAE,OAAAc,CAAO,CAAC,EACjC,IAAMP,EAAS,MAAO,MAAM,MAAM,qCAAsC,CACvE,OAAQ,OACR,QAAS,CACR,cAAiB,UAAUO,CAAM,EAClC,CACD,CAAC,GAAG,KAAK,EAET,GADAd,EAAM,uBAAwBO,CAAM,EAChC,CAACA,EAAO,GAAI,MAAM,IAAI,MAAM,wBAA0B,KAAK,UAAUA,EAAO,KAAK,CAAC,EACtF,OAAa,WAASA,EAAO,KAAK,CACnC","names":["ucans","Logger","CID","WARN","LOG","DEBUG","VERBOSE","ERROR","storeCar","car","token","isUcan","result","createRequestToken","keypair","serviceDID","ucanToken","payload","registerDid","apiKey","did","getRootUcan"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ucans from '@ucans/ucans';
|
|
2
2
|
import { type StorageConnector } from '@wovin/core/pubsub';
|
|
3
3
|
import { CID } from 'multiformats';
|
|
4
|
-
export * from './retrieve';
|
|
4
|
+
export * from './retrieve.ts';
|
|
5
5
|
export declare class NftStorageConnector implements StorageConnector {
|
|
6
6
|
serviceDID: string | null;
|
|
7
7
|
private apiKey;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAMlC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAMlC,cAAc,eAAe,CAAA;AAG7B,qBAAa,mBAAoB,YAAW,gBAAgB;IAanD,UAAU,EAAE,MAAM,GAAG,IAAI;IAChC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;WAfJ,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;QAChD,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,KAAK,CAAC,UAAU,CAAA;QAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,IAAI,CAAA;KACrB;gBAQO,UAAU,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,OAAO,EAAE,KAAK,CAAC,UAAU,GAAG,IAAI,EAChC,QAAQ,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI;IAKpC,IAAI,OAAO,YAEV;IACD,IAAI,OAAO,YAEV;IAEK,QAAQ,CAAC,GAAG,EAAE,IAAI;IA2BxB,UAAU;;;CAGV"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as o,b as f}from"./chunk-32YGF2Q4.js";import{a as i,b as s,c as n,d as c}from"./chunk-LQCSTNWI.js";import{Logger as p}from"besonders-logger";var{WARN:l,LOG:v,DEBUG:m,VERBOSE:I,ERROR:y}=p.setup(p.INFO),u=class D{constructor(r,t,e,a){this.serviceDID=r;this.apiKey=t;this.keypair=e;this.rootUcan=a;if(!(t||e&&a))throw new Error("Needs apiKey - or keypair and rootUcan")}static async init({apiKey:r,keypair:t,rootUcan:e}){return m("Init NFT.Storage connector:",{apiKey:r,keypair:t,serviceDID:null}),new D(null,r,t,e)}get isSetup(){return!0}get isValid(){return!0}async storeCar(r){if(this.apiKey&&!this.keypair)return i(r,this.apiKey);if(!this.serviceDID){let{value:t}=await(await fetch("https://api.nft.storage/did")).json();this.serviceDID=t}for(let t=1;t<3;t++)try{this.rootUcan||(await n(this.apiKey,this.keypair.did()),this.rootUcan=await c(this.apiKey));let e=await s(this.keypair,this.serviceDID,this.rootUcan);return i(r,e)}catch(e){if(y("[nft-connector.storeCar] error:",e),e.message?.contains?.("ERROR_TOKEN_NOT_FOUND"))l(`Token not found (might be a race condition) (attempt=${t}):`,e);else throw e}}getGateway(){return{url:o}}};export{o as NFTSTORAGE_GATEWAY,u as NftStorageConnector,f as retrieveThread};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import * as ucans from '@ucans/ucans'\nimport type { IpfsGateway } from '@wovin/connect-gateway'\nimport { type StorageConnector } from '@wovin/core/pubsub'\nimport { Logger } from 'besonders-logger'\nimport { CID } from 'multiformats'\nimport { NFTSTORAGE_GATEWAY } from './retrieve.ts'\nimport { createRequestToken, getRootUcan, registerDid, storeCar } from './store.ts'\n\nconst { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars\n\nexport * from './retrieve.ts'\n// export * from './store.ts'\n\nexport class NftStorageConnector implements StorageConnector {\n\tstatic async init({ apiKey, keypair, rootUcan }: {\n\t\tapiKey?: string\n\t\tkeypair?: ucans.DidableKey\n\t\trootUcan?: ucans.Ucan\n\t}) {\n\t\t// const { value: serviceDID } = await (await fetch('https://api.nft.storage/did')).json() - Unnecessary delay on init (& might fail when no internet)\n\t\tconst serviceDID = null\n\t\t// if (!keypair) keypair = await ucans.EdKeypair.create()\n\t\tDEBUG(`Init NFT.Storage connector:`, { apiKey, keypair, serviceDID })\n\t\treturn new NftStorageConnector(serviceDID, apiKey, keypair, rootUcan)\n\t}\n\tconstructor(\n\t\tpublic serviceDID: string | null,\n\t\tprivate apiKey: string | null,\n\t\tprivate keypair: ucans.DidableKey | null,\n\t\tprivate rootUcan: ucans.Ucan | null,\n\t) {\n\t\tif (!(apiKey || (keypair && rootUcan))) throw new Error(`Needs apiKey - or keypair and rootUcan`)\n\t}\n\n\tget isSetup() {\n\t\treturn true\n\t}\n\tget isValid() {\n\t\treturn true // TODO test ucan etc\n\t}\n\n\tasync storeCar(car: Blob) {\n\t\tif (this.apiKey && !this.keypair) {\n\t\t\treturn storeCar(car, this.apiKey)\n\t\t}\n\t\tif (!this.serviceDID) {\n\t\t\tconst { value: serviceDID } = await (await fetch('https://api.nft.storage/did')).json()\n\t\t\tthis.serviceDID = serviceDID\n\t\t}\n\t\t// NFT.storage allows only one registered DID, so we might have another agent register between our register & push\n\t\tfor (let attempt = 1; attempt < 3; attempt++) {\n\t\t\ttry {\n\t\t\t\tif (!this.rootUcan) {\n\t\t\t\t\tawait registerDid(this.apiKey, this.keypair.did())\n\t\t\t\t\tthis.rootUcan = await getRootUcan(this.apiKey)\n\t\t\t\t\t// TODO: callback to the app - for persisting?\n\t\t\t\t}\n\t\t\t\tconst requestUcan = await createRequestToken(this.keypair, this.serviceDID, this.rootUcan)\n\t\t\t\treturn storeCar(car, requestUcan)\n\t\t\t} catch (err) {\n\t\t\t\tERROR(`[nft-connector.storeCar] error:`, err)\n\t\t\t\tif ((err as any).message?.contains?.('ERROR_TOKEN_NOT_FOUND')) {\n\t\t\t\t\tWARN(`Token not found (might be a race condition) (attempt=${attempt}):`, err)\n\t\t\t\t} else throw err\n\t\t\t}\n\t\t}\n\t}\n\n\tgetGateway() {\n\t\treturn { url: NFTSTORAGE_GATEWAY } satisfies IpfsGateway\n\t}\n}\n"],"mappings":"4GAGA,OAAS,UAAAA,MAAc,mBAKvB,GAAM,CAAE,KAAAC,EAAM,IAAAC,EAAK,MAAAC,EAAO,QAAAC,EAAS,MAAAC,CAAM,EAAIC,EAAO,MAAMA,EAAO,IAAI,EAKxDC,EAAN,MAAMC,CAAgD,CAY5D,YACQC,EACCC,EACAC,EACAC,EACP,CAJM,gBAAAH,EACC,YAAAC,EACA,aAAAC,EACA,cAAAC,EAER,GAAI,EAAEF,GAAWC,GAAWC,GAAY,MAAM,IAAI,MAAM,wCAAwC,CACjG,CAlBA,aAAa,KAAK,CAAE,OAAAF,EAAQ,QAAAC,EAAS,SAAAC,CAAS,EAI3C,CAIF,OAAAT,EAAM,8BAA+B,CAAE,OAAAO,EAAQ,QAAAC,EAAS,eAAW,CAAC,EAC7D,IAAIH,EAAoB,KAAYE,EAAQC,EAASC,CAAQ,CACrE,CAUA,IAAI,SAAU,CACb,MAAO,EACR,CACA,IAAI,SAAU,CACb,MAAO,EACR,CAEA,MAAM,SAASC,EAAW,CACzB,GAAI,KAAK,QAAU,CAAC,KAAK,QACxB,OAAOC,EAASD,EAAK,KAAK,MAAM,EAEjC,GAAI,CAAC,KAAK,WAAY,CACrB,GAAM,CAAE,MAAOJ,CAAW,EAAI,MAAO,MAAM,MAAM,6BAA6B,GAAG,KAAK,EACtF,KAAK,WAAaA,CACnB,CAEA,QAASM,EAAU,EAAGA,EAAU,EAAGA,IAClC,GAAI,CACE,KAAK,WACT,MAAMC,EAAY,KAAK,OAAQ,KAAK,QAAQ,IAAI,CAAC,EACjD,KAAK,SAAW,MAAMC,EAAY,KAAK,MAAM,GAG9C,IAAMC,EAAc,MAAMC,EAAmB,KAAK,QAAS,KAAK,WAAY,KAAK,QAAQ,EACzF,OAAOL,EAASD,EAAKK,CAAW,CACjC,OAASE,EAAK,CAEb,GADAf,EAAM,kCAAmCe,CAAG,EACvCA,EAAY,SAAS,WAAW,uBAAuB,EAC3DnB,EAAK,wDAAwDc,CAAO,KAAMK,CAAG,MACvE,OAAMA,CACd,CAEF,CAEA,YAAa,CACZ,MAAO,CAAE,IAAKC,CAAmB,CAClC,CACD","names":["Logger","WARN","LOG","DEBUG","VERBOSE","ERROR","Logger","NftStorageConnector","_NftStorageConnector","serviceDID","apiKey","keypair","rootUcan","car","storeCar","attempt","registerDid","getRootUcan","requestUcan","createRequestToken","err","NFTSTORAGE_GATEWAY"]}
|
package/dist/retrieve.js
ADDED
package/dist/store.js
ADDED
package/package.json
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wovin/connect-nftstorage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./dist/index.
|
|
6
|
-
"module": "./dist/index.
|
|
7
|
-
"browser": "./dist/index.
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"browser": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"import": "./dist/index.
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
12
|
"types": "./dist/index.d.ts"
|
|
13
13
|
},
|
|
14
14
|
"./retrieve": {
|
|
15
|
-
"import": "./dist/retrieve.
|
|
15
|
+
"import": "./dist/retrieve.js",
|
|
16
16
|
"types": "./dist/retrieve.d.ts"
|
|
17
17
|
},
|
|
18
18
|
"./utils": {
|
|
19
|
-
"import": "./dist/utils.
|
|
19
|
+
"import": "./dist/utils.js",
|
|
20
20
|
"types": "./dist/utils.d.ts"
|
|
21
21
|
},
|
|
22
22
|
"./store": {
|
|
23
|
-
"import": "./dist/store.
|
|
23
|
+
"import": "./dist/store.js",
|
|
24
24
|
"types": "./dist/store.d.ts"
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"./dist/"
|
|
28
|
+
"./dist/",
|
|
29
|
+
"./src/"
|
|
29
30
|
],
|
|
30
31
|
"esm.sh": {
|
|
31
32
|
"bundle": false
|
|
@@ -36,8 +37,8 @@
|
|
|
36
37
|
"besonders-logger": "1.0.1",
|
|
37
38
|
"multiformats": "^13.0.1",
|
|
38
39
|
"ucan-storage": "^1.3.0",
|
|
39
|
-
"@wovin/connect-gateway": "^0.
|
|
40
|
-
"@wovin/core": "^0.
|
|
40
|
+
"@wovin/connect-gateway": "^0.2.0",
|
|
41
|
+
"@wovin/core": "^0.2.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"concurrently": "^8.2.2",
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as ucans from '@ucans/ucans'
|
|
2
|
+
import type { IpfsGateway } from '@wovin/connect-gateway'
|
|
3
|
+
import { type StorageConnector } from '@wovin/core/pubsub'
|
|
4
|
+
import { Logger } from 'besonders-logger'
|
|
5
|
+
import { CID } from 'multiformats'
|
|
6
|
+
import { NFTSTORAGE_GATEWAY } from './retrieve.ts'
|
|
7
|
+
import { createRequestToken, getRootUcan, registerDid, storeCar } from './store.ts'
|
|
8
|
+
|
|
9
|
+
const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
|
|
10
|
+
|
|
11
|
+
export * from './retrieve.ts'
|
|
12
|
+
// export * from './store.ts'
|
|
13
|
+
|
|
14
|
+
export class NftStorageConnector implements StorageConnector {
|
|
15
|
+
static async init({ apiKey, keypair, rootUcan }: {
|
|
16
|
+
apiKey?: string
|
|
17
|
+
keypair?: ucans.DidableKey
|
|
18
|
+
rootUcan?: ucans.Ucan
|
|
19
|
+
}) {
|
|
20
|
+
// const { value: serviceDID } = await (await fetch('https://api.nft.storage/did')).json() - Unnecessary delay on init (& might fail when no internet)
|
|
21
|
+
const serviceDID = null
|
|
22
|
+
// if (!keypair) keypair = await ucans.EdKeypair.create()
|
|
23
|
+
DEBUG(`Init NFT.Storage connector:`, { apiKey, keypair, serviceDID })
|
|
24
|
+
return new NftStorageConnector(serviceDID, apiKey, keypair, rootUcan)
|
|
25
|
+
}
|
|
26
|
+
constructor(
|
|
27
|
+
public serviceDID: string | null,
|
|
28
|
+
private apiKey: string | null,
|
|
29
|
+
private keypair: ucans.DidableKey | null,
|
|
30
|
+
private rootUcan: ucans.Ucan | null,
|
|
31
|
+
) {
|
|
32
|
+
if (!(apiKey || (keypair && rootUcan))) throw new Error(`Needs apiKey - or keypair and rootUcan`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get isSetup() {
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
get isValid() {
|
|
39
|
+
return true // TODO test ucan etc
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async storeCar(car: Blob) {
|
|
43
|
+
if (this.apiKey && !this.keypair) {
|
|
44
|
+
return storeCar(car, this.apiKey)
|
|
45
|
+
}
|
|
46
|
+
if (!this.serviceDID) {
|
|
47
|
+
const { value: serviceDID } = await (await fetch('https://api.nft.storage/did')).json()
|
|
48
|
+
this.serviceDID = serviceDID
|
|
49
|
+
}
|
|
50
|
+
// NFT.storage allows only one registered DID, so we might have another agent register between our register & push
|
|
51
|
+
for (let attempt = 1; attempt < 3; attempt++) {
|
|
52
|
+
try {
|
|
53
|
+
if (!this.rootUcan) {
|
|
54
|
+
await registerDid(this.apiKey, this.keypair.did())
|
|
55
|
+
this.rootUcan = await getRootUcan(this.apiKey)
|
|
56
|
+
// TODO: callback to the app - for persisting?
|
|
57
|
+
}
|
|
58
|
+
const requestUcan = await createRequestToken(this.keypair, this.serviceDID, this.rootUcan)
|
|
59
|
+
return storeCar(car, requestUcan)
|
|
60
|
+
} catch (err) {
|
|
61
|
+
ERROR(`[nft-connector.storeCar] error:`, err)
|
|
62
|
+
if ((err as any).message?.contains?.('ERROR_TOKEN_NOT_FOUND')) {
|
|
63
|
+
WARN(`Token not found (might be a race condition) (attempt=${attempt}):`, err)
|
|
64
|
+
} else throw err
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getGateway() {
|
|
70
|
+
return { url: NFTSTORAGE_GATEWAY } satisfies IpfsGateway
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/retrieve.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { retrieveThread as retrieveThreadGW } from '@wovin/connect-gateway'
|
|
2
|
+
import { CidString, IpnsString } from '@wovin/core/applog'
|
|
3
|
+
import { Logger } from 'besonders-logger'
|
|
4
|
+
|
|
5
|
+
const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
|
|
6
|
+
|
|
7
|
+
export const NFTSTORAGE_GATEWAY = `https://nftstorage.link`
|
|
8
|
+
|
|
9
|
+
export async function retrieveThread(
|
|
10
|
+
pubID: IpnsString | CidString | null,
|
|
11
|
+
{ readOnly = true, pinnedCID }: { readOnly?: boolean; pinnedCID?: CidString } = {},
|
|
12
|
+
) {
|
|
13
|
+
return retrieveThreadGW(pubID, {
|
|
14
|
+
readOnly,
|
|
15
|
+
pinnedCID,
|
|
16
|
+
gateways: [{ url: NFTSTORAGE_GATEWAY }],
|
|
17
|
+
})
|
|
18
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as ucans from '@ucans/ucans'
|
|
2
|
+
import { Logger } from 'besonders-logger'
|
|
3
|
+
import { CID } from 'multiformats'
|
|
4
|
+
|
|
5
|
+
const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
|
|
6
|
+
|
|
7
|
+
export async function storeCar(car: Blob, token: ucans.Ucan | string): Promise<CID> {
|
|
8
|
+
// const ucanInfo = await ucans.validate(ucanToken)
|
|
9
|
+
const isUcan = typeof token !== 'string'
|
|
10
|
+
|
|
11
|
+
// Docs: https://nft.storage/api-docs/#operations-NFT_Storage-upload
|
|
12
|
+
const result = await (await fetch('https://api.nft.storage/upload', {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Authorization': `Bearer ${isUcan ? ucans.encode(token) : token}`,
|
|
16
|
+
'Content-Type': `application/car`,
|
|
17
|
+
'x-agent-did': isUcan ? token.payload.iss : undefined,
|
|
18
|
+
},
|
|
19
|
+
body: car,
|
|
20
|
+
})).json()
|
|
21
|
+
DEBUG('[storeCar] result', { result, isUcan })
|
|
22
|
+
if (!result.ok) throw new Error(`Failed to upload` + JSON.stringify(result.error))
|
|
23
|
+
return CID.parse(result.value.cid)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function createRequestToken(
|
|
27
|
+
keypair: ucans.DidableKey,
|
|
28
|
+
serviceDID: string,
|
|
29
|
+
ucanToken: ucans.Ucan,
|
|
30
|
+
) {
|
|
31
|
+
DEBUG(`[createRequestToken] `, { keypair, ucanToken })
|
|
32
|
+
// we want to include the capabilities of the parent token in our request token
|
|
33
|
+
// so we validate the parent token to extract the payload and copy over the capabilities
|
|
34
|
+
// // the `att` field contains the capabilities we need for uploading
|
|
35
|
+
const payload = ucanToken.payload
|
|
36
|
+
return ucans.build({
|
|
37
|
+
issuer: keypair,
|
|
38
|
+
audience: serviceDID,
|
|
39
|
+
capabilities: (payload as any).att, // .map(att => ucans.capability.parse(att)),
|
|
40
|
+
proofs: [ucans.encode(ucanToken)],
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
export async function registerDid(apiKey: string, did: string) {
|
|
44
|
+
DEBUG(`Registering`, { did })
|
|
45
|
+
const result = await (await fetch('https://api.nft.storage/user/did', {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
did,
|
|
52
|
+
}),
|
|
53
|
+
})).json()
|
|
54
|
+
DEBUG(`Register DID result`, result)
|
|
55
|
+
if (!result.ok) throw new Error(`Failed to register DID ` + JSON.stringify(result.error))
|
|
56
|
+
}
|
|
57
|
+
export async function getRootUcan(apiKey: string) {
|
|
58
|
+
DEBUG(`Get root UCAN`, { apiKey })
|
|
59
|
+
const result = await (await fetch('https://api.nft.storage/ucan/token', {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
63
|
+
},
|
|
64
|
+
})).json()
|
|
65
|
+
DEBUG(`Get root UCAN result`, result)
|
|
66
|
+
if (!result.ok) throw new Error(`Failed get root UCAN ` + JSON.stringify(result.error))
|
|
67
|
+
return ucans.validate(result.value)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// import { build } from 'ucan-storage/ucan-storage'
|
|
71
|
+
|
|
72
|
+
// // The DID for the storage service. In real code, you should obtain this from the service you're targetting.
|
|
73
|
+
// const serviceDID = 'did:key:a-fake-service-did'
|
|
74
|
+
|
|
75
|
+
// async function createRequestToken(parentUCAN, issuerKeyPair) {
|
|
76
|
+
// // we want to include the capabilities of the parent token in our request token
|
|
77
|
+
// // so we validate the parent token to extract the payload and copy over the capabilities
|
|
78
|
+
// const { payload } = await validate(parentUCAN)
|
|
79
|
+
|
|
80
|
+
// // the `att` field contains the capabilities we need for uploading
|
|
81
|
+
// const { att } = payload
|
|
82
|
+
|
|
83
|
+
// return build({
|
|
84
|
+
// issuer: issuerKeyPair,
|
|
85
|
+
// audience: serviceDID,
|
|
86
|
+
// capabilities: att,
|
|
87
|
+
// proofs: [parentUcan],
|
|
88
|
+
// })
|
|
89
|
+
// }
|