@canvas-js/core
A Canvas application replicates and executes a log of signed actions, sourced from GossipLog, with read/write access to a ModelDB database.
Use this package directly if you want fine-grained control over when an application is started/stopped. Otherwise, you can use useCanvas
in @canvas-js/hooks
, which has the same API, but handles initialization inside React for you.
Table of Contents
Installation
$ npm i @canvas-js/core
Usage
ts
import { Canvas } from "@canvas-js/core"
const app = await Canvas.initialize({
topic: "com.example.my-app",
contract: {
models: {
posts: {
id: "primary",
user: "string",
content: "string",
updated_at: "integer",
},
},
actions: {
async createPost({ content }) {
const { id, chain, address, timestamp } = this
const user = [chain, address].join(":")
await db.posts.set({ id, user, content, updated_at: timestamp })
},
async deletePost({ postId }) {
const { chain, address } = this
const post = await db.posts.get(postId)
if (post === null) {
return
}
const user = [chain, address].join(":")
if (post.user !== user) {
throw new Error("not authorized")
}
await db.posts.delete(postId)
},
},
},
})
await app.actions.createPost({ content: "hello world!" })
const results = await app.db.query("posts", {})
// [
// {
// id: '09p5qn7affkhtbflscr663tet8ddeu41',
// user: 'did:pkh:eip155:1:0x79c5158f81ebb0c2bcF877E9e1813aed2Eb652B7',
// content: 'hello world!',
// updated_at: 1698339861041
// }
// ]
API
ts
import type { DeriveModelTypes, ModelSchema, ModelValue } from "@canvas-js/modeldb"
import type { Awaitable } from "@canvas-js/interfaces"
export type { ModelValue, ModelSchema, DeriveModelTypes, DeriveModelType } from "@canvas-js/modeldb"
export type Contract<
ModelsT extends ModelSchema = ModelSchema,
ActionsT extends Actions<ModelsT> = Actions<ModelsT>,
> = {
models: ModelsT
actions: ActionsT
}
export type Actions<ModelsT extends ModelSchema> = Record<string, ActionImplementation<ModelsT>>
export type ActionImplementation<
ModelsT extends ModelSchema = ModelSchema,
Args extends Array<any> = any,
Result = any,
> = (
this: ActionContext<DeriveModelTypes<ModelsT>>,
// db: ModelAPI<DeriveModelTypes<ModelsT>>,
...args: Args
) => Awaitable<Result>
export type ModelAPI<ModelTypes extends Record<string, ModelValue>> = {
get: <T extends keyof ModelTypes & string>(model: T, key: string) => Promise<ModelTypes[T] | null>
set: <T extends keyof ModelTypes & string>(model: T, value: ModelTypes[T]) => Promise<void>
update: <T extends keyof ModelTypes & string>(model: T, value: Partial<ModelTypes[T]>) => Promise<void>
merge: <T extends keyof ModelTypes & string>(model: T, value: Partial<ModelTypes[T]>) => Promise<void>
delete: <T extends keyof ModelTypes & string>(model: T, key: string) => Promise<void>
transaction: <T>(callback: () => Awaitable<T>) => Promise<T>
}
export type ActionContext<T extends Record<string, ModelValue>> = {
db: ModelAPI<T>
id: string
did: string
address: string
blockhash: string | null
timestamp: number
publicKey: string
}