├── .nojekyll ├── .gitignore ├── prettier.config.js ├── images └── chevron.png ├── RELEASE.md ├── .github ├── ISSUE_TEMPLATE │ └── issue-template.md ├── workflows │ └── main.yml └── pull_request_template.md ├── .eslintrc.js ├── LICENSE-APACHE ├── tsconfig.json ├── src ├── ceramic.ts ├── datastore.ts ├── wallet.ts └── app.ts ├── webpack.config.js ├── skydb.html ├── LICENSE-MIT ├── README.md ├── package.json ├── main.css ├── dist └── app.js.LICENSE.txt └── index.html /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('eslint-config-3box/prettier.config') 2 | -------------------------------------------------------------------------------- /images/chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceramicstudio/web-playground/HEAD/images/chevron.png -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # RELEASE GUIDELINES: 2 | _Note: These are for before any workflow has been configured to make this process easier._ 3 | 1. Finish the work required. 4 | 2. Have a member of the core team approve your PR. 5 | 3. Run `npm run build` & commit the repository. 6 | 4. Merge into `main` 7 | 5. Test in production -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue template 3 | about: Standard issue template. 4 | title: "[Replace me with meaningful title]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Description 11 | 12 | Provide a 2-3 sentence overview of the work to be done. 13 | 14 | # Technical Information 15 | 16 | Provide an explanation of the technical work to be done. 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['3box', '3box/typescript'], 3 | rules: { 4 | '@typescript-eslint/ban-ts-comment': 'off', 5 | '@typescript-eslint/ban-ts-ignore': 'off', 6 | '@typescript-eslint/interface-name-prefix': 'off', 7 | '@typescript-eslint/no-unsafe-assignment': 'off', 8 | '@typescript-eslint/no-unsafe-call': 'off', 9 | '@typescript-eslint/no-unsafe-member-access': 'off', 10 | '@typescript-eslint/no-unsafe-return': 'off', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "commonjs", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /src/ceramic.ts: -------------------------------------------------------------------------------- 1 | import type { CeramicApi } from '@ceramicnetwork/common' 2 | import Ceramic from '@ceramicnetwork/http-client' 3 | import { Caip10Link } from '@ceramicnetwork/stream-caip10-link' 4 | import { TileDocument } from '@ceramicnetwork/stream-tile' 5 | 6 | declare global { 7 | interface Window { 8 | ceramic?: CeramicApi 9 | [index: string]: any 10 | } 11 | } 12 | 13 | export async function createCeramic(): Promise { 14 | const ceramic = new Ceramic('https://ceramic-clay.3boxlabs.com') 15 | window.ceramic = ceramic 16 | window.TileDocument = TileDocument 17 | window.Caip10Link = Caip10Link 18 | return Promise.resolve(ceramic as CeramicApi) 19 | } 20 | -------------------------------------------------------------------------------- /src/datastore.ts: -------------------------------------------------------------------------------- 1 | import { CeramicApi } from '@ceramicnetwork/common' 2 | import { DIDDataStore } from '@glazed/did-datastore' 3 | 4 | declare global { 5 | interface Window { 6 | DataStore: DIDDataStore 7 | } 8 | } 9 | 10 | const publishedModel = { 11 | schemas: { 12 | basicProfile: 'ceramic://k3y52l7qbv1frxt706gqfzmq6cbqdkptzk8uudaryhlkf6ly9vx21hqu4r6k1jqio', 13 | }, 14 | definitions: { 15 | basicProfile: 'kjzl6cwe1jw145cjbeko9kil8g9bxszjhyde21ob8epxuxkaon1izyqsu8wgcic', 16 | }, 17 | tiles: {}, 18 | } 19 | 20 | export function createDataStore(ceramic: CeramicApi): DIDDataStore { 21 | const datastore = new DIDDataStore({ ceramic, model: publishedModel }) 22 | window.DataStore = datastore 23 | return datastore 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy to Pages 2 | 3 | on: 4 | pull_request: 5 | branches: main 6 | types: [closed] 7 | 8 | jobs: 9 | build: 10 | if: ${{github.event.pull_request.merged == true}} 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [16.x] 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | 24 | - name: Install 25 | run: npm install 26 | 27 | - name: Build 28 | run: npm run build 29 | 30 | - name: Deploy to GH Pages 31 | run: | 32 | git add --all 33 | git config --local user.email "action@github.com" 34 | git config --local user.name "GitHub Action" 35 | git commit -m 'deploy' 36 | git push origin HEAD:main 37 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: { 5 | app: './src/app.ts', 6 | }, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.tsx?$/, 11 | loader: 'esbuild-loader', 12 | exclude: /node_modules/, 13 | options: { 14 | loader: 'tsx', 15 | target: 'es2020', 16 | }, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: ['.ts', '.js'], 22 | fallback: { 23 | stream: require.resolve('stream-browserify'), 24 | https: require.resolve('https-browserify'), 25 | http: require.resolve('http-browserify'), 26 | os: require.resolve('os-browserify/browser'), 27 | assert: false, 28 | url: false, 29 | }, 30 | }, 31 | plugins: [ 32 | new webpack.ProvidePlugin({ 33 | process: 'process/browser', 34 | Buffer: ['buffer', 'Buffer'], 35 | }), 36 | ], 37 | output: { 38 | filename: '[name].js', 39 | }, 40 | mode: 'development', 41 | } 42 | -------------------------------------------------------------------------------- /skydb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | IDX + SkyDB Playground 8 | 9 | 10 | 11 | 21 | 22 | 23 | 24 |
25 |

Open your console and click the button below to authenticate

26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/wallet.ts: -------------------------------------------------------------------------------- 1 | import WalletConnectProvider from '@walletconnect/web3-provider' 2 | import { ThreeIdConnect, EthereumAuthProvider } from '@3id/connect' 3 | import Authereum from 'authereum' 4 | import type { DIDProvider } from 'dids' 5 | import Fortmatic from 'fortmatic' 6 | import Web3Modal from 'web3modal' 7 | 8 | // @ts-ignore 9 | export const threeID = new ThreeIdConnect() 10 | 11 | export const web3Modal = new Web3Modal({ 12 | network: 'mainnet', 13 | cacheProvider: true, 14 | providerOptions: { 15 | walletconnect: { 16 | package: WalletConnectProvider, 17 | options: { 18 | infuraId: 'e87f83fb85bf4aa09bdf6605ebe144b7', 19 | }, 20 | }, 21 | fortmatic: { 22 | package: Fortmatic, 23 | options: { 24 | key: 'pk_live_EC842EEAC7F08995', 25 | }, 26 | }, 27 | authereum: { 28 | package: Authereum, 29 | options: {}, 30 | }, 31 | }, 32 | }) 33 | 34 | export async function getProvider(): Promise { 35 | const ethProvider = await web3Modal.connect() 36 | const addresses = await ethProvider.enable() 37 | await threeID.connect(new EthereumAuthProvider(ethProvider, addresses[0])) 38 | return threeID.getDidProvider() 39 | } 40 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # [Replace Me With Meaningful Name] - #[Issue] 2 | 3 | ## Description 4 | 5 | Include a summary of the change and which issue it addresses in the title of the PR. 6 | 7 | Include relevant motivation, context, brief description and impact of the change(s). List follow-up tasks here. 8 | 9 | ## How Has This Been Tested? 10 | 11 | Describe the tests that you ran to verify your changes. Provide instructions for reproduction. 12 | 13 | - [ ] Test A (e.g. Test A - New test that ... ran in local, docker, and dev unstable.) 14 | - [ ] Test B 15 | 16 | ## Definition of Done 17 | 18 | Before submitting this PR, please make sure: 19 | 20 | - [ ] The work addresses the description and outcomes in the issue 21 | - [ ] I have added relevant tests for new or updated functionality 22 | - [ ] My code follows conventions, is well commented, and easy to understand 23 | - [ ] My code builds and tests pass without any errors or warnings 24 | - [ ] I have tagged the relevant reviewers 25 | - [ ] I have updated the READMEs of affected packages 26 | - [ ] I have made corresponding changes to the documentation 27 | - [ ] The changes have been communicated to interested parties 28 | 29 | ## References: 30 | 31 | Please list relevant documentation (e.g. tech specs, articles, related work etc.) relevant to this change, and note if the documentation has been updated. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Ceramic Web Playground](https://ceramicstudio.github.io/web-playground/) 2 | 3 | Test the full stack of [Ceramic Network](https://ceramic.network/) components in a web browser. 4 | 5 | ## Technologies 6 | 7 | - [Ceramic HTTP Client](https://developers.ceramic.network/reference/javascript/clients/#http-client): Provides access to the Ceramic Network via a remote node running Ceramic (and IPFS). 8 | - [3ID Connect](https://developers.ceramic.network/build/authentication/#did-provider-or-wallet): Provides authentication to a DID (used by Ceramic) from a blockchain wallet, and stores a link from this blockchain account to your DID in IDX. 9 | - [DID-DataStore](https://developers.ceramic.network/tools/glaze/did-datastore/): Implements the Identity Index protocol to store and retrieve data associated with a DID from a provided DataModel. 10 | 11 | ## Usage 12 | 13 | 1. Open the [Playground page](https://ceramicstudio.github.io/web-playground/) 14 | 1. Open your console by inspecting the page 15 | 1. Authenticate by clicking "Connect wallet" 16 | 1. Approve prompts in your Web3 wallet 17 | 1. Wait to see "Connected with DID" in your console 18 | 1. Write and read documents on the Ceramic Network from the console using the referenced API methods 19 | 1. Write and read records with the DID-DataStore from the console using the referenced API methods 20 | 21 | ## License 22 | 23 | Apache-2.0 OR MIT 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-playground", 3 | "version": "1.0.0", 4 | "description": "Ceramic Web playground", 5 | "author": "Ceramic Studio", 6 | "license": "(Apache-2.0 OR MIT)", 7 | "homepage": "https://github.com/ceramicstudio/web-playground#readme", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/ceramicstudio/web-playground.git" 11 | }, 12 | "scripts": { 13 | "build": "webpack --mode production", 14 | "lint": "eslint src", 15 | "serve": "serve .", 16 | "watch": "webpack --watch" 17 | }, 18 | "dependencies": { 19 | "@3id/connect": "^0.2.5", 20 | "@ceramicnetwork/3id-did-resolver": "^1.2.7", 21 | "@ceramicnetwork/common": "^1.1.0", 22 | "@ceramicnetwork/http-client": "^1.4.3", 23 | "@glazed/datamodel": "^0.2.0", 24 | "@glazed/devtools": "^0.1.3", 25 | "@glazed/did-datastore": "^0.2.0", 26 | "@walletconnect/web3-provider": "^1.6.6", 27 | "authereum": "^0.1.14", 28 | "buffer": "^5.7.1", 29 | "dag-jose": "^1.0.0", 30 | "dids": "^2.4.0", 31 | "esbuild-loader": "^2.16.0", 32 | "fortmatic": "^2.2.1", 33 | "key-did-resolver": "^1.4.0", 34 | "multiformats": "^9.4.10", 35 | "skynet-js": "^4.0.18-beta", 36 | "uint8arrays": "^3.0.0", 37 | "web3modal": "^1.9.4" 38 | }, 39 | "devDependencies": { 40 | "@datamodels/identity-profile-basic": "^0.1.2", 41 | "@glazed/types": "^0.1.3", 42 | "did-jwt": "^5.2.0", 43 | "did-resolver": "^3.0.1", 44 | "eslint": "^7.25.0", 45 | "eslint-config-3box": "^0.2.0", 46 | "http-browserify": "^1.7.0", 47 | "https-browserify": "^1.0.0", 48 | "os-browserify": "^0.3.0", 49 | "prettier": "^2.2.1", 50 | "process": "^0.11.10", 51 | "serve": "^11.3.2", 52 | "stream-browserify": "^3.0.0", 53 | "ts-loader": "^8.2.0", 54 | "typescript": "^4.2.4", 55 | "webpack": "^5.58.1", 56 | "webpack-cli": "^3.0.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f7f7f9; 3 | display: flex; 4 | flex-direction: column; 5 | height: 100vh; 6 | overflow: auto; 7 | padding-top: 25px; 8 | } 9 | 10 | header { 11 | text-align: center; 12 | } 13 | 14 | h1 { 15 | font-size: 2rem; 16 | } 17 | h3 { 18 | font-size: 1.5rem; 19 | } 20 | p { 21 | font-size: 1.2rem; 22 | } 23 | main { 24 | max-width: 600px; 25 | } 26 | .logo { 27 | height: 125px; 28 | width: 125px; 29 | margin-right: auto; 30 | margin-left: auto; 31 | } 32 | .hide { 33 | display: none; 34 | padding: 0; 35 | margin: 0; 36 | } 37 | .center { 38 | margin: 0 auto; 39 | } 40 | .loader { 41 | display: inline-block; 42 | width: 2rem; 43 | height: 2rem; 44 | vertical-align: text-bottom; 45 | border: 0.25em solid currentColor; 46 | border-right-color: transparent; 47 | border-radius: 50%; 48 | -webkit-animation: spin 1s linear infinite; 49 | -moz-animation: spin 1s linear infinite; 50 | animation: spin 1s linear infinite; 51 | margin-right: 10px; 52 | } 53 | @-moz-keyframes spin { 54 | 100% { 55 | -moz-transform: rotate(360deg); 56 | } 57 | } 58 | @-webkit-keyframes spin { 59 | 100% { 60 | -webkit-transform: rotate(360deg); 61 | } 62 | } 63 | @keyframes spin { 64 | 100% { 65 | -webkit-transform: rotate(360deg); 66 | transform: rotate(360deg); 67 | } 68 | } 69 | .alert { 70 | margin-top: 15px; 71 | } 72 | .space-around { 73 | display: flex; 74 | flex-direction: row; 75 | flex-wrap: nowrap; 76 | justify-content: space-between; 77 | } 78 | ul { 79 | padding-left: 20px; 80 | } 81 | 82 | /** 83 | * Utils & overrides 84 | */ 85 | .small-logo { 86 | height: 120px; 87 | transition: ease all 0.3s; 88 | } 89 | .connection { 90 | display: inline-block; 91 | width: 5px; 92 | height: 5px; 93 | border-radius: 50%; 94 | margin: 0px 3px 2px 0; 95 | } 96 | .connection.offline { 97 | background-color: red; 98 | } 99 | .connection.online { 100 | background-color: rgb(41, 241, 41); 101 | } 102 | main { 103 | margin-bottom: 40px; 104 | } 105 | 106 | /** 107 | * Accordion 108 | */ 109 | .accordion { 110 | width: 100%; 111 | cursor: pointer; 112 | } 113 | .accordion .head { 114 | /* width: 100%; */ 115 | font-weight: bold; 116 | opacity: 1 !important; 117 | background-color: white; 118 | padding: 15px; 119 | 120 | display: flex; 121 | flex-direction: row; 122 | justify-content: space-between; 123 | align-items: center; 124 | } 125 | .accordion .head h3 { 126 | margin: 0; 127 | } 128 | .accordion.acc-close .body { 129 | max-height: 0; 130 | overflow: hidden; 131 | transition: ease-out max-height 0.3s; 132 | } 133 | .accordion .body { 134 | margin-top: 10px; 135 | margin-left: 15px; 136 | } 137 | .accordion.acc-open .body { 138 | max-height: 500px; 139 | overflow: hidden; 140 | transition: ease-in max-height 0.3s; 141 | } 142 | .accordion.acc-open .head img { 143 | transform: rotate(-90deg); 144 | transition: ease transform 0.3s; 145 | height: 10px; 146 | } 147 | .accordion.acc-close .head img { 148 | transform: rotate(90deg); 149 | transition: ease transform 0.3s; 150 | height: 10px; 151 | } 152 | 153 | /** 154 | * Keep the DID visible. 155 | */ 156 | .sticky { 157 | position: -webkit-sticky; /* Safari */ 158 | position: sticky; 159 | top: 20px; 160 | } 161 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import { DID } from 'dids' 2 | import ThreeIdResolver from '@ceramicnetwork/3id-did-resolver' 3 | import KeyDidResolver from 'key-did-resolver' 4 | 5 | import { createCeramic } from './ceramic' 6 | import { createDataStore } from './datastore' 7 | import { getProvider } from './wallet' 8 | import type { ResolverRegistry } from 'did-resolver' 9 | 10 | declare global { 11 | interface Window { 12 | did?: DID 13 | } 14 | } 15 | 16 | const ceramicPromise = createCeramic() 17 | 18 | const authenticate = async (): Promise => { 19 | const [ceramic, provider] = await Promise.all([ceramicPromise, getProvider()]) 20 | const keyDidResolver = KeyDidResolver.getResolver() 21 | const threeIdResolver = ThreeIdResolver.getResolver(ceramic) 22 | const resolverRegistry: ResolverRegistry = { 23 | ...threeIdResolver, 24 | ...keyDidResolver, 25 | } 26 | const did = new DID({ 27 | provider: provider, 28 | resolver: resolverRegistry, 29 | }) 30 | document.getElementById('authenticating')?.classList.remove('hide') 31 | await did.authenticate() 32 | await ceramic.setDID(did) 33 | const datastore = createDataStore(ceramic) 34 | window.did = ceramic.did 35 | return datastore.id 36 | } 37 | 38 | const updateAlert = (status: string, message: string) => { 39 | const alert = document.getElementById('alerts') 40 | 41 | if (alert !== null) { 42 | alert.textContent = message 43 | alert.classList.add(`alert-${status}`) 44 | alert.classList.remove('hide') 45 | setTimeout(() => { 46 | alert.classList.add('hide') 47 | }, 5000) 48 | } 49 | } 50 | 51 | const accordions = document.getElementsByClassName('accordion') 52 | 53 | for (const accordion of accordions) { 54 | accordion.addEventListener('click', (e) => { 55 | if (accordion.classList.contains('acc-close')) { 56 | accordion.classList.remove('acc-close') 57 | accordion.classList.add('acc-open') 58 | } else { 59 | accordion.classList.add('acc-close') 60 | accordion.classList.remove('acc-open') 61 | } 62 | }) 63 | } 64 | 65 | document.getElementsByClassName('accordion') 66 | 67 | document.getElementById('bauth')?.addEventListener('click', () => { 68 | document.getElementById('loader')?.classList.remove('hide') 69 | authenticate().then( 70 | (id) => { 71 | const userDid = document.getElementById('did') 72 | const status = document.getElementById('status') 73 | const concatId = id.split('did:3:')[1] 74 | if (userDid !== null) { 75 | userDid.textContent = `${concatId.slice(0, 4)}...${concatId.slice( 76 | concatId.length - 4, 77 | concatId.length 78 | )}` 79 | status?.classList.remove('offline') 80 | status?.classList.add('online') 81 | document.getElementById('logo')?.classList.add('small-logo') 82 | } 83 | updateAlert('success', `Successfully connected with ${id}`) 84 | document.getElementById('bauth')?.classList.add('hide') 85 | document.getElementById('profile-cta')?.addEventListener('click', () => { 86 | window.open(`https://clay.self.id/${id}`, '_blank') 87 | }) 88 | document.getElementById('post-auth')?.classList.remove('hide') 89 | document.getElementById('post-auth')?.classList.add('show') 90 | }, 91 | (err) => { 92 | console.error('Failed to authenticate:', err) 93 | updateAlert('danger', err) 94 | document.getElementById('loader')?.classList.add('hide') 95 | } 96 | ) 97 | }) 98 | -------------------------------------------------------------------------------- /dist/app.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /*! 8 | * mustache.js - Logic-less {{mustache}} templates with JavaScript 9 | * http://github.com/janl/mustache.js 10 | */ 11 | 12 | /*! 13 | * Determine if an object is a Buffer 14 | * 15 | * @author Feross Aboukhadijeh 16 | * @license MIT 17 | */ 18 | 19 | /*! 20 | * The buffer module from node.js, for the browser. 21 | * 22 | * @author Feross Aboukhadijeh 23 | * @license MIT 24 | */ 25 | 26 | /*! 27 | * depd 28 | * Copyright(c) 2015 Douglas Christopher Wilson 29 | * MIT Licensed 30 | */ 31 | 32 | /*! 33 | * http-errors 34 | * Copyright(c) 2014 Jonathan Ong 35 | * Copyright(c) 2016 Douglas Christopher Wilson 36 | * MIT Licensed 37 | */ 38 | 39 | /*! 40 | * https://github.com/Starcounter-Jack/JSON-Patch 41 | * (c) 2017 Joachim Wester 42 | * MIT license 43 | */ 44 | 45 | /*! 46 | * statuses 47 | * Copyright(c) 2014 Jonathan Ong 48 | * Copyright(c) 2016 Douglas Christopher Wilson 49 | * MIT Licensed 50 | */ 51 | 52 | /*! 53 | * toidentifier 54 | * Copyright(c) 2016 Douglas Christopher Wilson 55 | * MIT Licensed 56 | */ 57 | 58 | /*! ***************************************************************************** 59 | Copyright (c) Microsoft Corporation. 60 | 61 | Permission to use, copy, modify, and/or distribute this software for any 62 | purpose with or without fee is hereby granted. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 65 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 66 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 67 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 68 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 69 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 70 | PERFORMANCE OF THIS SOFTWARE. 71 | ***************************************************************************** */ 72 | 73 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ 74 | 75 | /*! safe-buffer. MIT License. Feross Aboukhadijeh */ 76 | 77 | /** 78 | * [js-sha256]{@link https://github.com/emn178/js-sha256} 79 | * 80 | * @version 0.9.0 81 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 82 | * @copyright Chen, Yi-Cyuan 2014-2017 83 | * @license MIT 84 | */ 85 | 86 | /** 87 | * [js-sha3]{@link https://github.com/emn178/js-sha3} 88 | * 89 | * @version 0.8.0 90 | * @author Chen, Yi-Cyuan [emn178@gmail.com] 91 | * @copyright Chen, Yi-Cyuan 2015-2018 92 | * @license MIT 93 | */ 94 | 95 | /** @license React v0.19.1 96 | * scheduler.production.min.js 97 | * 98 | * Copyright (c) Facebook, Inc. and its affiliates. 99 | * 100 | * This source code is licensed under the MIT license found in the 101 | * LICENSE file in the root directory of this source tree. 102 | */ 103 | 104 | /** @license React v16.13.1 105 | * react-dom.production.min.js 106 | * 107 | * Copyright (c) Facebook, Inc. and its affiliates. 108 | * 109 | * This source code is licensed under the MIT license found in the 110 | * LICENSE file in the root directory of this source tree. 111 | */ 112 | 113 | /** @license React v16.13.1 114 | * react-is.production.min.js 115 | * 116 | * Copyright (c) Facebook, Inc. and its affiliates. 117 | * 118 | * This source code is licensed under the MIT license found in the 119 | * LICENSE file in the root directory of this source tree. 120 | */ 121 | 122 | /** @license React v16.13.1 123 | * react.production.min.js 124 | * 125 | * Copyright (c) Facebook, Inc. and its affiliates. 126 | * 127 | * This source code is licensed under the MIT license found in the 128 | * LICENSE file in the root directory of this source tree. 129 | */ 130 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ceramic Web Playground 7 | 11 | 12 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 | Not Connected
29 |
30 |
31 |
32 | 33 |
34 |

Ceramic Web Playground

35 |

36 | Test the full stack of 37 | Ceramic Network components in a web 38 | browser. 39 |

40 |
41 |
42 |
43 |
44 |

Technologies

45 | 46 |
47 |
48 |
    49 |
  • 50 | Ceramic HTTP Client: Provides access to the Ceramic Network via a remote node running Ceramic (and 55 | IPFS). 56 |
  • 57 |
  • 58 | 3ID Connect: Provides authentication to a DID (used by Ceramic) from a blockchain wallet, and 63 | stores a link from this blockchain account to your DID in IDX. 64 |
  • 65 |
  • 66 | DataModels: Provides validation for data before it is commited to the Ceramic Network. 71 |
  • 72 |
  • 73 | DID-DataStore: Implements the Identity Index protocol to store and retrieve data associated with 78 | a DID from a provided DataModel. 79 |
  • 80 |
81 |
82 |
83 |
84 |
85 |
86 |

Flight Check

87 | 88 |
89 |
90 |

A few things you should see before we continue on...

91 |
    92 |
  1. 93 | In the top right corner you should see a little pill that has some random text, 94 | this is a concatenated version of your DID. If you want to see the full version in 95 | your browser inspector's console you can enter ceramic.did.id to see 96 | your full DID. 97 |
  2. 98 |
  3. 99 | (optional) We recommend you take a quick look at the technologies we've listed 100 | above to get a feel for what Ceramic is capable of. 101 |
  4. 102 |
103 |
104 |
105 |
106 |
107 |

Variables

108 | 109 |
110 |
111 |
    112 |
  • 113 | ceramic, our core client providing access to a testnet node to handle 114 | all of our reads & writes. 115 |
  • 116 |
  • 117 | TileDocument, exposes our TileDocument library used to create 118 | documents on the Ceramic Network. Once created a document is called a Tile, and 119 | the Tile's history is referred to as a "stream". 120 |
  • 121 | 122 |
  • 123 | DataStore, our DID-DataStore instance. This is how we'll be 124 | interacting with the Identity Index protocol. The BasicProfile DataModel has been 125 | preloaded & configured. 126 |
  • 127 |
128 | Please note, you will not be able to use {import ...} statements as we 130 | are not transpling live and you are limited to the technologies provided. 132 |
133 |
134 |
135 |
136 |

Firing Your First Tile (Sample #1)

137 | 138 |
139 |
140 |

141 | The best way to start creating data on the Ceramic Network is to create it. This is 142 | going to be a step by step guide to get you started. For this we'll be building some 143 | sample blog posts, but you can use any data as long as it's structured as a JSON 144 | object. 145 |

146 |
    147 |
  1. 148 | Let's make sure you're authenticated. In your browser's inspector window open up 149 | the 'console' tab & enter ceramic.did.id. You should see something 150 | like did:3:.... If you don't refresh the page & re-authenticate your 151 | DID. 152 |
  2. 153 |
  3. 154 | Let's start by disecting the following command:
    155 | 156 | const post1 = await TileDocument.create(ceramic, { 157 |
      title:'My first Tile',
      body: 'My first 158 | Tile',
      author: ceramic.did.id
    })

    160 | First, TileDocument.create() is the method to create a new tile. It requires two 161 | arguments, the ceramic client and the tile data. Now you're ready to run the 162 | command and create your tile! 163 |
  4. 164 |
  5. 165 | You might have seen in your console that it returned undefined. This is because 166 | we're creating, not querying the tile. To see the details about it run 167 | post1.content and your browser will show you the content you just 168 | created! 169 |
  6. 170 |
  7. 171 | That's cool, but what happens when you close your browser and your inspector 172 | forgets what we created? That's where the StreamID comes into play. Run 173 | post1.id.toString() to see your StreamID. If you're familiar with 174 | databases this is similar to your primary key. We can then load a stream by 175 | running the following: 176 | TileDocument.load(ceramic, post1.id.toString()). You can copy & paste 177 | the string from the first command in this step but you don't need to. 178 |
  8. 179 |
180 |

181 | That's it! You've now created & loaded your data on Ceramic. You might be wondering 182 | how you can load your data without writing down the StreamID or storing it 183 | somewhere, that's really where the Identity Index protocol comes in handy. 184 |

185 |
186 |
187 |
188 |
189 |

Working with DID-DataStore (Sample #2)

190 | 191 |
192 |
193 |

194 | Now that you've created a tile it's time to work on creating data associated with 195 | your personal DID. We've provided the DID-DataStore that has the BasicProfile schema 196 | already loaded so all we have to do is use it. 197 |

198 |
    199 |
  1. 200 | If you haven't worked with Ceramic or the Web Playground in the past take a look 201 | at "Firing Your First Tile". 202 |
  2. 203 |
  3. 204 | Before we jump into using our DID-DataStore, let's take a look at how it's 205 | configured. You can find the source code 206 | on GitHub. If you take a look you can see that we're providing a 'publishedModel' to our 210 | DID-DataStore, this is a manual way to provide the Schema & Definition to 211 | DID-DataStore, you can also use a DataModel that has been published to NPM for 212 | this to make it easier. 213 |
  4. 214 |
  5. 215 | Now that we've set up the groundwork, we can actually interact with DID-DataStore. 216 | So let's start by querying your profile, all you need to do is run: 217 | await DataStore.get('basicProfile') 218 |
    219 | You might see a JSON response if you've used 220 | Self.ID to create a profile in the past, or you 221 | might just see undefined. Unless you see an error message you're good 222 | to move to the next step. 223 |
  6. 224 |
  7. 225 | Retrieving your profile is fine, but what about another persons profile? You can 226 | easily query this by providing their DID! Let's take a look at my profile. My DID 227 | is: 228 | did:3:bafyreihq3gquqeuzblcpckqoanlftg7zp3wivkvg26mzfiwvau45rrepie and 229 | with that we can run 230 | await DataStore.get('basicProfile', 232 | 'did:3:bafyreihq3gquqeuzblcpckqoanlftg7zp3wivkvg26mzfiwvau45rrepie') 234 | and retrieve my profile, if you were building your own app 235 | ceramic.did.id would reference the DID that is authenticating the 236 | Ceramic instance. 237 |
  8. 238 |
  9. 239 | So, we've done a lot of querying and no creation. We can almost fix that. 240 | First let's talk a bit about best practices. DataStore provides two different 241 | utilities for updating a record. The first should ONLY be used when you're 242 | creating a Tile for the first time, this being DataStore.set(). If 243 | you call this on a record that already has content you will be overwriting all 244 | other data in the tile (the stream will maintain it's history though so not all is 245 | lost). The second option is the recommended way to create & update data on 246 | Ceramic. This is DataStore.merge(), the main difference is that using 247 | merge will only update the fields that we are providing. 248 |
  10. 249 |
  11. 250 | Finally, we can write to our profile. As mentioned above, we want to be sure to 251 | use the right write method and provide good data to use. 252 | await DataStore.merge('basicProfile', {name: 'YOUR NAME HERE', description: 254 | 'Tell us about yourself!', emoji: '✌🏻' } 256 |
  12. 257 |
258 |

259 | That's it! You've now created (or updated) your basicProfile using DID-DataStore, if 260 | you take a look at Self.ID you can see your profile 261 | has already been updated. Welcome to the world of interoperable data, we're happy to 262 | have you here. 263 |

264 |
265 |
266 |
267 |
268 | 277 |
278 | 279 | 280 | 281 | --------------------------------------------------------------------------------