ceramicnetwork / js-ceramic

Typescript implementation of the Ceramic protocol
http://ceramic.network
Other
414 stars 127 forks source link

In sample core code : Ceramic.create is not a function ? #1429

Open on-meetsys opened 3 years ago

on-meetsys commented 3 years ago

Describe the bug The code described here leads to several errors : https://developers.ceramic.network/build/installation/ https://github.com/ceramicnetwork/js-ceramic/tree/develop/packages/core

To Reproduce With the code given, after packages are installed :

// package.json 
{
  "name": "ceramic-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@ceramicnetwork/core": "^0.19.7",
    "dag-jose": "^0.3.0",
    "ipfs-core": "^0.5.4",
    "multiformats": "^4.6.2"
  }
}
// index.js
import Ceramic from '@ceramicnetwork/core'
import IPFS from 'ipfs'
import dagJose from 'dag-jose'
import basicsImport from 'multiformats/cjs/src/basics-import.js'
import legacy from 'multiformats/cjs/src/legacy.js'

basicsImport.multicodec.add(dagJose)
const format = legacy(basicsImport, dagJose.name)
const ipfs = Ipfs.create({
    ipld: { formats: [format] },
})

const ceramic = await Ceramic.create(ipfs, config)

Several errors with this code occur after node index.js (v15.9.0) :

I've therefore modified ìndex.js` (according this comment dealing with multiformat ) :

import Ceramic from '@ceramicnetwork/core'
import IPFS from 'ipfs-core'
import dagJose from 'dag-jose'
import { sha256 } from 'multiformats/hashes/sha2'
import legacy from 'multiformats/legacy'

const hasher = {}
hasher[sha256.code] = sha256
const format = legacy(dagJose, {hashes: hasher})

const ipfs = IPFS.create({
    ipld: { formats: [format] },
})

Ceramic.create(ipfs, {}).then((c) => {
    console.info(c);
})

Which returns a TypeError: Ceramic.create is not a function

May be the example code should be modified/improved to help newcomers start with ceramiq, with a step by step sample.

oed commented 3 years ago

The ipfs creation code is outdated in the docs site it seems, try the example in this blogpost: https://blog.ceramic.network/how-to-store-signed-and-encrypted-data-on-ipfs/

Will update the docs!

oed commented 3 years ago

The documentation should be updated now. Can you give it another try and let us know how it works @on-meetsys?

on-meetsys commented 3 years ago

Thanks. The documentation is indeed closer to the code found in other places. I think there are still some mistakes in it to help a newcomer make it run. I've modified it this way (cf comments) :

import Ceramic from '@ceramicnetwork/core'
import IPFS from 'ipfs-core' // this is the ipfs package name
import dagJose from 'dag-jose'
import { sha256 } from 'multiformats/hashes/sha2'
import legacy from 'multiformats/legacy'

const hasher = {}
hasher[sha256.code] = sha256
const dagJoseFormat = legacy(dagJose, {hashes: hasher})

const ipfs = await IPFS.create({ ipld: { formats: [dagJoseFormat] } }) // not Ipfs.create

const ceramic = await Ceramic.create(ipfs) // config is optional, and is not defined previously

Nevertheless, this code still returns an error (stored as index.js, and launched by node index.js V15.9.0 ) :

const ceramic = await Ceramic.create(ipfs) 
                              ^
TypeError: Ceramic.create is not a function
    at file:///Users/olivier/Devs/ceramic-test/index.js:13:31

May be I've misunderstood anything else ? But as explained in this documentation, using node, I still can not make it work.

oed commented 3 years ago

I think this has something to do with your environment not using the default. Try doing this:

import { default: Ceramic } from '@ceramicnetwork/core'

If that doesnt' work try logging the Ceramic object you imported above and see if there is a default property.

ayushm2003 commented 3 years ago

I think this has something to do with your environment not using the default. Try doing this:

import { default: Ceramic } from '@ceramicnetwork/core'

If that doesnt' work try logging the Ceramic object you imported above and see if there is a default property.

const ceramic = await Ceramic["default"].create(ipfs) worked. it would be good if this could be updated in the documentation

oed commented 3 years ago

@zachferland @PaulLeCam Any idea why the default may not be available as the top level export when using import?

I imagine this only happens for certain setups (usually works in the expected way).

olnrt commented 3 years ago

Hello, You are all right, it comes from the environment setup. In my case, in node/javascript, the import creates a Ceramic object with a 'default' parameter. Therefore, it works with a const ceramic = await Ceramic.defautl.create(ipfs) In a typescript environment, the 'default' is not required.

By the way, once corrected, the previous code launches another error with dag-jose. Even if it not the same issue, I will ask here, because other people may find this code by googling it. This issue is also opened here

Whatever the way I setup the ìpld.formats', the dag-jose format is not found during the first ipfs.dag.put :

const hasher = {}
hasher[sha256.code] = sha256
const dagJoseFormat = legacy(dagJose, {hashes: hasher})
// or 
//  import { convert } from 'blockcodec-to-ipld-format'
//  const dagJoseFormat = convert(dagJose)
const ipfs = await IPFS.create({ ipld: { formats: [dagJoseFormat] } })

const jwe = await did.createDagJWE(...)
ipfs.dag.put(jwe, { format: 'dag-jose', hashAlg: 'sha2-256' })

A console.info(dagJoseFormat) returns :

{
  defaultHashAlg: 'sha2-256',
  codec: undefined,
  util: {
    serialize: [Function: serialize],
    deserialize: [Function: deserialize],
    cid: [AsyncFunction: cid]
  },
  resolver: { resolve: [Function: resolve], tree: [Function: tree] }
}

and the ipfs.dag.put(jwe, { format: 'dag-jose', hashAlg: 'sha2-256' }) throws a

Loading IPLD format 133
(node:99004) UnhandledPromiseRejectionWarning: Error: Missing IPLD format "dag-jose"

I found surprising the codec:undefined, and have tried to 'force' it to 'dag-jose' without success. Is it also a environment setup bug ?

PaulLeCam commented 3 years ago

@zachferland @PaulLeCam Any idea why the default may not be available as the top level export when using import?

I imagine this only happens for certain setups (usually works in the expected way).

Put simply, we shouldn't use export default, compilers and bundlers have different ways to handle it that may be incompatible. Longer answer: our docs use ESM syntax with the expectations TypeScript or Babel is used and handles interoperability with CJS exports, but now that Node supports ESM natively we shouldn't have this expectation. Node has some interoperability to import CJS modules from ESM ones but likely handling the "default" import differently than Babel/TypeScript. Ideally we should export ESM modules directly, but in the meantime I think using named exports would provide better compatibility. We can do this in a backwards-compatible way, such as:

// Add named export
export class Ceramic {...}
// Keep default export while in v1.x
export default Ceramic

And then in docs, replace import Ceramic from '...' by import { Ceramic } from '...'.

Hesbon5600 commented 2 years ago

Hello, You are all right, it comes from the environment setup. In my case, in node/javascript, the import creates a Ceramic object with a 'default' parameter. Therefore, it works with a const ceramic = await Ceramic.defautl.create(ipfs) In a typescript environment, the 'default' is not required.

By the way, once corrected, the previous code launches another error with dag-jose. Even if it not the same issue, I will ask here, because other people may find this code by googling it. This issue is also opened here

Whatever the way I setup the ìpld.formats', the dag-jose format is not found during the first ipfs.dag.put :

const hasher = {}
hasher[sha256.code] = sha256
const dagJoseFormat = legacy(dagJose, {hashes: hasher})
// or 
//  import { convert } from 'blockcodec-to-ipld-format'
//  const dagJoseFormat = convert(dagJose)
const ipfs = await IPFS.create({ ipld: { formats: [dagJoseFormat] } })

const jwe = await did.createDagJWE(...)
ipfs.dag.put(jwe, { format: 'dag-jose', hashAlg: 'sha2-256' })

A console.info(dagJoseFormat) returns :

{
  defaultHashAlg: 'sha2-256',
  codec: undefined,
  util: {
    serialize: [Function: serialize],
    deserialize: [Function: deserialize],
    cid: [AsyncFunction: cid]
  },
  resolver: { resolve: [Function: resolve], tree: [Function: tree] }
}

and the ipfs.dag.put(jwe, { format: 'dag-jose', hashAlg: 'sha2-256' }) throws a

Loading IPLD format 133
(node:99004) UnhandledPromiseRejectionWarning: Error: Missing IPLD format "dag-jose"

I found surprising the codec:undefined, and have tried to 'force' it to 'dag-jose' without success. Is it also a environment setup bug ?

I'm experiencing the same issue

import * as dagJose from 'dag-jose'
import { convert } from 'blockcodec-to-ipld-format'
import { create } from 'ipfs-http-client'
const auth =
'Basic ' + Buffer.from("projectId:projectSecret").toString('base64')

const dagJoseFormat = convert(dagJose)

const ipfs = create({
    url: new URL('https://ipfs.infura.io:5001'),
    headers: {
        authorization: auth
    },
    ipld: { formats: [dagJoseFormat] },
})

image

oed commented 2 years ago

What version of IPFS are you using @Hesbon5600 ? This conversation might be relevant to your issue: https://github.com/ceramicnetwork/js-dag-jose/pull/20

Hesbon5600 commented 2 years ago

What version of IPFS are you using @Hesbon5600 ? This conversation might be relevant to your issue: ceramicnetwork/js-dag-jose#20

I'm using ipfs HTTP client

"ipfs-http-client": "^52.0.2",
btme0011 commented 9 months ago

can i work on this issue?

oed commented 8 months ago

@btme0011 This issue is pretty old so not sure if it's still a problem actually. If you can replicate it then feel free!