Skip to main content

Library Initialization

Automerge is implemented in Rust and compiled to WebAssembly for use in javascript environments. Unfortunately the way that WebAssembly modules are loaded varies across environments. In some situations this can be handled by your build tool, but in others you may need to manually load the module. This page describes how to load automerge in various environments, and also an escape hatch which should work everywhere.

Common Environments

Node.js

In node you don't need to do anything special as WebAssembly is supported natively, you just import { next as A } from "@automerge/automerge" and you're good to go.

WebPack

If you're building using webpack you need to enable the asyncWebAssembly feature. This is done by adding the following to your webpack.config.js:

{
experiments: {
asyncWebAssembly: true
}
}

Vite

In vite you'll need to add two plugins, vite-plugin-wasm and vite-plugin-top-level-await.

yarn add vite-plugin-wasm vite-plugin-top-level-await

Then in your vite.config.js:

import { defineConfig } from 'vite'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
...
plugins: [wasm(), topLevelAwait()],
...
})

Unbundled (Vanilla) JS

If you'd rather use Automerge outwide of any build processes, you can use something like the following example:

// This approach requires the initializeWasm export not yet found in the stable 1.2.1 release.
import * as AutomergeRepo from "https://esm.sh/@automerge/[email protected]/slim?bundle-deps"
import { IndexedDBStorageAdapter } from "https://esm.sh/@automerge/[email protected]?bundle-deps"
import { BrowserWebSocketClientAdapter } from "https://esm.sh/@automerge/[email protected]?bundle-deps"
import { MessageChannelNetworkAdapter } from "https://esm.sh/@automerge/[email protected]?bundle-deps"

await AutomergeRepo.initializeWasm(
fetch("https://esm.sh/@automerge/automerge/dist/automerge.wasm")
)

// Then set up an automerge repo (loading with our annoying WASM hack)
const repo = new AutomergeRepo.Repo({
storage: new IndexedDBStorageAdapter(),
network: [new BrowserWebSocketClientAdapter("wss://sync.automerge.org")],
})

Note that in some environments you may not have support for top-level await, in which case, you can run the last two statements inside an async function.

Cloudflare Workers

Here you should be good to go by just importing @automerge/automerge as normal.

warning

If you see obscure looking rust stack traces complaining about being unable to create random bytes while constructing a UUID then this is because you are trying to create a document (either a new one, or loading or forking one) outside of a handler. If you run the problematic code in a handler you should be fine.

Deno

If your Deno instance allows access to the filesystem (the default for local development) then you can import Automerge from an npm specifier like so:

import { next as Am } from "npm:@automerge/automerge"

However, if your Deno process doesn't have filesystem permission then you'll need to manually initialize the WebAssembly module. One way of doing that is:

import { automergeWasmBase64 } from "npm:@automerge/automerge";
import { next as Am } from "npm:@automerge/automerge";

await Am.initializeBase64Wasm(automergeWasmBase64);

Val.town

Val.town is a cloud-based Deno execution platform. Here's the text of a simple "val" which returns the contents of the documentId passed via the path.

import { BrowserWebSocketClientAdapter } from "npm:@automerge/automerge-repo-network-websocket";
import { isValidAutomergeUrl, Repo } from "npm:@automerge/automerge-repo/slim";

/* set up Automerge's internal wasm guts manually */
import { automergeWasmBase64 } from "npm:@automerge/automerge/automerge.wasm.base64.js";
import * as automerge from "npm:@automerge/automerge/slim";
await automerge.next.initializeBase64Wasm(automergeWasmBase64);

/* This example will return the contents of a documentID passed in as the path as JSON. */
export default async function(req: Request): Promise<Response> {
const docId = new URL(req.url).pathname.substring(1);

if (!isValidAutomergeUrl("automerge:" + docId)) {
return Response.error();
}

const repo = new Repo({ network: [new BrowserWebSocketClientAdapter("wss://sync.automerge.org")] });
const handle = repo.find(docId);
const contents = await handle.doc();
return Response.json(contents);
}

The escape hatch

If you're in an environment which doesn't support importing WebAssembly modules as ES modules then you need to initialize the WebAssembly manually. There are two parts to this:

  • Change all imports in your application of @automerge/automerge and @automerge/automerge-repo to the "slim" variants (@automerge/automerge/slim and @automerge/automerge-repo/slim)
  • Obtain the WebAssembly module and initialize it manually, then wait for initialization to complete.

For this latter part we expose two exports from the @automerge/automerge package which can be used to obtain the raw WebAssembly. @automerge/automerge/automerge.wasm is a binary version of the WebAssembly file, whilst @automerge/automerge/automerge.wasm.base64.js is a JS modules with a single export called automergeWasmBase64 which is a base64 encoded version of the WebAssembly file.

note

Automerge's npm module uses the package exports feature, which means your environment will need to support that.

For example, React Native requires configuring a metro.config.js to support package exports:

const {getDefaultConfig} = require("expo/metro-config")
const config = getDefaultConfig(__dirname)
config.resolver && (config.resolver.unstable_enablePackageExports = true)
module.exports = config

Once you've obtained the WebAssembly file you initialize it by passing it to either initializeWasm - which expects a WebAssembly module or a URL to fetch - or to initializeBase64Wasm which expects a base64 encoded string.

Using the raw WebAssembly

Here's an example of using the raw WebAssembly in a Vite application. Here we can use the ?url suffix on an import to obtain the URL to an asset.

// Note the ?url suffix
import wasmUrl from "@automerge/automerge/automerge.wasm?url";
// Note the `/slim` suffixes
import { next as Automerge } from "@automerge/automerge/slim";
import { Repo } from "@automerge/automerge-repo/slim";

await next.initializeWasm(wasmUrl)

// Now we can get on with our lives

const repo = new Repo({..})

Using the base64 encoded WebAssembly

Here's an example of using the raw WebAssembly in an application where we can load JavaScript files but nothing else.

import { automergeWasmBase64 } from "@automerge/automerge/automerge.wasm.base64.js";
// Note the `/slim` suffixes
import { next as Automerge } from "@automerge/automerge/slim";
import { Repo } from `@automerge/automerge-repo/slim`;

await next.initializeBase64Wasm(automergeWasmBase64)

// Now we can get on with our lives
const repo = new Repo({..})