Compare commits
10 commits
main
...
revert-434
Author | SHA1 | Date | |
---|---|---|---|
|
cc3757f9fd | ||
|
824fa0a99a | ||
|
7cb2ec6e0e | ||
|
648a62b71c | ||
|
a360cf44cc | ||
|
b2888f2871 | ||
|
55e4a53f20 | ||
|
de25e671aa | ||
|
e4a3b26eb8 | ||
|
cfd2c95519 |
71 changed files with 530 additions and 12712 deletions
|
@ -11,11 +11,7 @@ resolver = "2"
|
|||
[profile.release]
|
||||
debug = true
|
||||
lto = true
|
||||
opt-level = 'z'
|
||||
opt-level = 3
|
||||
|
||||
[profile.bench]
|
||||
debug = true
|
||||
|
||||
[profile.release.package.automerge-wasm]
|
||||
debug = false
|
||||
opt-level = 'z'
|
||||
|
|
|
@ -1,18 +1,25 @@
|
|||
## Automerge
|
||||
## Automerge JS
|
||||
|
||||
Automerge is a library of data structures for building collaborative
|
||||
applications, this package is the javascript implementation.
|
||||
This is a reimplementation of Automerge as a JavaScript wrapper around the "automerge-wasm".
|
||||
|
||||
Please see [automerge.org](http://automerge.org/) for documentation.
|
||||
This package is in alpha and feedback in welcome.
|
||||
|
||||
## Setup
|
||||
The primary differences between using this package and "automerge" are as follows:
|
||||
|
||||
This package is a wrapper around a core library which is written in rust and
|
||||
compiled to WASM. In `node` this should be transparent to you, but in the
|
||||
browser you will need a bundler to include the WASM blob as part of your module
|
||||
hierarchy. There are examples of doing this with common bundlers in `./examples`.
|
||||
1. The low level api needs to plugged in via the use function. The only current implementation of "automerge-wasm" but another could used in theory.
|
||||
|
||||
## Meta
|
||||
```javascript
|
||||
import * as Automerge from "automerge-js";
|
||||
import * as wasm_api from "automerge-wasm";
|
||||
|
||||
Copyright 2017–2021, the Automerge contributors. Released under the terms of the
|
||||
MIT license (see `LICENSE`).
|
||||
// browsers require an async wasm load - see automerge-wasm docs
|
||||
Automerge.use(wasm_api);
|
||||
```
|
||||
|
||||
2. There is no front-end back-end split, and no patch format or patch observer. These concepts don't make sense with the wasm implementation.
|
||||
|
||||
3. The basic `Doc<T>` object is now a Proxy object and will behave differently in a repl environment.
|
||||
|
||||
4. The 'Text' class is currently very slow and needs to be re-worked.
|
||||
|
||||
Beyond this please refer to the Automerge [README](http://github.com/automerge/automerge/) for further information.
|
||||
|
|
3
automerge-js/e2e/.gitignore
vendored
3
automerge-js/e2e/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
node_modules/
|
||||
verdacciodb/
|
||||
htpasswd
|
|
@ -1,71 +0,0 @@
|
|||
#End to end testing for javascript packaging
|
||||
|
||||
The network of packages and bundlers we rely on to get the `automerge` package
|
||||
working is a little complex. We have the `automerge-wasm` package, which the
|
||||
`automerge` package depends upon, which means that anyone who depends on
|
||||
`automerge` needs to either a) be using node or b) use a bundler in order to
|
||||
load the underlying WASM module which is packaged in `automerge-wasm`.
|
||||
|
||||
The various bundlers involved are complicated and capricious and so we need an
|
||||
easy way of testing that everything is in fact working as expected. To do this
|
||||
we run a custom NPM registry (namely [Verdaccio](https://verdaccio.org/)) and
|
||||
build the `automerge-wasm` and `automerge` packages and publish them to this
|
||||
registry. Once we have this registry running we are able to build the example
|
||||
projects which depend on these packages and check that everything works as
|
||||
expected.
|
||||
|
||||
## Usage
|
||||
|
||||
First, install everything:
|
||||
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build `automerge-js`
|
||||
|
||||
This builds the `automerge-wasm` package and then runs `yarn build` in the
|
||||
`automerge-js` project with the `--registry` set to the verdaccio registry. The
|
||||
end result is that you can run `yarn test` in the resulting `automerge-js`
|
||||
directory in order to run tests against the current `automerge-wasm`.
|
||||
|
||||
```
|
||||
yarn e2e buildjs
|
||||
```
|
||||
|
||||
### Build examples
|
||||
|
||||
This either builds or the examples in `automerge-js/examples` or just a subset
|
||||
of them. Once this is complete you can run the relevant scripts (e.g. `vite dev`
|
||||
for the Vite example) to check everything works.
|
||||
|
||||
```
|
||||
yarn e2e buildexamples
|
||||
```
|
||||
|
||||
Or, to just build the webpack example
|
||||
|
||||
```
|
||||
yarn e2e buildexamples -e webpack
|
||||
```
|
||||
|
||||
### Run Registry
|
||||
|
||||
If you're experimenting with a project which is not in the `examples` folder
|
||||
you'll need a running registry. `run-registry` builds and publishes
|
||||
`automerge-js` and `automerge-wasm` and then runs the registry at
|
||||
`localhost:4873`.
|
||||
|
||||
```
|
||||
yarn e2e run-registry
|
||||
```
|
||||
|
||||
You can now run `yarn install --registry http://localhost:4873` to experiment
|
||||
with the built packages.
|
||||
|
||||
|
||||
## Using the `dev` build of `automerge-wasm`
|
||||
|
||||
All the commands above take a `-p` flag which can be either `release` or
|
||||
`debug`. The `debug` builds with additional debug symbols which makes errors
|
||||
less cryptic.
|
|
@ -1,438 +0,0 @@
|
|||
import {once} from "events"
|
||||
import {setTimeout} from "timers/promises"
|
||||
import {spawn, ChildProcess} from "child_process"
|
||||
import * as child_process from "child_process"
|
||||
import {command, subcommands, run, array, multioption, option, Type} from "cmd-ts"
|
||||
import * as path from "path"
|
||||
import * as fsPromises from "fs/promises"
|
||||
import fetch from "node-fetch"
|
||||
|
||||
const VERDACCIO_DB_PATH = path.normalize(`${__dirname}/verdacciodb`)
|
||||
const VERDACCIO_CONFIG_PATH = path.normalize(`${__dirname}/verdaccio.yaml`)
|
||||
const AUTOMERGE_WASM_PATH = path.normalize(`${__dirname}/../../automerge-wasm`)
|
||||
const AUTOMERGE_JS_PATH = path.normalize(`${__dirname}/..`)
|
||||
const EXAMPLES_DIR = path.normalize(path.join(__dirname, "../", "examples"))
|
||||
|
||||
// The different example projects in "../examples"
|
||||
type Example = "webpack" | "vite" | "create-react-app"
|
||||
|
||||
// Type to parse strings to `Example` so the types line up for the `buildExamples` commmand
|
||||
const ReadExample: Type<string, Example> = {
|
||||
async from(str) {
|
||||
if (str === "webpack") {
|
||||
return "webpack"
|
||||
} else if (str === "vite") {
|
||||
return "vite"
|
||||
} else if (str === "create-react-app") {
|
||||
return "create-react-app"
|
||||
} else {
|
||||
throw new Error(`Unknown example type ${str}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Profile = "dev" | "release"
|
||||
|
||||
const ReadProfile: Type<string, Profile> = {
|
||||
async from(str) {
|
||||
if (str === "dev") {
|
||||
return "dev"
|
||||
} else if (str === "release") {
|
||||
return "release"
|
||||
} else {
|
||||
throw new Error(`Unknown profile ${str}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const buildjs = command({
|
||||
name: "buildjs",
|
||||
args: {
|
||||
profile: option({
|
||||
type: ReadProfile,
|
||||
long: "profile",
|
||||
short: "p",
|
||||
defaultValue: () => "dev" as Profile
|
||||
})
|
||||
},
|
||||
handler: ({profile}) => {
|
||||
console.log("building js")
|
||||
withPublishedWasm(profile, async (registryUrl: string) => {
|
||||
await buildAndPublishAutomergeJs(registryUrl)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const buildWasm = command({
|
||||
name: "buildwasm",
|
||||
args: {
|
||||
profile: option({
|
||||
type: ReadProfile,
|
||||
long: "profile",
|
||||
short: "p",
|
||||
defaultValue: () => "dev" as Profile
|
||||
})
|
||||
},
|
||||
handler: ({profile}) => {
|
||||
console.log("building automerge-wasm")
|
||||
withRegistry(
|
||||
buildAutomergeWasm(profile),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const buildexamples = command({
|
||||
name: "buildexamples",
|
||||
args: {
|
||||
examples: multioption({
|
||||
long: "example",
|
||||
short: "e",
|
||||
type: array(ReadExample),
|
||||
}),
|
||||
profile: option({
|
||||
type: ReadProfile,
|
||||
long: "profile",
|
||||
short: "p",
|
||||
defaultValue: () => "dev" as Profile
|
||||
})
|
||||
},
|
||||
handler: ({examples, profile}) => {
|
||||
if (examples.length === 0) {
|
||||
examples = ["webpack", "vite", "create-react-app"]
|
||||
}
|
||||
buildExamples(examples, profile)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const runRegistry = command({
|
||||
name: "run-registry",
|
||||
args: {
|
||||
profile: option({
|
||||
type: ReadProfile,
|
||||
long: "profile",
|
||||
short: "p",
|
||||
defaultValue: () => "dev" as Profile
|
||||
})
|
||||
},
|
||||
handler: ({profile}) => {
|
||||
withPublishedWasm(profile, async (registryUrl: string) => {
|
||||
await buildAndPublishAutomergeJs(registryUrl)
|
||||
console.log("\n************************")
|
||||
console.log(` Verdaccio NPM registry is running at ${registryUrl}`)
|
||||
console.log(" press CTRL-C to exit ")
|
||||
console.log("************************")
|
||||
await once(process, "SIGINT")
|
||||
}).catch(e => {
|
||||
console.error(`Failed: ${e}`)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const app = subcommands({
|
||||
name: "e2e",
|
||||
cmds: {buildjs, buildexamples, buildwasm: buildWasm, "run-registry": runRegistry}
|
||||
})
|
||||
|
||||
run(app, process.argv.slice(2))
|
||||
|
||||
async function buildExamples(examples: Array<Example>, profile: Profile) {
|
||||
await withPublishedWasm(profile, async (registryUrl) => {
|
||||
printHeader("building and publishing automerge")
|
||||
await buildAndPublishAutomergeJs(registryUrl)
|
||||
for (const example of examples) {
|
||||
printHeader(`building ${example} example`)
|
||||
if (example === "webpack") {
|
||||
const projectPath = path.join(EXAMPLES_DIR, example)
|
||||
await removeExistingAutomerge(projectPath)
|
||||
await fsPromises.rm(path.join(projectPath, "yarn.lock"), {force: true})
|
||||
await spawnAndWait("yarn", ["--cwd", projectPath, "install", "--registry", registryUrl, "--check-files"], {stdio: "inherit"})
|
||||
await spawnAndWait("yarn", ["--cwd", projectPath, "build"], {stdio: "inherit"})
|
||||
} else if (example === "vite") {
|
||||
const projectPath = path.join(EXAMPLES_DIR, example)
|
||||
await removeExistingAutomerge(projectPath)
|
||||
await fsPromises.rm(path.join(projectPath, "yarn.lock"), {force: true})
|
||||
await spawnAndWait("yarn", ["--cwd", projectPath, "install", "--registry", registryUrl, "--check-files"], {stdio: "inherit"})
|
||||
await spawnAndWait("yarn", ["--cwd", projectPath, "build"], {stdio: "inherit"})
|
||||
} else if (example === "create-react-app") {
|
||||
const projectPath = path.join(EXAMPLES_DIR, example)
|
||||
await removeExistingAutomerge(projectPath)
|
||||
await fsPromises.rm(path.join(projectPath, "yarn.lock"), {force: true})
|
||||
await spawnAndWait("yarn", ["--cwd", projectPath, "install", "--registry", registryUrl, "--check-files"], {stdio: "inherit"})
|
||||
await spawnAndWait("yarn", ["--cwd", projectPath, "build"], {stdio: "inherit"})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type WithRegistryAction = (registryUrl: string) => Promise<void>
|
||||
|
||||
async function withRegistry(action: WithRegistryAction, ...actions: Array<WithRegistryAction>) {
|
||||
// First, start verdaccio
|
||||
printHeader("Starting verdaccio NPM server")
|
||||
const verd = await VerdaccioProcess.start()
|
||||
actions.unshift(action)
|
||||
|
||||
for (const action of actions) {
|
||||
try {
|
||||
type Step = "verd-died" | "action-completed"
|
||||
const verdDied: () => Promise<Step> = async () => {
|
||||
await verd.died()
|
||||
return "verd-died"
|
||||
}
|
||||
const actionComplete: () => Promise<Step> = async () => {
|
||||
await action("http://localhost:4873")
|
||||
return "action-completed"
|
||||
}
|
||||
const result = await Promise.race([verdDied(), actionComplete()])
|
||||
if (result === "verd-died") {
|
||||
throw new Error("verdaccio unexpectedly exited")
|
||||
}
|
||||
} catch(e) {
|
||||
await verd.kill()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
await verd.kill()
|
||||
}
|
||||
|
||||
async function withPublishedWasm(profile: Profile, action: WithRegistryAction) {
|
||||
await withRegistry(
|
||||
buildAutomergeWasm(profile),
|
||||
publishAutomergeWasm,
|
||||
action
|
||||
)
|
||||
}
|
||||
|
||||
function buildAutomergeWasm(profile: Profile): WithRegistryAction {
|
||||
return async (registryUrl: string) => {
|
||||
printHeader("building automerge-wasm")
|
||||
await spawnAndWait("yarn", ["--cwd", AUTOMERGE_WASM_PATH, "--registry", registryUrl, "install"], {stdio: "inherit"})
|
||||
const cmd = profile === "release" ? "release" : "debug"
|
||||
await spawnAndWait("yarn", ["--cwd", AUTOMERGE_WASM_PATH, cmd], {stdio: "inherit"})
|
||||
}
|
||||
}
|
||||
|
||||
async function publishAutomergeWasm(registryUrl: string) {
|
||||
printHeader("Publishing automerge-wasm to verdaccio")
|
||||
await fsPromises.rm(path.join(VERDACCIO_DB_PATH, "automerge-wasm"), { recursive: true, force: true} )
|
||||
await yarnPublish(registryUrl, AUTOMERGE_WASM_PATH)
|
||||
}
|
||||
|
||||
async function buildAndPublishAutomergeJs(registryUrl: string) {
|
||||
// Build the js package
|
||||
printHeader("Building automerge")
|
||||
await removeExistingAutomerge(AUTOMERGE_JS_PATH)
|
||||
await removeFromVerdaccio("automerge")
|
||||
await fsPromises.rm(path.join(AUTOMERGE_JS_PATH, "yarn.lock"), {force: true})
|
||||
await spawnAndWait("yarn", ["--cwd", AUTOMERGE_JS_PATH, "install", "--registry", registryUrl, "--check-files"], {stdio: "inherit"})
|
||||
await spawnAndWait("yarn", ["--cwd", AUTOMERGE_JS_PATH, "build"], {stdio: "inherit"})
|
||||
await yarnPublish(registryUrl, AUTOMERGE_JS_PATH)
|
||||
}
|
||||
|
||||
/**
|
||||
* A running verdaccio process
|
||||
*
|
||||
*/
|
||||
class VerdaccioProcess {
|
||||
child: ChildProcess
|
||||
stdout: Array<Buffer>
|
||||
stderr: Array<Buffer>
|
||||
|
||||
constructor(child: ChildProcess) {
|
||||
this.child = child
|
||||
|
||||
// Collect stdout/stderr otherwise the subprocess gets blocked writing
|
||||
this.stdout = []
|
||||
this.stderr = []
|
||||
this.child.stdout && this.child.stdout.on("data", (data) => this.stdout.push(data))
|
||||
this.child.stderr && this.child.stderr.on("data", (data) => this.stderr.push(data))
|
||||
|
||||
const errCallback = (e: any) => {
|
||||
console.error("!!!!!!!!!ERROR IN VERDACCIO PROCESS!!!!!!!!!")
|
||||
console.error(" ", e)
|
||||
if (this.stdout.length > 0) {
|
||||
console.log("\n**Verdaccio stdout**")
|
||||
const stdout = Buffer.concat(this.stdout)
|
||||
process.stdout.write(stdout)
|
||||
}
|
||||
|
||||
if (this.stderr.length > 0) {
|
||||
console.log("\n**Verdaccio stderr**")
|
||||
const stdout = Buffer.concat(this.stderr)
|
||||
process.stdout.write(stdout)
|
||||
}
|
||||
process.exit(-1)
|
||||
}
|
||||
this.child.on("error", errCallback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn a verdaccio process and wait for it to respond succesfully to http requests
|
||||
*
|
||||
* The returned `VerdaccioProcess` can be used to control the subprocess
|
||||
*/
|
||||
static async start() {
|
||||
const child = spawn("yarn", ["verdaccio", "--config", VERDACCIO_CONFIG_PATH], {env: { ...process.env, FORCE_COLOR: "true"}})
|
||||
|
||||
// Forward stdout and stderr whilst waiting for startup to complete
|
||||
const stdoutCallback = (data: Buffer) => process.stdout.write(data)
|
||||
const stderrCallback = (data: Buffer) => process.stderr.write(data)
|
||||
child.stdout && child.stdout.on("data", stdoutCallback)
|
||||
child.stderr && child.stderr.on("data", stderrCallback)
|
||||
|
||||
const healthCheck = async () => {
|
||||
while (true) {
|
||||
try {
|
||||
const resp = await fetch("http://localhost:4873")
|
||||
if (resp.status === 200) {
|
||||
return
|
||||
} else {
|
||||
console.log(`Healthcheck failed: bad status ${resp.status}`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Healthcheck failed: ${e}`)
|
||||
}
|
||||
await setTimeout(500)
|
||||
}
|
||||
}
|
||||
await withTimeout(healthCheck(), 10000)
|
||||
|
||||
// Stop forwarding stdout/stderr
|
||||
child.stdout && child.stdout.off("data", stdoutCallback)
|
||||
child.stderr && child.stderr.off("data", stderrCallback)
|
||||
return new VerdaccioProcess(child)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a SIGKILL to the process and wait for it to stop
|
||||
*/
|
||||
async kill() {
|
||||
this.child.stdout && this.child.stdout.destroy()
|
||||
this.child.stderr && this.child.stderr.destroy()
|
||||
this.child.kill();
|
||||
try {
|
||||
await withTimeout(once(this.child, "close"), 500)
|
||||
} catch (e) {
|
||||
console.error("unable to kill verdaccio subprocess, trying -9")
|
||||
this.child.kill(9)
|
||||
await withTimeout(once(this.child, "close"), 500)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise which resolves if the subprocess exits for some reason
|
||||
*/
|
||||
async died(): Promise<number | null> {
|
||||
const [exit, _signal] = await once(this.child, "exit")
|
||||
return exit
|
||||
}
|
||||
}
|
||||
|
||||
function printHeader(header: string) {
|
||||
console.log("\n===============================")
|
||||
console.log(` ${header}`)
|
||||
console.log("===============================")
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the automerge, automerge-wasm, and automerge-js packages from
|
||||
* `$packageDir/node_modules`
|
||||
*
|
||||
* This is useful to force refreshing a package by use in combination with
|
||||
* `yarn install --check-files`, which checks if a package is present in
|
||||
* `node_modules` and if it is not forces a reinstall.
|
||||
*
|
||||
* @param packageDir - The directory containing the package.json of the target project
|
||||
*/
|
||||
async function removeExistingAutomerge(packageDir: string) {
|
||||
await fsPromises.rm(path.join(packageDir, "node_modules", "automerge-wasm"), {recursive: true, force: true})
|
||||
await fsPromises.rm(path.join(packageDir, "node_modules", "automerge"), {recursive: true, force: true})
|
||||
}
|
||||
|
||||
type SpawnResult = {
|
||||
stdout?: Buffer,
|
||||
stderr?: Buffer,
|
||||
}
|
||||
|
||||
async function spawnAndWait(cmd: string, args: Array<string>, options: child_process.SpawnOptions): Promise<SpawnResult> {
|
||||
const child = spawn(cmd, args, options)
|
||||
let stdout = null
|
||||
let stderr = null
|
||||
if (child.stdout) {
|
||||
stdout = []
|
||||
child.stdout.on("data", data => stdout.push(data))
|
||||
}
|
||||
if (child.stderr) {
|
||||
stderr = []
|
||||
child.stderr.on("data", data => stderr.push(data))
|
||||
}
|
||||
|
||||
const [exit, _signal] = await once(child, "exit")
|
||||
if (exit && exit !== 0) {
|
||||
throw new Error("nonzero exit code")
|
||||
}
|
||||
return {
|
||||
stderr: stderr? Buffer.concat(stderr) : null,
|
||||
stdout: stdout ? Buffer.concat(stdout) : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a package from the verdaccio registry. This is necessary because we
|
||||
* often want to _replace_ a version rather than update the version number.
|
||||
* Obviously this is very bad and verboten in normal circumastances, but the
|
||||
* whole point here is to be able to test the entire packaging story so it's
|
||||
* okay I Promise.
|
||||
*/
|
||||
async function removeFromVerdaccio(packageName: string) {
|
||||
await fsPromises.rm(path.join(VERDACCIO_DB_PATH, packageName), {force: true, recursive: true})
|
||||
}
|
||||
|
||||
async function yarnPublish(registryUrl: string, cwd: string) {
|
||||
await spawnAndWait(
|
||||
"yarn",
|
||||
[
|
||||
"--registry",
|
||||
registryUrl,
|
||||
"--cwd",
|
||||
cwd,
|
||||
"publish",
|
||||
"--non-interactive",
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
env: {
|
||||
...process.env,
|
||||
FORCE_COLOR: "true",
|
||||
// This is a fake token, it just has to be the right format
|
||||
npm_config__auth: "//localhost:4873/:_authToken=Gp2Mgxm4faa/7wp0dMSuRA=="
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a given delay to resolve a promise, throwing an error if the
|
||||
* promise doesn't resolve with the timeout
|
||||
*
|
||||
* @param promise - the promise to wait for @param timeout - the delay in
|
||||
* milliseconds to wait before throwing
|
||||
*/
|
||||
async function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {
|
||||
type Step = "timed-out" | {result: T}
|
||||
const timedOut: () => Promise<Step> = async () => {
|
||||
await setTimeout(timeout)
|
||||
return "timed-out"
|
||||
}
|
||||
const succeeded: () => Promise<Step> = async () => {
|
||||
const result = await promise
|
||||
return {result}
|
||||
}
|
||||
const result = await Promise.race([timedOut(), succeeded()])
|
||||
if (result === "timed-out") {
|
||||
throw new Error("timed out")
|
||||
} else {
|
||||
return result.result
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "e2e",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"e2e": "ts-node index.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.7.18",
|
||||
"cmd-ts": "^0.11.0",
|
||||
"node-fetch": "^2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"typescript": "^4.8.3",
|
||||
"verdaccio": "5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node-fetch": "2.x"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
},
|
||||
"module": "nodenext"
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
storage: "./verdacciodb"
|
||||
auth:
|
||||
htpasswd:
|
||||
file: ./htpasswd
|
||||
publish:
|
||||
allow_offline: true
|
||||
logs: {type: stdout, format: pretty, level: info}
|
||||
packages:
|
||||
"automerge-wasm":
|
||||
access: "$all"
|
||||
publish: "$all"
|
||||
"automerge-js":
|
||||
access: "$all"
|
||||
publish: "$all"
|
||||
"*":
|
||||
access: "$all"
|
||||
publish: "$all"
|
||||
proxy: npmjs
|
||||
"@*/*":
|
||||
access: "$all"
|
||||
publish: "$all"
|
||||
proxy: npmjs
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
node_modules/
|
|
@ -1,59 +0,0 @@
|
|||
# Automerge + `create-react-app`
|
||||
|
||||
This is a little fiddly to get working. The problem is that `create-react-app`
|
||||
hard codes a webpack configuration which does not support WASM modules, which we
|
||||
require in order to bundle the WASM implementation of automerge. To get around
|
||||
this we use [`craco`](https://github.com/dilanx/craco) which does some monkey
|
||||
patching to allow us to modify the webpack config that `create-react-app`
|
||||
bundles. Then we use a craco plugin called
|
||||
[`craco-wasm`](https://www.npmjs.com/package/craco-wasm) to perform the
|
||||
necessary modifications to the webpack config. It should be noted that this is
|
||||
all quite fragile and ideally you probably don't want to use `create-react-app`
|
||||
to do this in production.
|
||||
|
||||
## Setup
|
||||
|
||||
Assuming you have already run `create-react-app` and your working directory is
|
||||
the project.
|
||||
|
||||
### Install craco and craco-wasm
|
||||
|
||||
```bash
|
||||
yarn add craco craco-wasm
|
||||
```
|
||||
|
||||
### Modify `package.json` to use `craco` for scripts
|
||||
|
||||
In `package.json` the `scripts` section will look like this:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test",
|
||||
"eject": "craco eject"
|
||||
},
|
||||
```
|
||||
|
||||
Replace that section with:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test",
|
||||
"eject": "craco eject"
|
||||
},
|
||||
```
|
||||
|
||||
### Create `craco.config.js`
|
||||
|
||||
In the root of the project add the following contents to `craco.config.js`
|
||||
|
||||
```javascript
|
||||
const cracoWasm = require("craco-wasm")
|
||||
|
||||
module.exports = {
|
||||
plugins: [cracoWasm()]
|
||||
}
|
||||
```
|
|
@ -1,5 +0,0 @@
|
|||
const cracoWasm = require("craco-wasm")
|
||||
|
||||
module.exports = {
|
||||
plugins: [cracoWasm()]
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"name": "automerge-create-react-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@craco/craco": "^7.0.0-alpha.8",
|
||||
"craco-wasm": "0.0.1",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"automerge": "2.0.0-alpha.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test",
|
||||
"eject": "craco eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
|
@ -1,43 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -1,38 +0,0 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import * as Automerge from "automerge"
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
|
||||
let doc = Automerge.init()
|
||||
doc = Automerge.change(doc, (d) => d.hello = "from automerge-js")
|
||||
const result = JSON.stringify(doc)
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>{result}</p>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -1,8 +0,0 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,13 +0,0 @@
|
|||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
|
@ -1,5 +0,0 @@
|
|||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
File diff suppressed because it is too large
Load diff
2
automerge-js/examples/vite/.gitignore
vendored
2
automerge-js/examples/vite/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
node_modules/
|
||||
yarn.lock
|
|
@ -1,47 +0,0 @@
|
|||
# Vite + Automerge
|
||||
|
||||
There are three things you need to do to get WASM packaging working with vite:
|
||||
|
||||
1. Install the top level await plugin
|
||||
2. Install the `vite-plugin-wasm` plugin
|
||||
3. Exclude `automerge-wasm` from the optimizer
|
||||
|
||||
First, install the packages we need:
|
||||
```bash
|
||||
yarn add vite-plugin-top-level-await
|
||||
yarn add vite-plugin-wasm
|
||||
```
|
||||
|
||||
In `vite.config.js`
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from "vite"
|
||||
import wasm from "vite-plugin-wasm"
|
||||
import topLevelAwait from "vite-plugin-top-level-await"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [topLevelAwait(), wasm()],
|
||||
|
||||
optimizeDeps: {
|
||||
// This is necessary because otherwise `vite dev` includes two separate
|
||||
// versions of the JS wrapper. This causes problems because the JS
|
||||
// wrapper has a module level variable to track JS side heap
|
||||
// allocations, initializing this twice causes horrible breakage
|
||||
exclude: ["automerge-wasm"]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Now start the dev server:
|
||||
|
||||
```bash
|
||||
yarn vite
|
||||
```
|
||||
|
||||
## Running the example
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn dev
|
||||
```
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +0,0 @@
|
|||
import * as Automerge from "/node_modules/.vite/deps/automerge-js.js?v=6e973f28";
|
||||
console.log(Automerge);
|
||||
let doc = Automerge.init();
|
||||
doc = Automerge.change(doc, (d) => d.hello = "from automerge-js");
|
||||
console.log(doc);
|
||||
const result = JSON.stringify(doc);
|
||||
if (typeof document !== "undefined") {
|
||||
const element = document.createElement("div");
|
||||
element.innerHTML = JSON.stringify(result);
|
||||
document.body.appendChild(element);
|
||||
} else {
|
||||
console.log("node:", result);
|
||||
}
|
||||
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL2FsZXgvUHJvamVjdHMvYXV0b21lcmdlL2F1dG9tZXJnZS1ycy9hdXRvbWVyZ2UtanMvZXhhbXBsZXMvdml0ZS9zcmMvbWFpbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBBdXRvbWVyZ2UgZnJvbSBcImF1dG9tZXJnZS1qc1wiXG5cbi8vIGhlbGxvIHdvcmxkIGNvZGUgdGhhdCB3aWxsIHJ1biBjb3JyZWN0bHkgb24gd2ViIG9yIG5vZGVcblxuY29uc29sZS5sb2coQXV0b21lcmdlKVxubGV0IGRvYyA9IEF1dG9tZXJnZS5pbml0KClcbmRvYyA9IEF1dG9tZXJnZS5jaGFuZ2UoZG9jLCAoZDogYW55KSA9PiBkLmhlbGxvID0gXCJmcm9tIGF1dG9tZXJnZS1qc1wiKVxuY29uc29sZS5sb2coZG9jKVxuY29uc3QgcmVzdWx0ID0gSlNPTi5zdHJpbmdpZnkoZG9jKVxuXG5pZiAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJykge1xuICAgIC8vIGJyb3dzZXJcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgZWxlbWVudC5pbm5lckhUTUwgPSBKU09OLnN0cmluZ2lmeShyZXN1bHQpXG4gICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChlbGVtZW50KTtcbn0gZWxzZSB7XG4gICAgLy8gc2VydmVyXG4gICAgY29uc29sZS5sb2coXCJub2RlOlwiLCByZXN1bHQpXG59XG5cbiJdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWSxlQUFlO0FBSTNCLFFBQVEsSUFBSSxTQUFTO0FBQ3JCLElBQUksTUFBTSxVQUFVLEtBQUs7QUFDekIsTUFBTSxVQUFVLE9BQU8sS0FBSyxDQUFDLE1BQVcsRUFBRSxRQUFRLG1CQUFtQjtBQUNyRSxRQUFRLElBQUksR0FBRztBQUNmLE1BQU0sU0FBUyxLQUFLLFVBQVUsR0FBRztBQUVqQyxJQUFJLE9BQU8sYUFBYSxhQUFhO0FBRWpDLFFBQU0sVUFBVSxTQUFTLGNBQWMsS0FBSztBQUM1QyxVQUFRLFlBQVksS0FBSyxVQUFVLE1BQU07QUFDekMsV0FBUyxLQUFLLFlBQVksT0FBTztBQUNyQyxPQUFPO0FBRUgsVUFBUSxJQUFJLFNBQVMsTUFBTTtBQUMvQjsiLCJuYW1lcyI6W119
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "autovite",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"automerge": "2.0.0-alpha.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.1.0",
|
||||
"vite-plugin-top-level-await": "^1.1.1",
|
||||
"vite-plugin-wasm": "^2.1.0"
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,9 +0,0 @@
|
|||
export function setupCounter(element: HTMLButtonElement) {
|
||||
let counter = 0
|
||||
const setCounter = (count: number) => {
|
||||
counter = count
|
||||
element.innerHTML = `count is ${counter}`
|
||||
}
|
||||
element.addEventListener('click', () => setCounter(++counter))
|
||||
setCounter(0)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import * as Automerge from "automerge"
|
||||
|
||||
// hello world code that will run correctly on web or node
|
||||
|
||||
let doc = Automerge.init()
|
||||
doc = Automerge.change(doc, (d: any) => d.hello = "from automerge-js")
|
||||
const result = JSON.stringify(doc)
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
// browser
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = JSON.stringify(result)
|
||||
document.body.appendChild(element);
|
||||
} else {
|
||||
// server
|
||||
console.log("node:", result)
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.vanilla:hover {
|
||||
filter: drop-shadow(0 0 2em #3178c6aa);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"></path><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"></path></svg>
|
Before Width: | Height: | Size: 1.4 KiB |
1
automerge-js/examples/vite/src/vite-env.d.ts
vendored
1
automerge-js/examples/vite/src/vite-env.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
/// <reference types="vite/client" />
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import { defineConfig } from "vite"
|
||||
import wasm from "vite-plugin-wasm"
|
||||
import topLevelAwait from "vite-plugin-top-level-await"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [topLevelAwait(), wasm()],
|
||||
|
||||
optimizeDeps: {
|
||||
// This is necessary because otherwise `vite dev` includes two separate
|
||||
// versions of the JS wrapper. This causes problems because the JS
|
||||
// wrapper has a module level variable to track JS side heap
|
||||
// allocations, initializing this twice causes horrible breakage
|
||||
exclude: ["automerge-wasm"]
|
||||
}
|
||||
})
|
|
@ -1,37 +0,0 @@
|
|||
# Webpack + Automerge
|
||||
|
||||
|
||||
Getting WASM working in webpack 5 is very easy. You just need to enable the
|
||||
`asyncWebAssembly`
|
||||
[experiment](https://webpack.js.org/configuration/experiments/). For example:
|
||||
|
||||
|
||||
```javascript
|
||||
const path = require('path');
|
||||
|
||||
const clientConfig = {
|
||||
experiments: { asyncWebAssembly: true },
|
||||
target: 'web',
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'public'),
|
||||
},
|
||||
mode: "development", // or production
|
||||
performance: { // we dont want the wasm blob to generate warnings
|
||||
hints: false,
|
||||
maxEntrypointSize: 512000,
|
||||
maxAssetSize: 512000
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = clientConfig
|
||||
```
|
||||
|
||||
## Running the example
|
||||
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn start
|
||||
```
|
|
@ -10,13 +10,13 @@
|
|||
},
|
||||
"author": "",
|
||||
"dependencies": {
|
||||
"automerge": "2.0.0-alpha.1"
|
||||
"automerge-js": "file:automerge-js-0.1.0.tgz",
|
||||
"automerge-wasm": "file:automerge-wasm-0.1.3.tgz"
|
||||
},
|
||||
"devDependencies": {
|
||||
"serve": "^13.0.2",
|
||||
"webpack": "^5.72.1",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.11.1",
|
||||
"webpack-node-externals": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
import * as Automerge from "automerge"
|
||||
import * as Automerge from "automerge-js"
|
||||
import init from "automerge-wasm"
|
||||
|
||||
// hello world code that will run correctly on web or node
|
||||
|
||||
let doc = Automerge.init()
|
||||
doc = Automerge.change(doc, (d) => d.hello = "from automerge-js")
|
||||
const result = JSON.stringify(doc)
|
||||
init().then((api) => {
|
||||
Automerge.use(api)
|
||||
let doc = Automerge.init()
|
||||
doc = Automerge.change(doc, (d) => d.hello = "from automerge-js")
|
||||
const result = JSON.stringify(doc)
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
// browser
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = JSON.stringify(result)
|
||||
document.body.appendChild(element);
|
||||
} else {
|
||||
// server
|
||||
console.log("node:", result)
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
// browser
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = JSON.stringify(result)
|
||||
document.body.appendChild(element);
|
||||
} else {
|
||||
// server
|
||||
console.log("node:", result)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ const serverConfig = {
|
|||
};
|
||||
|
||||
const clientConfig = {
|
||||
experiments: { asyncWebAssembly: true },
|
||||
target: 'web',
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
|
|
113
automerge-js/index.d.ts
vendored
Normal file
113
automerge-js/index.d.ts
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { API as LowLevelApi } from "automerge-types";
|
||||
import { Actor as ActorId, Prop, ObjID, Change, DecodedChange, Heads, MaterializeValue } from "automerge-types";
|
||||
import { JsSyncState as SyncState, SyncMessage, DecodedSyncMessage } from "automerge-types";
|
||||
|
||||
export { API as LowLevelApi } from "automerge-types";
|
||||
export { Actor as ActorId, Prop, ObjID, Change, DecodedChange, Heads, Automerge, MaterializeValue } from "automerge-types";
|
||||
export { JsSyncState as SyncState, SyncMessage, DecodedSyncMessage } from "automerge-types";
|
||||
|
||||
export type ChangeOptions = {
|
||||
message?: string;
|
||||
time?: number;
|
||||
};
|
||||
|
||||
export class Int {
|
||||
value: number;
|
||||
constructor(value: number);
|
||||
}
|
||||
|
||||
export class Uint {
|
||||
value: number;
|
||||
constructor(value: number);
|
||||
}
|
||||
|
||||
export class Float64 {
|
||||
value: number;
|
||||
constructor(value: number);
|
||||
}
|
||||
|
||||
export class Counter {
|
||||
value: number;
|
||||
constructor(value?: number);
|
||||
valueOf(): number;
|
||||
toString(): string;
|
||||
toJSON(): number;
|
||||
}
|
||||
|
||||
export class Text {
|
||||
elems: AutomergeValue[];
|
||||
constructor(text?: string | string[]);
|
||||
get length(): number;
|
||||
get(index: number): AutomergeValue | undefined;
|
||||
[index: number]: AutomergeValue | undefined;
|
||||
[Symbol.iterator](): {
|
||||
next(): {
|
||||
done: boolean;
|
||||
value: AutomergeValue;
|
||||
} | {
|
||||
done: boolean;
|
||||
value?: undefined;
|
||||
};
|
||||
};
|
||||
toString(): string;
|
||||
toSpans(): AutomergeValue[];
|
||||
toJSON(): string;
|
||||
set(index: number, value: AutomergeValue): void;
|
||||
insertAt(index: number, ...values: AutomergeValue[]): void;
|
||||
deleteAt(index: number, numDelete?: number): void;
|
||||
map<T>(callback: (e: AutomergeValue) => T): void;
|
||||
}
|
||||
|
||||
export type Doc<T> = {
|
||||
readonly [P in keyof T]: T[P];
|
||||
};
|
||||
|
||||
export type ChangeFn<T> = (doc: T) => void;
|
||||
|
||||
export interface State<T> {
|
||||
change: DecodedChange;
|
||||
snapshot: T;
|
||||
}
|
||||
|
||||
export type ScalarValue = string | number | null | boolean | Date | Counter | Uint8Array;
|
||||
|
||||
export type AutomergeValue = ScalarValue | {[key: string]: AutomergeValue;} | Array<AutomergeValue>;
|
||||
|
||||
type Conflicts = {
|
||||
[key: string]: AutomergeValue;
|
||||
};
|
||||
|
||||
export function use(api: LowLevelApi): void;
|
||||
export function getBackend<T>(doc: Doc<T>) : Automerge;
|
||||
export function init<T>(actor?: ActorId): Doc<T>;
|
||||
export function clone<T>(doc: Doc<T>): Doc<T>;
|
||||
export function free<T>(doc: Doc<T>): void;
|
||||
export function from<T>(initialState: T | Doc<T>, actor?: ActorId): Doc<T>;
|
||||
export function change<T>(doc: Doc<T>, options: string | ChangeOptions | ChangeFn<T>, callback?: ChangeFn<T>): Doc<T>;
|
||||
export function emptyChange<T>(doc: Doc<T>, options: ChangeOptions): unknown;
|
||||
export function load<T>(data: Uint8Array, actor?: ActorId): Doc<T>;
|
||||
export function save<T>(doc: Doc<T>): Uint8Array;
|
||||
export function merge<T>(local: Doc<T>, remote: Doc<T>): Doc<T>;
|
||||
export function getActorId<T>(doc: Doc<T>): ActorId;
|
||||
export function getConflicts<T>(doc: Doc<T>, prop: Prop): Conflicts | undefined;
|
||||
export function getLastLocalChange<T>(doc: Doc<T>): Change | undefined;
|
||||
export function getObjectId<T>(doc: Doc<T>): ObjID;
|
||||
export function getChanges<T>(oldState: Doc<T>, newState: Doc<T>): Change[];
|
||||
export function getAllChanges<T>(doc: Doc<T>): Change[];
|
||||
export function applyChanges<T>(doc: Doc<T>, changes: Change[]): [Doc<T>];
|
||||
export function getHistory<T>(doc: Doc<T>): State<T>[];
|
||||
export function equals<T>(val1: Doc<T>, val2: Doc<T>): boolean;
|
||||
export function encodeSyncState(state: SyncState): Uint8Array;
|
||||
export function decodeSyncState(state: Uint8Array): SyncState;
|
||||
export function generateSyncMessage<T>(doc: Doc<T>, inState: SyncState): [SyncState, SyncMessage | null];
|
||||
export function receiveSyncMessage<T>(doc: Doc<T>, inState: SyncState, message: SyncMessage): [Doc<T>, SyncState, null];
|
||||
export function initSyncState(): SyncState;
|
||||
export function encodeChange(change: DecodedChange): Change;
|
||||
export function decodeChange(data: Change): DecodedChange;
|
||||
export function encodeSyncMessage(message: DecodedSyncMessage): SyncMessage;
|
||||
export function decodeSyncMessage(message: SyncMessage): DecodedSyncMessage;
|
||||
export function getMissingDeps<T>(doc: Doc<T>, heads: Heads): Heads;
|
||||
export function getHeads<T>(doc: Doc<T>): Heads;
|
||||
export function dump<T>(doc: Doc<T>): void;
|
||||
export function toJS<T>(doc: Doc<T>): MaterializeValue;
|
||||
export function uuid(): string;
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "automerge",
|
||||
"name": "automerge-js",
|
||||
"collaborators": [
|
||||
"Orion Henry <orion@inkandswitch.com>",
|
||||
"Martin Kleppmann"
|
||||
],
|
||||
"version": "2.0.0-alpha.1",
|
||||
"version": "0.1.12",
|
||||
"description": "Reimplementation of `automerge` on top of the automerge-wasm backend",
|
||||
"homepage": "https://github.com/automerge/automerge-rs/tree/main/automerge-js",
|
||||
"repository": "github:automerge/automerge-rs",
|
||||
|
@ -13,7 +13,6 @@
|
|||
"LICENSE",
|
||||
"package.json",
|
||||
"index.d.ts",
|
||||
"dist/cjs/*.d.ts",
|
||||
"dist/cjs/constants.js",
|
||||
"dist/cjs/types.js",
|
||||
"dist/cjs/numbers.js",
|
||||
|
@ -23,7 +22,6 @@
|
|||
"dist/cjs/low_level.js",
|
||||
"dist/cjs/text.js",
|
||||
"dist/cjs/proxies.js",
|
||||
"dist/mjs/*.d.ts",
|
||||
"dist/mjs/constants.js",
|
||||
"dist/mjs/types.js",
|
||||
"dist/mjs/numbers.js",
|
||||
|
@ -49,6 +47,7 @@
|
|||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.25.0",
|
||||
"@typescript-eslint/parser": "^5.25.0",
|
||||
"automerge-wasm": "^0.1.6",
|
||||
"eslint": "^8.15.0",
|
||||
"fast-sha256": "^1.3.0",
|
||||
"mocha": "^10.0.0",
|
||||
|
@ -57,7 +56,7 @@
|
|||
"typescript": "^4.6.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"automerge-wasm": "0.1.7",
|
||||
"automerge-types": "0.1.5",
|
||||
"uuid": "^8.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Automerge, ObjID, Prop } from "automerge-wasm"
|
||||
import { Automerge, ObjID, Prop } from "automerge-types"
|
||||
import { COUNTER } from "./constants"
|
||||
/**
|
||||
* The most basic CRDT: an integer value that can be changed only by
|
||||
|
|
|
@ -7,11 +7,11 @@ import { STATE, HEADS, TRACE, OBJECT_ID, READ_ONLY, FROZEN } from "./constants"
|
|||
import { AutomergeValue, Counter } from "./types"
|
||||
export { AutomergeValue, Text, Counter, Int, Uint, Float64 } from "./types"
|
||||
|
||||
import { API } from "automerge-wasm";
|
||||
import { API } from "automerge-types";
|
||||
import { ApiHandler, UseApi } from "./low_level"
|
||||
|
||||
import { Actor as ActorId, Prop, ObjID, Change, DecodedChange, Heads, Automerge, MaterializeValue } from "automerge-wasm"
|
||||
import { JsSyncState as SyncState, SyncMessage, DecodedSyncMessage } from "automerge-wasm"
|
||||
import { Actor as ActorId, Prop, ObjID, Change, DecodedChange, Heads, Automerge, MaterializeValue } from "automerge-types"
|
||||
import { JsSyncState as SyncState, SyncMessage, DecodedSyncMessage } from "automerge-types"
|
||||
|
||||
export type ChangeOptions = { message?: string, time?: number }
|
||||
|
||||
|
@ -24,14 +24,10 @@ export interface State<T> {
|
|||
snapshot: T
|
||||
}
|
||||
|
||||
|
||||
export function use(api: API) {
|
||||
UseApi(api)
|
||||
}
|
||||
|
||||
import * as wasm from "automerge-wasm"
|
||||
use(wasm)
|
||||
|
||||
export function getBackend<T>(doc: Doc<T>) : Automerge {
|
||||
return _state(doc)
|
||||
}
|
||||
|
@ -91,7 +87,7 @@ export function free<T>(doc: Doc<T>) {
|
|||
return _state(doc).free()
|
||||
}
|
||||
|
||||
export function from<T extends Record<string, unknown>>(initialState: T | Doc<T>, actor?: ActorId): Doc<T> {
|
||||
export function from<T>(initialState: T | Doc<T>, actor?: ActorId): Doc<T> {
|
||||
return change(init(actor), (d) => Object.assign(d, initialState))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
import { Automerge, Change, DecodedChange, Actor, SyncState, SyncMessage, JsSyncState, DecodedSyncMessage } from "automerge-wasm"
|
||||
import { API } from "automerge-wasm"
|
||||
import { Automerge, Change, DecodedChange, Actor, SyncState, SyncMessage, JsSyncState, DecodedSyncMessage } from "automerge-types"
|
||||
import { API } from "automerge-types"
|
||||
|
||||
export function UseApi(api: API) {
|
||||
for (const k in api) {
|
||||
|
@ -11,15 +11,15 @@ export function UseApi(api: API) {
|
|||
/* eslint-disable */
|
||||
export const ApiHandler : API = {
|
||||
create(actor?: Actor): Automerge { throw new RangeError("Automerge.use() not called") },
|
||||
load(data: Uint8Array, actor?: Actor): Automerge { throw new RangeError("Automerge.use() not called (load)") },
|
||||
encodeChange(change: DecodedChange): Change { throw new RangeError("Automerge.use() not called (encodeChange)") },
|
||||
decodeChange(change: Change): DecodedChange { throw new RangeError("Automerge.use() not called (decodeChange)") },
|
||||
initSyncState(): SyncState { throw new RangeError("Automerge.use() not called (initSyncState)") },
|
||||
encodeSyncMessage(message: DecodedSyncMessage): SyncMessage { throw new RangeError("Automerge.use() not called (encodeSyncMessage)") },
|
||||
decodeSyncMessage(msg: SyncMessage): DecodedSyncMessage { throw new RangeError("Automerge.use() not called (decodeSyncMessage)") },
|
||||
encodeSyncState(state: SyncState): Uint8Array { throw new RangeError("Automerge.use() not called (encodeSyncState)") },
|
||||
decodeSyncState(data: Uint8Array): SyncState { throw new RangeError("Automerge.use() not called (decodeSyncState)") },
|
||||
exportSyncState(state: SyncState): JsSyncState { throw new RangeError("Automerge.use() not called (exportSyncState)") },
|
||||
importSyncState(state: JsSyncState): SyncState { throw new RangeError("Automerge.use() not called (importSyncState)") },
|
||||
load(data: Uint8Array, actor?: Actor): Automerge { throw new RangeError("Automerge.use() not called") },
|
||||
encodeChange(change: DecodedChange): Change { throw new RangeError("Automerge.use() not called") },
|
||||
decodeChange(change: Change): DecodedChange { throw new RangeError("Automerge.use() not called") },
|
||||
initSyncState(): SyncState { throw new RangeError("Automerge.use() not called") },
|
||||
encodeSyncMessage(message: DecodedSyncMessage): SyncMessage { throw new RangeError("Automerge.use() not called") },
|
||||
decodeSyncMessage(msg: SyncMessage): DecodedSyncMessage { throw new RangeError("Automerge.use() not called") },
|
||||
encodeSyncState(state: SyncState): Uint8Array { throw new RangeError("Automerge.use() not called") },
|
||||
decodeSyncState(data: Uint8Array): SyncState { throw new RangeError("Automerge.use() not called") },
|
||||
exportSyncState(state: SyncState): JsSyncState { throw new RangeError("Automerge.use() not called") },
|
||||
importSyncState(state: JsSyncState): SyncState { throw new RangeError("Automerge.use() not called") },
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
import { Automerge, Heads, ObjID } from "automerge-wasm"
|
||||
import { Prop } from "automerge-wasm"
|
||||
import { Automerge, Heads, ObjID } from "automerge-types"
|
||||
import { Prop } from "automerge-types"
|
||||
import { AutomergeValue, ScalarValue, MapValue, ListValue, TextValue } from "./types"
|
||||
import { Int, Uint, Float64 } from "./numbers"
|
||||
import { Counter, getWriteableCounter } from "./counter"
|
||||
import { Text } from "./text"
|
||||
import { STATE, HEADS, TRACE, FROZEN, OBJECT_ID, READ_ONLY, COUNTER, INT, UINT, F64, TEXT } from "./constants"
|
||||
|
@ -199,7 +200,7 @@ const MapHandler = {
|
|||
ownKeys (target) {
|
||||
const { context, objectId, heads} = target
|
||||
// FIXME - this is a tmp workaround until fix the dupe key bug in keys()
|
||||
const keys = context.keys(objectId, heads)
|
||||
let keys = context.keys(objectId, heads)
|
||||
return [...new Set<string>(keys)]
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Value } from "automerge-wasm"
|
||||
import { Value } from "automerge-types"
|
||||
import { TEXT } from "./constants"
|
||||
|
||||
export class Text {
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import * as tt from "automerge-types"
|
||||
import * as assert from 'assert'
|
||||
import * as util from 'util'
|
||||
import * as Automerge from '../src'
|
||||
import * as AutomergeWASM from "automerge-wasm"
|
||||
|
||||
Automerge.use(AutomergeWASM)
|
||||
|
||||
describe('Automerge', () => {
|
||||
describe('basics', () => {
|
||||
|
@ -170,15 +175,4 @@ describe('Automerge', () => {
|
|||
console.log(doc.text.indexOf("world"))
|
||||
})
|
||||
})
|
||||
|
||||
it('should obtain the same conflicts, regardless of merge order', () => {
|
||||
let s1 = Automerge.init()
|
||||
let s2 = Automerge.init()
|
||||
s1 = Automerge.change(s1, doc => { doc.x = 1; doc.y = 2 })
|
||||
s2 = Automerge.change(s2, doc => { doc.x = 3; doc.y = 4 })
|
||||
const m1 = Automerge.merge(Automerge.clone(s1), Automerge.clone(s2))
|
||||
const m2 = Automerge.merge(Automerge.clone(s2), Automerge.clone(s1))
|
||||
assert.deepStrictEqual(Automerge.getConflicts(m1, 'x'), Automerge.getConflicts(m2, 'x'))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@ import * as assert from 'assert'
|
|||
import { checkEncoded } from './helpers'
|
||||
import * as Automerge from '../src'
|
||||
import { encodeChange, decodeChange } from '../src'
|
||||
import * as AutomergeWASM from "automerge-wasm"
|
||||
|
||||
Automerge.use(AutomergeWASM)
|
||||
|
||||
describe('change encoding', () => {
|
||||
it('should encode text edits', () => {
|
||||
|
|
|
@ -2,6 +2,9 @@ import * as assert from 'assert'
|
|||
import * as Automerge from '../src'
|
||||
import { assertEqualsOneOf } from './helpers'
|
||||
import { decodeChange } from './legacy/columnar'
|
||||
import * as AutomergeWASM from "automerge-wasm"
|
||||
|
||||
Automerge.use(AutomergeWASM)
|
||||
|
||||
const UUID_PATTERN = /^[0-9a-f]{32}$/
|
||||
const OPID_PATTERN = /^[0-9]+@[0-9a-f]{32}$/
|
||||
|
|
|
@ -3,6 +3,9 @@ import * as Automerge from '../src'
|
|||
import { BloomFilter } from './legacy/sync'
|
||||
import { decodeChangeMeta } from './legacy/columnar'
|
||||
import { decodeSyncMessage, encodeSyncMessage, decodeSyncState, encodeSyncState, initSyncState } from "../src"
|
||||
import * as AutomergeWASM from "automerge-wasm"
|
||||
|
||||
Automerge.use(AutomergeWASM)
|
||||
|
||||
function inspect(a) {
|
||||
const util = require("util");
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import * as assert from 'assert'
|
||||
import * as Automerge from '../src'
|
||||
import { assertEqualsOneOf } from './helpers'
|
||||
import * as AutomergeWASM from "automerge-wasm"
|
||||
|
||||
Automerge.use(AutomergeWASM)
|
||||
|
||||
function attributeStateToAttributes(accumulatedAttributes) {
|
||||
const attributes = {}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import * as assert from 'assert'
|
||||
import * as Automerge from '../src'
|
||||
import * as AutomergeWASM from "automerge-wasm"
|
||||
|
||||
Automerge.use(AutomergeWASM)
|
||||
|
||||
const uuid = Automerge.uuid
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"sourceMap": false,
|
||||
"declaration": true,
|
||||
"declaration": false,
|
||||
"resolveJsonModule": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
|
|
|
@ -35,7 +35,7 @@ hex = "^0.4.3"
|
|||
regex = "^1.5"
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "^0.2.83"
|
||||
version = "^0.2"
|
||||
#features = ["std"]
|
||||
features = ["serde-serialize", "std"]
|
||||
|
||||
|
|
207
automerge-wasm/index.d.ts
vendored
207
automerge-wasm/index.d.ts
vendored
|
@ -1,205 +1,2 @@
|
|||
export type Actor = string;
|
||||
export type ObjID = string;
|
||||
export type Change = Uint8Array;
|
||||
export type SyncMessage = Uint8Array;
|
||||
export type Prop = string | number;
|
||||
export type Hash = string;
|
||||
export type Heads = Hash[];
|
||||
export type Value = string | number | boolean | null | Date | Uint8Array
|
||||
export type MaterializeValue = { [key:string]: MaterializeValue } | Array<MaterializeValue> | Value
|
||||
export type ObjType = string | Array<ObjType | Value> | { [key: string]: ObjType | Value }
|
||||
export type FullValue =
|
||||
["str", string] |
|
||||
["int", number] |
|
||||
["uint", number] |
|
||||
["f64", number] |
|
||||
["boolean", boolean] |
|
||||
["timestamp", Date] |
|
||||
["counter", number] |
|
||||
["bytes", Uint8Array] |
|
||||
["null", null] |
|
||||
["map", ObjID] |
|
||||
["list", ObjID] |
|
||||
["text", ObjID] |
|
||||
["table", ObjID]
|
||||
|
||||
export type FullValueWithId =
|
||||
["str", string, ObjID ] |
|
||||
["int", number, ObjID ] |
|
||||
["uint", number, ObjID ] |
|
||||
["f64", number, ObjID ] |
|
||||
["boolean", boolean, ObjID ] |
|
||||
["timestamp", Date, ObjID ] |
|
||||
["counter", number, ObjID ] |
|
||||
["bytes", Uint8Array, ObjID ] |
|
||||
["null", null, ObjID ] |
|
||||
["map", ObjID ] |
|
||||
["list", ObjID] |
|
||||
["text", ObjID] |
|
||||
["table", ObjID]
|
||||
|
||||
export enum ObjTypeName {
|
||||
list = "list",
|
||||
map = "map",
|
||||
table = "table",
|
||||
text = "text",
|
||||
}
|
||||
|
||||
export type Datatype =
|
||||
"boolean" |
|
||||
"str" |
|
||||
"int" |
|
||||
"uint" |
|
||||
"f64" |
|
||||
"null" |
|
||||
"timestamp" |
|
||||
"counter" |
|
||||
"bytes" |
|
||||
"map" |
|
||||
"text" |
|
||||
"list";
|
||||
|
||||
export type SyncHave = {
|
||||
lastSync: Heads,
|
||||
bloom: Uint8Array,
|
||||
}
|
||||
|
||||
export type DecodedSyncMessage = {
|
||||
heads: Heads,
|
||||
need: Heads,
|
||||
have: SyncHave[]
|
||||
changes: Change[]
|
||||
}
|
||||
|
||||
export type DecodedChange = {
|
||||
actor: Actor,
|
||||
seq: number
|
||||
startOp: number,
|
||||
time: number,
|
||||
message: string | null,
|
||||
deps: Heads,
|
||||
hash: Hash,
|
||||
ops: Op[]
|
||||
}
|
||||
|
||||
export type Op = {
|
||||
action: string,
|
||||
obj: ObjID,
|
||||
key: string,
|
||||
value?: string | number | boolean,
|
||||
datatype?: string,
|
||||
pred: string[],
|
||||
}
|
||||
|
||||
export type Patch = {
|
||||
obj: ObjID
|
||||
action: 'assign' | 'insert' | 'delete'
|
||||
key: Prop
|
||||
value: Value
|
||||
datatype: Datatype
|
||||
conflict: boolean
|
||||
}
|
||||
|
||||
export function create(actor?: Actor): Automerge;
|
||||
export function load(data: Uint8Array, actor?: Actor): Automerge;
|
||||
export function encodeChange(change: DecodedChange): Change;
|
||||
export function decodeChange(change: Change): DecodedChange;
|
||||
export function initSyncState(): SyncState;
|
||||
export function encodeSyncMessage(message: DecodedSyncMessage): SyncMessage;
|
||||
export function decodeSyncMessage(msg: SyncMessage): DecodedSyncMessage;
|
||||
export function encodeSyncState(state: SyncState): Uint8Array;
|
||||
export function decodeSyncState(data: Uint8Array): SyncState;
|
||||
export function exportSyncState(state: SyncState): JsSyncState;
|
||||
export function importSyncState(state: JsSyncState): SyncState;
|
||||
|
||||
export class API {
|
||||
create(actor?: Actor): Automerge;
|
||||
load(data: Uint8Array, actor?: Actor): Automerge;
|
||||
encodeChange(change: DecodedChange): Change;
|
||||
decodeChange(change: Change): DecodedChange;
|
||||
initSyncState(): SyncState;
|
||||
encodeSyncMessage(message: DecodedSyncMessage): SyncMessage;
|
||||
decodeSyncMessage(msg: SyncMessage): DecodedSyncMessage;
|
||||
encodeSyncState(state: SyncState): Uint8Array;
|
||||
decodeSyncState(data: Uint8Array): SyncState;
|
||||
exportSyncState(state: SyncState): JsSyncState;
|
||||
importSyncState(state: JsSyncState): SyncState;
|
||||
}
|
||||
|
||||
export class Automerge {
|
||||
// change state
|
||||
put(obj: ObjID, prop: Prop, value: Value, datatype?: Datatype): void;
|
||||
putObject(obj: ObjID, prop: Prop, value: ObjType): ObjID;
|
||||
insert(obj: ObjID, index: number, value: Value, datatype?: Datatype): void;
|
||||
insertObject(obj: ObjID, index: number, value: ObjType): ObjID;
|
||||
push(obj: ObjID, value: Value, datatype?: Datatype): void;
|
||||
pushObject(obj: ObjID, value: ObjType): ObjID;
|
||||
splice(obj: ObjID, start: number, delete_count: number, text?: string | Array<Value>): ObjID[] | undefined;
|
||||
increment(obj: ObjID, prop: Prop, value: number): void;
|
||||
delete(obj: ObjID, prop: Prop): void;
|
||||
|
||||
// returns a single value - if there is a conflict return the winner
|
||||
get(obj: ObjID, prop: Prop, heads?: Heads): Value | undefined;
|
||||
getWithType(obj: ObjID, prop: Prop, heads?: Heads): FullValue | null;
|
||||
// return all values in case of a conflict
|
||||
getAll(obj: ObjID, arg: Prop, heads?: Heads): FullValueWithId[];
|
||||
keys(obj: ObjID, heads?: Heads): string[];
|
||||
text(obj: ObjID, heads?: Heads): string;
|
||||
length(obj: ObjID, heads?: Heads): number;
|
||||
materialize(obj?: ObjID, heads?: Heads): MaterializeValue;
|
||||
|
||||
// transactions
|
||||
commit(message?: string, time?: number): Hash;
|
||||
merge(other: Automerge): Heads;
|
||||
getActorId(): Actor;
|
||||
pendingOps(): number;
|
||||
rollback(): number;
|
||||
|
||||
// patches
|
||||
enablePatches(enable: boolean): void;
|
||||
popPatches(): Patch[];
|
||||
|
||||
// save and load to local store
|
||||
save(): Uint8Array;
|
||||
saveIncremental(): Uint8Array;
|
||||
loadIncremental(data: Uint8Array): number;
|
||||
|
||||
// sync over network
|
||||
receiveSyncMessage(state: SyncState, message: SyncMessage): void;
|
||||
generateSyncMessage(state: SyncState): SyncMessage | null;
|
||||
|
||||
// low level change functions
|
||||
applyChanges(changes: Change[]): void;
|
||||
getChanges(have_deps: Heads): Change[];
|
||||
getChangeByHash(hash: Hash): Change | null;
|
||||
getChangesAdded(other: Automerge): Change[];
|
||||
getHeads(): Heads;
|
||||
getLastLocalChange(): Change | null;
|
||||
getMissingDeps(heads?: Heads): Heads;
|
||||
|
||||
// memory management
|
||||
free(): void;
|
||||
clone(actor?: string): Automerge;
|
||||
fork(actor?: string): Automerge;
|
||||
forkAt(heads: Heads, actor?: string): Automerge;
|
||||
|
||||
// dump internal state to console.log
|
||||
dump(): void;
|
||||
}
|
||||
|
||||
export class JsSyncState {
|
||||
sharedHeads: Heads;
|
||||
lastSentHeads: Heads;
|
||||
theirHeads: Heads | undefined;
|
||||
theirHeed: Heads | undefined;
|
||||
theirHave: SyncHave[] | undefined;
|
||||
sentHashes: Heads;
|
||||
}
|
||||
|
||||
export class SyncState {
|
||||
free(): void;
|
||||
clone(): SyncState;
|
||||
lastSentHeads: Heads;
|
||||
sentHashes: Heads;
|
||||
readonly sharedHeads: Heads;
|
||||
}
|
||||
export * from "automerge-types"
|
||||
export { default } from "automerge-types"
|
||||
|
|
5
automerge-wasm/nodejs-index.js
Normal file
5
automerge-wasm/nodejs-index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
let wasm = require("./bindgen")
|
||||
module.exports = wasm
|
||||
module.exports.load = module.exports.loadDoc
|
||||
delete module.exports.loadDoc
|
||||
module.exports.init = () => (new Promise((resolve,reject) => { resolve(module.exports) }))
|
|
@ -8,29 +8,29 @@
|
|||
"description": "wasm-bindgen bindings to the automerge rust implementation",
|
||||
"homepage": "https://github.com/automerge/automerge-rs/tree/main/automerge-wasm",
|
||||
"repository": "github:automerge/automerge-rs",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.6",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
"package.json",
|
||||
"index.d.ts",
|
||||
"nodejs/index.js",
|
||||
"nodejs/bindgen.js",
|
||||
"nodejs/bindgen_bg.wasm",
|
||||
"bundler/bindgen.js",
|
||||
"bundler/bindgen_bg.js",
|
||||
"bundler/bindgen_bg.wasm"
|
||||
"web/index.js",
|
||||
"web/bindgen.js",
|
||||
"web/bindgen_bg.wasm"
|
||||
],
|
||||
"types": "index.d.ts",
|
||||
"module": "./bundler/bindgen.js",
|
||||
"main": "./nodejs/bindgen.js",
|
||||
"module": "./web/index.js",
|
||||
"main": "./nodejs/index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint test/*.ts",
|
||||
"debug": "cross-env PROFILE=dev yarn buildall",
|
||||
"build": "cross-env PROFILE=dev FEATURES='' yarn buildall",
|
||||
"build": "cross-env PROFILE=dev TARGET=nodejs FEATURES='' yarn target",
|
||||
"release": "cross-env PROFILE=release yarn buildall",
|
||||
"buildall": "cross-env TARGET=nodejs yarn target && cross-env TARGET=bundler yarn target",
|
||||
"target": "rimraf ./$TARGET && wasm-pack build --target $TARGET --$PROFILE --out-name bindgen -d $TARGET -- $FEATURES",
|
||||
"buildall": "cross-env TARGET=nodejs yarn target && cross-env TARGET=web yarn target",
|
||||
"target": "rimraf ./$TARGET && wasm-pack build --target $TARGET --$PROFILE --out-name bindgen -d $TARGET -- $FEATURES && cp $TARGET-index.js $TARGET/index.js",
|
||||
"test": "ts-mocha -p tsconfig.json --type-check --bail --full-trace test/*.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -50,8 +50,7 @@
|
|||
"ts-mocha": "^9.0.2",
|
||||
"typescript": "^4.6.4"
|
||||
},
|
||||
"exports": {
|
||||
"browser": "./bundler/bindgen.js",
|
||||
"require": "./nodejs/bindgen.js"
|
||||
"dependencies": {
|
||||
"automerge-types": "0.1.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -853,7 +853,7 @@ pub fn init(actor: Option<String>) -> Result<Automerge, JsValue> {
|
|||
Automerge::new(actor)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = load)]
|
||||
#[wasm_bindgen(js_name = loadDoc)]
|
||||
pub fn load(data: Uint8Array, actor: Option<String>) -> Result<Automerge, JsValue> {
|
||||
let data = data.to_vec();
|
||||
let observer = None;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, it } from 'mocha';
|
||||
import * as assert from 'assert'
|
||||
//@ts-ignore
|
||||
import { create, load } from '..'
|
||||
import { init, create, load } from '..'
|
||||
|
||||
describe('Automerge', () => {
|
||||
describe('Readme Examples', () => {
|
||||
|
@ -10,9 +10,11 @@ describe('Automerge', () => {
|
|||
doc.free()
|
||||
})
|
||||
it('Using the Library and Creating a Document (2)', (done) => {
|
||||
const doc = create()
|
||||
doc.free()
|
||||
done()
|
||||
init().then((_:any) => {
|
||||
const doc = create()
|
||||
doc.free()
|
||||
done()
|
||||
})
|
||||
})
|
||||
it('Automerge Scalar Types (1)', () => {
|
||||
const doc = create()
|
||||
|
|
|
@ -3,7 +3,7 @@ import { describe, it } from 'mocha';
|
|||
import assert from 'assert'
|
||||
//@ts-ignore
|
||||
import { BloomFilter } from './helpers/sync'
|
||||
import { create, load, SyncState, Automerge, encodeChange, decodeChange, initSyncState, decodeSyncMessage, decodeSyncState, encodeSyncState, encodeSyncMessage } from '..'
|
||||
import { init, create, load, SyncState, Automerge, encodeChange, decodeChange, initSyncState, decodeSyncMessage, decodeSyncState, encodeSyncState, encodeSyncMessage } from '..'
|
||||
import { DecodedSyncMessage, Hash } from '..';
|
||||
|
||||
function sync(a: Automerge, b: Automerge, aSyncState = initSyncState(), bSyncState = initSyncState()) {
|
||||
|
@ -28,6 +28,9 @@ function sync(a: Automerge, b: Automerge, aSyncState = initSyncState(), bSyncSta
|
|||
|
||||
describe('Automerge', () => {
|
||||
describe('basics', () => {
|
||||
it('default import init() should return a promise', () => {
|
||||
assert(init() instanceof Promise)
|
||||
})
|
||||
|
||||
it('should create, clone and free', () => {
|
||||
const doc1 = create()
|
||||
|
|
10
automerge-wasm/types/LICENSE
Normal file
10
automerge-wasm/types/LICENSE
Normal file
|
@ -0,0 +1,10 @@
|
|||
MIT License
|
||||
|
||||
Copyright 2022, Ink & Switch LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
209
automerge-wasm/types/index.d.ts
vendored
Normal file
209
automerge-wasm/types/index.d.ts
vendored
Normal file
|
@ -0,0 +1,209 @@
|
|||
|
||||
export type Actor = string;
|
||||
export type ObjID = string;
|
||||
export type Change = Uint8Array;
|
||||
export type SyncMessage = Uint8Array;
|
||||
export type Prop = string | number;
|
||||
export type Hash = string;
|
||||
export type Heads = Hash[];
|
||||
export type Value = string | number | boolean | null | Date | Uint8Array
|
||||
export type MaterializeValue = { [key:string]: MaterializeValue } | Array<MaterializeValue> | Value
|
||||
export type ObjType = string | Array<ObjType | Value> | { [key: string]: ObjType | Value }
|
||||
export type FullValue =
|
||||
["str", string] |
|
||||
["int", number] |
|
||||
["uint", number] |
|
||||
["f64", number] |
|
||||
["boolean", boolean] |
|
||||
["timestamp", Date] |
|
||||
["counter", number] |
|
||||
["bytes", Uint8Array] |
|
||||
["null", null] |
|
||||
["map", ObjID] |
|
||||
["list", ObjID] |
|
||||
["text", ObjID] |
|
||||
["table", ObjID]
|
||||
|
||||
export type FullValueWithId =
|
||||
["str", string, ObjID ] |
|
||||
["int", number, ObjID ] |
|
||||
["uint", number, ObjID ] |
|
||||
["f64", number, ObjID ] |
|
||||
["boolean", boolean, ObjID ] |
|
||||
["timestamp", Date, ObjID ] |
|
||||
["counter", number, ObjID ] |
|
||||
["bytes", Uint8Array, ObjID ] |
|
||||
["null", null, ObjID ] |
|
||||
["map", ObjID ] |
|
||||
["list", ObjID] |
|
||||
["text", ObjID] |
|
||||
["table", ObjID]
|
||||
|
||||
export enum ObjTypeName {
|
||||
list = "list",
|
||||
map = "map",
|
||||
table = "table",
|
||||
text = "text",
|
||||
}
|
||||
|
||||
export type Datatype =
|
||||
"boolean" |
|
||||
"str" |
|
||||
"int" |
|
||||
"uint" |
|
||||
"f64" |
|
||||
"null" |
|
||||
"timestamp" |
|
||||
"counter" |
|
||||
"bytes" |
|
||||
"map" |
|
||||
"text" |
|
||||
"list";
|
||||
|
||||
export type SyncHave = {
|
||||
lastSync: Heads,
|
||||
bloom: Uint8Array,
|
||||
}
|
||||
|
||||
export type DecodedSyncMessage = {
|
||||
heads: Heads,
|
||||
need: Heads,
|
||||
have: SyncHave[]
|
||||
changes: Change[]
|
||||
}
|
||||
|
||||
export type DecodedChange = {
|
||||
actor: Actor,
|
||||
seq: number
|
||||
startOp: number,
|
||||
time: number,
|
||||
message: string | null,
|
||||
deps: Heads,
|
||||
hash: Hash,
|
||||
ops: Op[]
|
||||
}
|
||||
|
||||
export type Op = {
|
||||
action: string,
|
||||
obj: ObjID,
|
||||
key: string,
|
||||
value?: string | number | boolean,
|
||||
datatype?: string,
|
||||
pred: string[],
|
||||
}
|
||||
|
||||
export type Patch = {
|
||||
obj: ObjID
|
||||
action: 'assign' | 'insert' | 'delete'
|
||||
key: Prop
|
||||
value: Value
|
||||
datatype: Datatype
|
||||
conflict: boolean
|
||||
}
|
||||
|
||||
export function create(actor?: Actor): Automerge;
|
||||
export function load(data: Uint8Array, actor?: Actor): Automerge;
|
||||
export function encodeChange(change: DecodedChange): Change;
|
||||
export function decodeChange(change: Change): DecodedChange;
|
||||
export function initSyncState(): SyncState;
|
||||
export function encodeSyncMessage(message: DecodedSyncMessage): SyncMessage;
|
||||
export function decodeSyncMessage(msg: SyncMessage): DecodedSyncMessage;
|
||||
export function encodeSyncState(state: SyncState): Uint8Array;
|
||||
export function decodeSyncState(data: Uint8Array): SyncState;
|
||||
export function exportSyncState(state: SyncState): JsSyncState;
|
||||
export function importSyncState(state: JsSyncState): SyncState;
|
||||
|
||||
export class API {
|
||||
create(actor?: Actor): Automerge;
|
||||
load(data: Uint8Array, actor?: Actor): Automerge;
|
||||
encodeChange(change: DecodedChange): Change;
|
||||
decodeChange(change: Change): DecodedChange;
|
||||
initSyncState(): SyncState;
|
||||
encodeSyncMessage(message: DecodedSyncMessage): SyncMessage;
|
||||
decodeSyncMessage(msg: SyncMessage): DecodedSyncMessage;
|
||||
encodeSyncState(state: SyncState): Uint8Array;
|
||||
decodeSyncState(data: Uint8Array): SyncState;
|
||||
exportSyncState(state: SyncState): JsSyncState;
|
||||
importSyncState(state: JsSyncState): SyncState;
|
||||
}
|
||||
|
||||
export class Automerge {
|
||||
// change state
|
||||
put(obj: ObjID, prop: Prop, value: Value, datatype?: Datatype): void;
|
||||
putObject(obj: ObjID, prop: Prop, value: ObjType): ObjID;
|
||||
insert(obj: ObjID, index: number, value: Value, datatype?: Datatype): void;
|
||||
insertObject(obj: ObjID, index: number, value: ObjType): ObjID;
|
||||
push(obj: ObjID, value: Value, datatype?: Datatype): void;
|
||||
pushObject(obj: ObjID, value: ObjType): ObjID;
|
||||
splice(obj: ObjID, start: number, delete_count: number, text?: string | Array<Value>): ObjID[] | undefined;
|
||||
increment(obj: ObjID, prop: Prop, value: number): void;
|
||||
delete(obj: ObjID, prop: Prop): void;
|
||||
|
||||
// returns a single value - if there is a conflict return the winner
|
||||
get(obj: ObjID, prop: Prop, heads?: Heads): Value | undefined;
|
||||
getWithType(obj: ObjID, prop: Prop, heads?: Heads): FullValue | null;
|
||||
// return all values in case of a conflict
|
||||
getAll(obj: ObjID, arg: Prop, heads?: Heads): FullValueWithId[];
|
||||
keys(obj: ObjID, heads?: Heads): string[];
|
||||
text(obj: ObjID, heads?: Heads): string;
|
||||
length(obj: ObjID, heads?: Heads): number;
|
||||
materialize(obj?: ObjID, heads?: Heads): MaterializeValue;
|
||||
|
||||
// transactions
|
||||
commit(message?: string, time?: number): Hash;
|
||||
merge(other: Automerge): Heads;
|
||||
getActorId(): Actor;
|
||||
pendingOps(): number;
|
||||
rollback(): number;
|
||||
|
||||
// patches
|
||||
enablePatches(enable: boolean): void;
|
||||
popPatches(): Patch[];
|
||||
|
||||
// save and load to local store
|
||||
save(): Uint8Array;
|
||||
saveIncremental(): Uint8Array;
|
||||
loadIncremental(data: Uint8Array): number;
|
||||
|
||||
// sync over network
|
||||
receiveSyncMessage(state: SyncState, message: SyncMessage): void;
|
||||
generateSyncMessage(state: SyncState): SyncMessage | null;
|
||||
|
||||
// low level change functions
|
||||
applyChanges(changes: Change[]): void;
|
||||
getChanges(have_deps: Heads): Change[];
|
||||
getChangeByHash(hash: Hash): Change | null;
|
||||
getChangesAdded(other: Automerge): Change[];
|
||||
getHeads(): Heads;
|
||||
getLastLocalChange(): Change | null;
|
||||
getMissingDeps(heads?: Heads): Heads;
|
||||
|
||||
// memory management
|
||||
free(): void;
|
||||
clone(actor?: string): Automerge;
|
||||
fork(actor?: string): Automerge;
|
||||
forkAt(heads: Heads, actor?: string): Automerge;
|
||||
|
||||
// dump internal state to console.log
|
||||
dump(): void;
|
||||
}
|
||||
|
||||
export class JsSyncState {
|
||||
sharedHeads: Heads;
|
||||
lastSentHeads: Heads;
|
||||
theirHeads: Heads | undefined;
|
||||
theirHeed: Heads | undefined;
|
||||
theirHave: SyncHave[] | undefined;
|
||||
sentHashes: Heads;
|
||||
}
|
||||
|
||||
export class SyncState {
|
||||
free(): void;
|
||||
clone(): SyncState;
|
||||
lastSentHeads: Heads;
|
||||
sentHashes: Heads;
|
||||
readonly sharedHeads: Heads;
|
||||
}
|
||||
|
||||
export function init (): Promise<API>;
|
||||
|
18
automerge-wasm/types/package.json
Normal file
18
automerge-wasm/types/package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"collaborators": [
|
||||
"Orion Henry <orion@inkandswitch.com>"
|
||||
],
|
||||
"name": "automerge-types",
|
||||
"description": "typescript types for low level automerge api",
|
||||
"homepage": "https://github.com/automerge/automerge-rs/tree/main/automerge-wasm",
|
||||
"repository": "github:automerge/automerge-rs",
|
||||
"version": "0.1.5",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"package.json",
|
||||
"index.d.ts"
|
||||
],
|
||||
"types": "index.d.ts",
|
||||
"main": ""
|
||||
}
|
49
automerge-wasm/web-index.js
Normal file
49
automerge-wasm/web-index.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
export {
|
||||
loadDoc as load,
|
||||
create,
|
||||
encodeChange,
|
||||
decodeChange,
|
||||
initSyncState,
|
||||
encodeSyncMessage,
|
||||
decodeSyncMessage,
|
||||
encodeSyncState,
|
||||
decodeSyncState,
|
||||
exportSyncState,
|
||||
importSyncState,
|
||||
} from "./bindgen.js"
|
||||
import {
|
||||
loadDoc as load,
|
||||
create,
|
||||
encodeChange,
|
||||
decodeChange,
|
||||
initSyncState,
|
||||
encodeSyncMessage,
|
||||
decodeSyncMessage,
|
||||
encodeSyncState,
|
||||
decodeSyncState,
|
||||
exportSyncState,
|
||||
importSyncState,
|
||||
} from "./bindgen.js"
|
||||
|
||||
let api = {
|
||||
load,
|
||||
create,
|
||||
encodeChange,
|
||||
decodeChange,
|
||||
initSyncState,
|
||||
encodeSyncMessage,
|
||||
decodeSyncMessage,
|
||||
encodeSyncState,
|
||||
decodeSyncState,
|
||||
exportSyncState,
|
||||
importSyncState
|
||||
}
|
||||
|
||||
import wasm_init from "./bindgen.js"
|
||||
|
||||
export function init() {
|
||||
return new Promise((resolve,reject) => wasm_init().then(() => {
|
||||
resolve({ ... api, load, create })
|
||||
}))
|
||||
}
|
||||
|
|
@ -3,11 +3,18 @@ set -e
|
|||
THIS_SCRIPT=$(dirname "$0");
|
||||
WASM_PROJECT=$THIS_SCRIPT/../../automerge-wasm;
|
||||
JS_PROJECT=$THIS_SCRIPT/../../automerge-js;
|
||||
E2E_PROJECT=$THIS_SCRIPT/../../automerge-js/e2e;
|
||||
|
||||
yarn --cwd $E2E_PROJECT install;
|
||||
# This will build the automerge-wasm project, publish it to a local NPM
|
||||
# repository, then run `yarn build` in the `automerge-js` directory with
|
||||
# the local registry
|
||||
yarn --cwd $E2E_PROJECT e2e buildjs;
|
||||
yarn --cwd $JS_PROJECT test
|
||||
yarn --cwd $WASM_PROJECT install;
|
||||
# This will take care of running wasm-pack
|
||||
yarn --cwd $WASM_PROJECT build;
|
||||
# If the dependencies are already installed we delete automerge-wasm. This makes
|
||||
# this script usable for iterative development.
|
||||
if [ -d $JS_PROJECT/node_modules/automerge-wasm ]; then
|
||||
rm -rf $JS_PROJECT/node_modules/automerge-wasm
|
||||
fi
|
||||
# --check-files forces yarn to check if the local dep has changed
|
||||
yarn --cwd $JS_PROJECT install --check-files;
|
||||
yarn --cwd $JS_PROJECT test;
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue