UNPKG

@oddjs/odd

Version:
175 lines (163 loc) 34.8 kB
<!DOCTYPE html><html class="default" lang="en"><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>ODD SDK</title><meta name="description" content="Documentation for ODD SDK"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script async src="assets/search.js" id="search-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os"</script><header class="tsd-page-toolbar"> <div class="tsd-toolbar-contents container"> <div class="table-cell" id="tsd-search" data-base="."> <div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M15.7824 13.833L12.6666 10.7177C12.5259 10.5771 12.3353 10.499 12.1353 10.499H11.6259C12.4884 9.39596 13.001 8.00859 13.001 6.49937C13.001 2.90909 10.0914 0 6.50048 0C2.90959 0 0 2.90909 0 6.49937C0 10.0896 2.90959 12.9987 6.50048 12.9987C8.00996 12.9987 9.39756 12.4863 10.5008 11.6239V12.1332C10.5008 12.3332 10.5789 12.5238 10.7195 12.6644L13.8354 15.7797C14.1292 16.0734 14.6042 16.0734 14.8948 15.7797L15.7793 14.8954C16.0731 14.6017 16.0731 14.1267 15.7824 13.833ZM6.50048 10.499C4.29094 10.499 2.50018 8.71165 2.50018 6.49937C2.50018 4.29021 4.28781 2.49976 6.50048 2.49976C8.71001 2.49976 10.5008 4.28708 10.5008 6.49937C10.5008 8.70852 8.71314 10.499 6.50048 10.499Z" fill="var(--color-text)"></path></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div> <div class="field"> <div id="tsd-toolbar-links"></div></div> <ul class="results"> <li class="state loading">Preparing search index...</li> <li class="state failure">The search index is not available</li></ul><a href="index.html" class="title">ODD SDK</a></div> <div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="3" width="14" height="2" fill="var(--color-text)"></rect><rect x="1" y="7" width="14" height="2" fill="var(--color-text)"></rect><rect x="1" y="11" width="14" height="2" fill="var(--color-text)"></rect></svg></a></div></div></header> <div class="container container-main"> <div class="col-8 col-content"> <div class="tsd-page-title"> <h2>ODD SDK</h2></div> <div class="tsd-panel tsd-typography"> <a href="#odd-sdk" id="odd-sdk" style="color: inherit; text-decoration: none;"> <h1>ODD SDK</h1> </a> <p><a href="https://www.npmjs.com/package/@oddjs/odd"><img src="https://img.shields.io/npm/v/@oddjs/odd" alt="NPM"></a> <a href="https://github.com/oddsdk/ts-odd/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License"></a> <a href="https://fission.codes"><img src="https://img.shields.io/badge/%E2%8C%98-Built_by_FISSION-purple.svg" alt="Built by FISSION"></a> <a href="https://discord.gg/zAQBDEq"><img src="https://img.shields.io/discord/478735028319158273.svg" alt="Discord"></a> <a href="https://talk.fission.codes"><img src="https://img.shields.io/discourse/https/talk.fission.codes/topics" alt="Discourse"></a></p> <p>The ODD SDK empowers developers to build fully distributed web applications without needing a complex back-end. The SDK provides:</p> <ul> <li><strong>User accounts</strong> via the browser&#39;s <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API">Web Crypto API</a> or by using a blockchain wallet as a <a href="https://github.com/oddsdk/odd-walletauth">ODD plugin</a>.</li> <li><strong>Authorization</strong> using <a href="https://ucan.xyz/">UCAN</a>.</li> <li><strong>Encrypted file storage</strong> using <a href="https://docs.odd.dev/file-system-wnfs">WNFS</a> backed by <a href="https://ipld.io/">IPLD</a>.</li> <li><strong>Key management</strong> using websockets and a two-factor auth-like flow.</li> </ul> <p>ODD applications work offline and store data encrypted for the user by leveraging the power of the web platform. You can read more about the ODD SDK in Fission&#39;s <a href="https://docs.odd.dev">ODD SDK Guide</a>. There&#39;s also an API reference which can be found at <a href="https://api.odd.dev">api.odd.dev</a></p> <a href="#installation" id="installation" style="color: inherit; text-decoration: none;"> <h1>Installation</h1> </a> <p>The <code>ts-odd</code> package is published on yarn, pnpm and npm as <code>@oddjs/odd</code>:</p> <a href="#npm" id="npm" style="color: inherit; text-decoration: none;"> <h3>npm</h3> </a> <pre><code class="language-bash"><span class="hl-0">npm </span><span class="hl-1">install</span><span class="hl-0"> </span><span class="hl-1">@oddjs/odd</span> </code></pre> <a href="#pnpm" id="pnpm" style="color: inherit; text-decoration: none;"> <h3>pnpm</h3> </a> <pre><code class="language-bash"><span class="hl-0">pnpm </span><span class="hl-1">install</span><span class="hl-0"> </span><span class="hl-1">@oddjs/odd</span> </code></pre> <a href="#yarn" id="yarn" style="color: inherit; text-decoration: none;"> <h3>yarn</h3> </a> <pre><code class="language-bash"><span class="hl-0">yarn </span><span class="hl-1">add</span><span class="hl-0"> </span><span class="hl-1">@oddjs/odd</span> </code></pre> <a href="#getting-started" id="getting-started" style="color: inherit; text-decoration: none;"> <h1>Getting started</h1> </a> <pre><code class="language-ts"><span class="hl-2">// ESM</span><br/><span class="hl-3">import</span><span class="hl-0"> </span><span class="hl-4">*</span><span class="hl-0"> </span><span class="hl-3">as</span><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0"> </span><span class="hl-3">from</span><span class="hl-0"> </span><span class="hl-1">&quot;@oddjs/odd&quot;</span><br/><br/><span class="hl-2">// Browser/UMD build</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">odd</span><span class="hl-0"> = </span><span class="hl-5">globalThis</span><span class="hl-0">.</span><span class="hl-5">oddjs</span> </code></pre> <a href="#creating-a-program" id="creating-a-program" style="color: inherit; text-decoration: none;"> <h2>Creating a Program</h2> </a> <p>An ODD program is an assembly of components that make up a distributed web application. Several of the components can be customized. <em>Let&#39;s stick with the default components for now, which means we&#39;ll be using the Web Crypto API.</em></p> <pre><code class="language-ts"><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">program</span><span class="hl-0"> = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-7">program</span><span class="hl-0">({</span><br/><span class="hl-0"> </span><span class="hl-2">// Can also be a string, used as an identifier for caches.</span><br/><span class="hl-0"> </span><span class="hl-2">// If you&#39;re developing multiple apps on the same localhost port,</span><br/><span class="hl-0"> </span><span class="hl-2">// make sure these differ.</span><br/><span class="hl-0"> </span><span class="hl-5">namespace:</span><span class="hl-0"> { </span><span class="hl-5">creator:</span><span class="hl-0"> </span><span class="hl-1">&quot;Nullsoft&quot;</span><span class="hl-0">, </span><span class="hl-5">name:</span><span class="hl-0"> </span><span class="hl-1">&quot;Winamp&quot;</span><span class="hl-0"> }</span><br/><br/><span class="hl-0">}).</span><span class="hl-7">catch</span><span class="hl-0">(</span><span class="hl-5">error</span><span class="hl-0"> </span><span class="hl-4">=&gt;</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-3">switch</span><span class="hl-0"> (</span><span class="hl-5">error</span><span class="hl-0">) {</span><br/><span class="hl-0"> </span><span class="hl-3">case</span><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-5">ProgramError</span><span class="hl-0">.</span><span class="hl-5">InsecureContext</span><span class="hl-0">:</span><br/><span class="hl-0"> </span><span class="hl-2">// The ODD SDK requires HTTPS</span><br/><span class="hl-0"> </span><span class="hl-3">break</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-3">case</span><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-5">ProgramError</span><span class="hl-0">.</span><span class="hl-5">UnsupportedBrowser</span><span class="hl-0">:</span><br/><span class="hl-0"> </span><span class="hl-3">break</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0">})</span> </code></pre> <p><code>odd.program</code> returns a <code>Program</code> object, which can create a new user session or reuse an existing session. There are two ways to create a user session, either by using an authentication strategy or by requesting access from another app through the &quot;capabilities&quot; system. Let&#39;s start with the default authentication strategy.</p> <pre><code class="language-ts"><span class="hl-4">let</span><span class="hl-0"> </span><span class="hl-5">session</span><br/><br/><span class="hl-2">// Do we have an existing session?</span><br/><span class="hl-3">if</span><span class="hl-0"> (</span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">session</span><span class="hl-0">) {</span><br/><span class="hl-0"> </span><span class="hl-5">session</span><span class="hl-0"> = </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">session</span><br/><br/><span class="hl-2">// If not, let&#39;s authenticate.</span><br/><span class="hl-2">// (a) new user, register a new Fission account</span><br/><span class="hl-0">} </span><span class="hl-3">else</span><span class="hl-0"> </span><span class="hl-3">if</span><span class="hl-0"> (</span><span class="hl-5">userChoseToRegister</span><span class="hl-0">) {</span><br/><span class="hl-0"> </span><span class="hl-4">const</span><span class="hl-0"> { </span><span class="hl-6">success</span><span class="hl-0"> } = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">auth</span><span class="hl-0">.</span><span class="hl-7">register</span><span class="hl-0">({ </span><span class="hl-5">username:</span><span class="hl-0"> </span><span class="hl-1">&quot;llama&quot;</span><span class="hl-0"> })</span><br/><span class="hl-0"> </span><span class="hl-5">session</span><span class="hl-0"> = </span><span class="hl-5">success</span><span class="hl-0"> ? </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">auth</span><span class="hl-0">.</span><span class="hl-7">session</span><span class="hl-0">() : </span><span class="hl-4">null</span><br/><br/><span class="hl-2">// (b) existing user, link a new device</span><br/><span class="hl-0">} </span><span class="hl-3">else</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-2">// On device with existing session:</span><br/><span class="hl-0"> </span><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">producer</span><span class="hl-0"> = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">auth</span><span class="hl-0">.</span><span class="hl-7">accountProducer</span><span class="hl-0">(</span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">session</span><span class="hl-0">.</span><span class="hl-5">username</span><span class="hl-0">)</span><br/><br/><span class="hl-0"> </span><span class="hl-5">producer</span><span class="hl-0">.</span><span class="hl-7">on</span><span class="hl-0">(</span><span class="hl-1">&quot;challenge&quot;</span><span class="hl-0">, </span><span class="hl-5">challenge</span><span class="hl-0"> </span><span class="hl-4">=&gt;</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-2">// Either show `challenge.pin` or have the user input a PIN and see if they&#39;re equal.</span><br/><span class="hl-0"> </span><span class="hl-3">if</span><span class="hl-0"> (</span><span class="hl-5">userInput</span><span class="hl-0"> === </span><span class="hl-5">challenge</span><span class="hl-0">.</span><span class="hl-5">pin</span><span class="hl-0">) </span><span class="hl-5">challenge</span><span class="hl-0">.</span><span class="hl-7">confirmPin</span><span class="hl-0">()</span><br/><span class="hl-0"> </span><span class="hl-3">else</span><span class="hl-0"> </span><span class="hl-5">challenge</span><span class="hl-0">.</span><span class="hl-7">rejectPin</span><span class="hl-0">()</span><br/><span class="hl-0"> })</span><br/><br/><span class="hl-0"> </span><span class="hl-5">producer</span><span class="hl-0">.</span><span class="hl-7">on</span><span class="hl-0">(</span><span class="hl-1">&quot;link&quot;</span><span class="hl-0">, ({ </span><span class="hl-5">approved</span><span class="hl-0"> }) </span><span class="hl-4">=&gt;</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-3">if</span><span class="hl-0"> (</span><span class="hl-5">approved</span><span class="hl-0">) </span><span class="hl-5">console</span><span class="hl-0">.</span><span class="hl-7">log</span><span class="hl-0">(</span><span class="hl-1">&quot;Link device successfully&quot;</span><span class="hl-0">)</span><br/><span class="hl-0"> })</span><br/><br/><span class="hl-0"> </span><span class="hl-2">// On device without session:</span><br/><span class="hl-0"> </span><span class="hl-2">// Somehow you&#39;ll need to get ahold of the username.</span><br/><span class="hl-0"> </span><span class="hl-2">// Few ideas: URL query param, QR code, manual input.</span><br/><span class="hl-0"> </span><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">consumer</span><span class="hl-0"> = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">auth</span><span class="hl-0">.</span><span class="hl-7">accountConsumer</span><span class="hl-0">(</span><span class="hl-5">username</span><span class="hl-0">)</span><br/><br/><span class="hl-0"> </span><span class="hl-5">consumer</span><span class="hl-0">.</span><span class="hl-7">on</span><span class="hl-0">(</span><span class="hl-1">&quot;challenge&quot;</span><span class="hl-0">, ({ </span><span class="hl-5">pin</span><span class="hl-0"> }) </span><span class="hl-4">=&gt;</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-7">showPinOnUI</span><span class="hl-0">(</span><span class="hl-5">pin</span><span class="hl-0">)</span><br/><span class="hl-0"> })</span><br/><br/><span class="hl-0"> </span><span class="hl-5">consumer</span><span class="hl-0">.</span><span class="hl-7">on</span><span class="hl-0">(</span><span class="hl-1">&quot;link&quot;</span><span class="hl-0">, </span><span class="hl-4">async</span><span class="hl-0"> ({ </span><span class="hl-5">approved</span><span class="hl-0">, </span><span class="hl-5">username</span><span class="hl-0"> }) </span><span class="hl-4">=&gt;</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-3">if</span><span class="hl-0"> (</span><span class="hl-5">approved</span><span class="hl-0">) {</span><br/><span class="hl-0"> </span><span class="hl-5">console</span><span class="hl-0">.</span><span class="hl-7">log</span><span class="hl-0">(</span><span class="hl-1">`Successfully authenticated as </span><span class="hl-4">${</span><span class="hl-5">username</span><span class="hl-4">}</span><span class="hl-1">`</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-5">session</span><span class="hl-0"> = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">auth</span><span class="hl-0">.</span><span class="hl-7">session</span><span class="hl-0">()</span><br/><span class="hl-0"> }</span><br/><span class="hl-0"> })</span><br/><span class="hl-0">}</span> </code></pre> <p>Alternatively you can use the &quot;capabilities&quot; system when you want partial access to a file system. At the moment of writing, capabilities are only supported through the &quot;Fission auth lobby&quot;, which is an ODD app that uses the auth strategy shown above.</p> <p>This Fission auth lobby flow works as follows:</p> <ol> <li>You get redirected to the Fission lobby from your app.</li> <li>Here you create an account like in the normal auth strategy flow shown above.</li> <li>The lobby shows what your app wants to access in your file system.</li> <li>You approve or deny these permissions and get redirected back to your app.</li> <li>Your app collects the encrypted information (UCANs &amp; file system secrets).</li> <li>Your app can create a user session.</li> </ol> <pre><code class="language-ts"><span class="hl-2">// We define a `Permissions` object,</span><br/><span class="hl-2">// this represents what permissions to ask the user.</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">permissions</span><span class="hl-0"> = {</span><br/><span class="hl-0"> </span><span class="hl-2">// Ask permission to write to and read from the directory:</span><br/><span class="hl-0"> </span><span class="hl-2">// private/Apps/Nullsoft/Winamp</span><br/><span class="hl-0"> </span><span class="hl-5">app:</span><span class="hl-0"> { </span><span class="hl-5">creator:</span><span class="hl-0"> </span><span class="hl-1">&quot;Nullsoft&quot;</span><span class="hl-0">, </span><span class="hl-5">name:</span><span class="hl-0"> </span><span class="hl-1">&quot;Winamp&quot;</span><span class="hl-0"> }</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-2">// We need to pass this object to our program</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">program</span><span class="hl-0"> = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-7">program</span><span class="hl-0">({</span><br/><span class="hl-0"> </span><span class="hl-5">namespace:</span><span class="hl-0"> { </span><span class="hl-5">creator:</span><span class="hl-0"> </span><span class="hl-1">&quot;Nullsoft&quot;</span><span class="hl-0">, </span><span class="hl-5">name:</span><span class="hl-0"> </span><span class="hl-1">&quot;Winamp&quot;</span><span class="hl-0"> },</span><br/><span class="hl-0"> </span><span class="hl-5">permissions</span><br/><span class="hl-0">})</span><br/><br/><span class="hl-2">// (a) Whenever you are ready to redirect to the lobby, call this:</span><br/><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">capabilities</span><span class="hl-0">.</span><span class="hl-7">request</span><span class="hl-0">()</span><br/><br/><span class="hl-2">// (b) When you get redirected back and your program is ready,</span><br/><span class="hl-2">// you will have access to your user session.</span><br/><span class="hl-5">session</span><span class="hl-0"> = </span><span class="hl-5">program</span><span class="hl-0">.</span><span class="hl-5">session</span> </code></pre> <p>Once you have your <code>Session</code>, you have access to your file system 🎉</p> <pre><code class="language-ts"><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">fs</span><span class="hl-0"> = </span><span class="hl-5">session</span><span class="hl-0">.</span><span class="hl-5">fs</span> </code></pre> <p><strong>Notes:</strong></p> <ul> <li>You can use alternative authentication strategies, such as <a href="https://github.com/oddsdk/odd-walletauth">odd-walletauth</a>.</li> <li>You can remove all traces of the user using <code>await session.destroy()</code></li> <li>You can load the file system separately if you&#39;re using a web worker. This is done using the combination of <code>configuration.fileSystem.loadImmediately = false</code> and <code>program.fileSystem.load()</code></li> <li>You can recover a file system if you&#39;ve downloaded a Recovery Kit by calling <code>program.fileSystem.recover({ newUsername, oldUsername, readKey })</code>. The <code>oldUsername</code> and <code>readKey</code> can be parsed from the uploaded Recovery Kit and the <code>newUsername</code> can be generated before calling the function. Please refer to <a href="https://github.com/oddsdk/odd-app-template/blob/5498e7062a4578028b8b55d2ac4c611bd5daab85/src/components/auth/recover/HasRecoveryKit.svelte#L49">this example</a> from Fission&#39;s ODD App Template. Additionally, if you would like to see how to generate a Recovery Kit, you can reference <a href="https://github.com/oddsdk/odd-app-template/blob/main/src/lib/account-settings.ts#L186">this example</a></li> </ul> <a href="#working-with-the-file-system" id="working-with-the-file-system" style="color: inherit; text-decoration: none;"> <h2>Working with the file system</h2> </a> <p>The Webnative File System (WNFS) is a file system built on top of <a href="https://ipld.io/">IPLD</a>. It supports operations similar to your macOS, Windows, or Linux desktop file system. It consists of a public and private branch: The public branch is &quot;live&quot; and publicly accessible on the Internet. The private branch is encrypted so that only the owner can see the contents. Read more about it <a href="https://github.com/wnfs-wg">here</a>.</p> <pre><code class="language-ts"><span class="hl-4">const</span><span class="hl-0"> { </span><span class="hl-6">Branch</span><span class="hl-0"> } = </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-5">path</span><br/><br/><span class="hl-2">// List the user&#39;s private files</span><br/><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-0">.</span><span class="hl-7">ls</span><span class="hl-0">(</span><br/><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-5">path</span><span class="hl-0">.</span><span class="hl-7">directory</span><span class="hl-0">(</span><span class="hl-5">Branch</span><span class="hl-0">.</span><span class="hl-5">Private</span><span class="hl-0">)</span><br/><span class="hl-0">)</span><br/><br/><span class="hl-2">// Create a sub directory and add some content</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">contentPath</span><span class="hl-0"> = </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-7">file</span><span class="hl-0">(</span><br/><span class="hl-0"> </span><span class="hl-5">Branch</span><span class="hl-0">.</span><span class="hl-5">Private</span><span class="hl-0">, </span><span class="hl-1">&quot;Sub Directory&quot;</span><span class="hl-0">, </span><span class="hl-1">&quot;hello.txt&quot;</span><br/><span class="hl-0">)</span><br/><br/><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-0">.</span><span class="hl-7">write</span><span class="hl-0">(</span><br/><span class="hl-0"> </span><span class="hl-5">contentPath</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-4">new</span><span class="hl-0"> </span><span class="hl-7">TextEncoder</span><span class="hl-0">().</span><span class="hl-7">encode</span><span class="hl-0">(</span><span class="hl-1">&quot;👋&quot;</span><span class="hl-0">) </span><span class="hl-2">// Uint8Array</span><br/><span class="hl-0">)</span><br/><br/><span class="hl-2">// Persist changes and announce them to your other devices</span><br/><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-0">.</span><span class="hl-7">publish</span><span class="hl-0">()</span><br/><br/><span class="hl-2">// Read the file</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">content</span><span class="hl-0"> = </span><span class="hl-4">new</span><span class="hl-0"> </span><span class="hl-7">TextDecoder</span><span class="hl-0">().</span><span class="hl-7">decode</span><span class="hl-0">(</span><br/><span class="hl-0"> </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-0">.</span><span class="hl-7">read</span><span class="hl-0">(</span><span class="hl-5">contentPath</span><span class="hl-0">)</span><br/><span class="hl-0">)</span> </code></pre> <p>That&#39;s it, you have successfully created an ODD app! 🚀</p> <a href="#posix-interface" id="posix-interface" style="color: inherit; text-decoration: none;"> <h2>POSIX Interface</h2> </a> <p>WNFS exposes a familiar POSIX-style interface:</p> <ul> <li><code>exists</code>: check if a file or directory exists</li> <li><code>ls</code>: list a directory</li> <li><code>mkdir</code>: create a directory</li> <li><code>mv</code>: move a file or directory</li> <li><code>read</code>: read from a file</li> <li><code>rm</code>: remove a file or directory</li> <li><code>write</code>: write to a file</li> </ul> <a href="#versioning" id="versioning" style="color: inherit; text-decoration: none;"> <h2>Versioning</h2> </a> <p>Each file and directory has a <code>history</code> property, which you can use to get an earlier version of that item. We use the <code>delta</code> variable as the order index. Primarily because the timestamps can be slightly out of sequence, due to device inconsistencies.</p> <pre><code class="language-ts"><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">file</span><span class="hl-0"> = </span><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-0">.</span><span class="hl-7">get</span><span class="hl-0">(</span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-5">path</span><span class="hl-0">.</span><span class="hl-7">file</span><span class="hl-0">(</span><span class="hl-1">&quot;private&quot;</span><span class="hl-0">, </span><span class="hl-1">&quot;Blog Posts&quot;</span><span class="hl-0">, </span><span class="hl-1">&quot;article.md&quot;</span><span class="hl-0">))</span><br/><br/><span class="hl-5">file</span><span class="hl-0">.</span><span class="hl-5">history</span><span class="hl-0">.</span><span class="hl-7">list</span><span class="hl-0">()</span><br/><span class="hl-2">// { delta: -1, timestamp: 1606236743 }</span><br/><span class="hl-2">// { delta: -2, timestamp: 1606236532 }</span><br/><br/><span class="hl-2">// Get the previous version</span><br/><span class="hl-5">file</span><span class="hl-0">.</span><span class="hl-5">history</span><span class="hl-0">.</span><span class="hl-7">back</span><span class="hl-0">()</span><br/><br/><span class="hl-2">// Go back two versions</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">delta</span><span class="hl-0"> = -</span><span class="hl-8">2</span><br/><span class="hl-5">file</span><span class="hl-0">.</span><span class="hl-5">history</span><span class="hl-0">.</span><span class="hl-7">back</span><span class="hl-0">(</span><span class="hl-5">delta</span><span class="hl-0">)</span><br/><br/><span class="hl-2">// Get the first version (ie. delta -2)</span><br/><span class="hl-2">// by providing a timestamp</span><br/><span class="hl-5">file</span><span class="hl-0">.</span><span class="hl-5">history</span><span class="hl-0">.</span><span class="hl-7">prior</span><span class="hl-0">(</span><span class="hl-8">1606236743</span><span class="hl-0">)</span> </code></pre> <a href="#sharing-private-data" id="sharing-private-data" style="color: inherit; text-decoration: none;"> <h2>Sharing Private Data</h2> </a> <p><a href="https://docs.odd.dev/sharing-private-data">https://docs.odd.dev/sharing-private-data</a></p> <a href="#migration" id="migration" style="color: inherit; text-decoration: none;"> <h2>Migration</h2> </a> <p>Some versions of the ODD SDK require apps to migrate their codebase to address breaking changes. Please see our <a href="https://docs.odd.dev/developers/odd/migration">migration guide</a> for help migrating your apps to the latest ODD SDK version.</p> <a href="#debugging" id="debugging" style="color: inherit; text-decoration: none;"> <h2>Debugging</h2> </a> <p>Debugging mode can be enable by setting <code>debug</code> to <code>true</code> in your configuration object that you pass to your <code>Program</code>. By default this will add your programs to the global context object (eg. <code>window</code>) under <code>globalThis.__odd.programs</code> (can be disabled, see API docs).</p> <pre><code class="language-ts"><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">appInfo</span><span class="hl-0"> = { </span><span class="hl-5">creator:</span><span class="hl-0"> </span><span class="hl-1">&quot;Nullsoft&quot;</span><span class="hl-0">, </span><span class="hl-5">name:</span><span class="hl-0"> </span><span class="hl-1">&quot;Winamp&quot;</span><span class="hl-0"> }</span><br/><br/><span class="hl-3">await</span><span class="hl-0"> </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-7">program</span><span class="hl-0">({</span><br/><span class="hl-0"> </span><span class="hl-5">namespace:</span><span class="hl-0"> </span><span class="hl-5">appInfo</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-5">debug:</span><span class="hl-0"> </span><span class="hl-4">true</span><br/><span class="hl-0">})</span><br/><br/><span class="hl-2">// Automatically exposed Program in debug mode</span><br/><span class="hl-4">const</span><span class="hl-0"> </span><span class="hl-6">program</span><span class="hl-0"> = </span><span class="hl-5">globalThis</span><span class="hl-0">.</span><span class="hl-5">__odd</span><span class="hl-0">[ </span><span class="hl-5">odd</span><span class="hl-0">.</span><span class="hl-7">namespace</span><span class="hl-0">(</span><span class="hl-5">appInfo</span><span class="hl-0">) ] </span><span class="hl-2">// namespace: &quot;Nullsoft/Winamp&quot;</span> </code></pre> </div></div> <div class="col-4 col-menu menu-sticky-wrap menu-highlight"> <div class="tsd-navigation settings"> <details class="tsd-index-accordion"><summary class="tsd-accordion-summary"> <h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M4.93896 8.531L12 15.591L19.061 8.531L16.939 6.409L12 11.349L7.06098 6.409L4.93896 8.531Z" fill="var(--color-text)"></path></svg> Settings</h3></summary> <div class="tsd-accordion-details"> <div class="tsd-filter-visibility"> <h4 class="uppercase">Member Visibility</h4><form> <ul id="tsd-filter-options"> <li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li> <li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-private" name="private"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Private</span></label></li> <li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></form></div> <div class="tsd-theme-toggle"> <h4 class="uppercase">Theme</h4><select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div> <nav class="tsd-navigation primary"> <details class="tsd-index-accordion" open><summary class="tsd-accordion-summary"> <h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M4.93896 8.531L12 15.591L19.061 8.531L16.939 6.409L12 11.349L7.06098 6.409L4.93896 8.531Z" fill="var(--color-text)"></path></svg> Modules</h3></summary> <div class="tsd-accordion-details"> <ul> <li class="current selected"><a href="modules.html">ODD SDK</a> <ul> <li class="tsd-kind-module"><a href="modules/_internal_.html">&lt;internal&gt;</a></li> <li class="tsd-kind-namespace"><a href="modules/Auth.html">Auth</a></li> <li class="tsd-kind-namespace"><a href="modules/Capabilities.html">Capabilities</a></li> <li class="tsd-kind-namespace"><a href="modules/Crypto.html">Crypto</a></li> <li class="tsd-kind-namespace"><a href="modules/Depot.html">Depot</a></li> <li class="tsd-kind-namespace"><a href="modules/Manners.html">Manners</a></li> <li class="tsd-kind-namespace"><a href="modules/Reference.html">Reference</a></li> <li class="tsd-kind-namespace"><a href="modules/Storage.html">Storage</a></li> <li class="tsd-kind-namespace"><a href="modules/apps.html">apps</a></li> <li class="tsd-kind-namespace"><a href="modules/did.html">did</a></li> <li class="tsd-kind-namespace"><a href="modules/fission.html">fission</a></li> <li class="tsd-kind-namespace"><a href="modules/path.html">path</a></li> <li class="tsd-kind-namespace"><a href="modules/ucan.html">ucan</a></li></ul></li></ul></div></details></nav></div></div> <div class="container tsd-generator"> <p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div> <div class="overlay"></div><script src="assets/main.js"></script></body></html>