Skip to content

Commit

Permalink
packages/core: use get helper for #context in runtime impls
Browse files Browse the repository at this point in the history
  • Loading branch information
joeltg committed Feb 6, 2025
1 parent b214154 commit b303063
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 37 deletions.
32 changes: 15 additions & 17 deletions packages/core/src/runtime/ContractRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,33 +92,29 @@ export class ContractRuntime extends AbstractRuntime {
this.#databaseAPI = vm
.wrapObject({
get: vm.wrapFunction((model, key) => {
assert(this.#context !== null, "expected this.#context !== null")
assert(typeof model === "string", 'expected typeof model === "string"')
assert(typeof key === "string", 'expected typeof key === "string"')
return this.#context.getModelValue(model, key)
return this.context.getModelValue(model, key)
}),
set: vm.context.newFunction("set", (modelHandle, valueHandle) => {
assert(this.#context !== null, "expected this.#context !== null")
const model = vm.context.getString(modelHandle)
const value = this.vm.unwrapValue(valueHandle) as ModelValue
this.#context.setModelValue(model, value)
this.context.setModelValue(model, value)
}),
create: vm.context.newFunction("create", (modelHandle, valueHandle) => {
assert(this.#context !== null, "expected this.#context !== null")
const model = vm.context.getString(modelHandle)
const value = this.vm.unwrapValue(valueHandle) as ModelValue
this.#context.setModelValue(model, value)
this.context.setModelValue(model, value)
}),
update: vm.context.newFunction("update", (modelHandle, valueHandle) => {
assert(this.#context !== null, "expected this.#context !== null")
const model = vm.context.getString(modelHandle)
const value = this.vm.unwrapValue(valueHandle) as ModelValue

const promise = vm.context.newPromise()

// TODO: Ensure concurrent merges into the same value don't create a race condition
// if the user doesn't call db.update() with await.
this.#context
this.context
.updateModelValue(model, value)
.then(() => promise.resolve())
.catch((err) => promise.reject())
Expand All @@ -127,15 +123,14 @@ export class ContractRuntime extends AbstractRuntime {
return promise.handle
}),
merge: vm.context.newFunction("merge", (modelHandle, valueHandle) => {
assert(this.#context !== null, "expected this.#context !== null")
const model = vm.context.getString(modelHandle)
const value = this.vm.unwrapValue(valueHandle) as ModelValue

const promise = vm.context.newPromise()

// TODO: Ensure concurrent merges into the same value don't create a race condition
// if the user doesn't call db.update() with await.
this.#context
this.context
.mergeModelValue(model, value)
.then(() => promise.resolve())
.catch((err) => promise.reject())
Expand All @@ -145,10 +140,9 @@ export class ContractRuntime extends AbstractRuntime {
}),

delete: vm.context.newFunction("delete", (modelHandle, keyHandle) => {
assert(this.#context !== null, "expected this.#context !== null")
const model = vm.context.getString(modelHandle)
const key = vm.context.getString(keyHandle)
this.#context.deleteModelValue(model, key)
this.context.deleteModelValue(model, key)
}),
})
.consume(vm.cache)
Expand All @@ -163,6 +157,11 @@ export class ContractRuntime extends AbstractRuntime {
return Object.keys(this.actions)
}

private get context() {
assert(this.#context !== null, "expected this.#context !== null")
return this.#context
}

protected async execute(context: ExecutionContext): Promise<void | any> {
const { publicKey } = context.signature
const { address } = context
Expand All @@ -179,9 +178,7 @@ export class ContractRuntime extends AbstractRuntime {
throw new Error(`invalid action name: ${name}`)
}

this.#context = context

const ctxHandle = this.vm.wrapValue({
const thisHandle = this.vm.wrapValue({
id: context.id,
publicKey,
did,
Expand All @@ -193,7 +190,8 @@ export class ContractRuntime extends AbstractRuntime {
const argHandles = Array.isArray(args) ? args.map(this.vm.wrapValue) : [this.vm.wrapValue(args)]

try {
const result = await this.vm.callAsync(actionHandle, ctxHandle, [this.#databaseAPI, ...argHandles])
this.#context = context
const result = await this.vm.callAsync(actionHandle, thisHandle, [this.#databaseAPI, ...argHandles])

return result.consume((handle) => {
if (this.vm.context.typeof(handle) === "undefined") {
Expand All @@ -204,7 +202,7 @@ export class ContractRuntime extends AbstractRuntime {
})
} finally {
argHandles.map((handle: QuickJSHandle) => handle.dispose())
ctxHandle.dispose()
thisHandle.dispose()
this.#context = null
}
}
Expand Down
38 changes: 18 additions & 20 deletions packages/core/src/runtime/FunctionRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,11 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
promise.link = async (linkModel: string, linkPrimaryKey: string, params?: { through: string }) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
const {
primaryKey: [primaryKey],
} = this.db.models[model]
const target = isSelect ? (value as string) : ((value as ModelValue)[primaryKey] as string)
const modelValue = await this.#context.getModelValue(linkModel, linkPrimaryKey)
const modelValue = await this.context.getModelValue(linkModel, linkPrimaryKey)
assert(modelValue !== null, `db.link(): link from a missing model ${linkModel}.get(${linkPrimaryKey})`)
const backlinkKey = params?.through ?? model
const backlinkProp = this.db.models[linkModel].properties.find((prop) => prop.name === backlinkKey)
Expand All @@ -86,7 +85,7 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
}
if (this.db.models[linkModel] === undefined) throw new Error(`db.link(): no such model "${linkModel}"`)
validateModelValue(this.db.models[linkModel], modelValue)
this.#context.modelEntries[linkModel][linkPrimaryKey] = modelValue
this.context.modelEntries[linkModel][linkPrimaryKey] = modelValue
} finally {
this.releaseLock()
}
Expand All @@ -95,12 +94,11 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
promise.unlink = async (linkModel: string, linkPrimaryKey: string, params?: { through: string }) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
const {
primaryKey: [primaryKey],
} = this.db.models[model]
const target = isSelect ? (value as string) : ((value as ModelValue)[primaryKey] as string)
const modelValue = await this.#context.getModelValue(linkModel, linkPrimaryKey)
const modelValue = await this.context.getModelValue(linkModel, linkPrimaryKey)
assert(modelValue !== null, `db.unlink(): called on a missing model ${linkModel}.get(${linkPrimaryKey})`)
const backlinkKey = params?.through ?? model
const backlinkProp = this.db.models[linkModel].properties.find((prop) => prop.name === backlinkKey)
Expand All @@ -115,7 +113,7 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
throw new Error(`db.unlink(): link from ${linkModel} ${backlinkKey} must be a relation`)
}
validateModelValue(this.db.models[linkModel], modelValue)
this.#context.modelEntries[linkModel][linkPrimaryKey] = modelValue
this.context.modelEntries[linkModel][linkPrimaryKey] = modelValue
} finally {
this.releaseLock()
}
Expand All @@ -128,8 +126,7 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
get: async <T extends keyof DeriveModelTypes<ModelsT> & string>(model: T, key: string) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
const result = await this.#context.getModelValue(model, key)
const result = await this.context.getModelValue(model, key)
return result as DeriveModelTypes<ModelsT>[T]
} finally {
this.releaseLock()
Expand All @@ -139,44 +136,39 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
set: getChainableMethod(async (model, value) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
this.#context.setModelValue(model, value)
this.context.setModelValue(model, value)
} finally {
this.releaseLock()
}
}),
create: getChainableMethod(async (model, value) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
this.#context.setModelValue(model, value)
this.context.setModelValue(model, value)
} finally {
this.releaseLock()
}
}),
update: getChainableMethod(async (model, value) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
await this.#context.updateModelValue(model, value)
await this.context.updateModelValue(model, value)
} finally {
this.releaseLock()
}
}),
merge: getChainableMethod(async (model, value) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
await this.#context.mergeModelValue(model, value)
await this.context.mergeModelValue(model, value)
} finally {
this.releaseLock()
}
}),
delete: async (model: string, key: string) => {
await this.acquireLock()
try {
assert(this.#context !== null, "expected this.#context !== null")
this.#context.deleteModelValue(model, key)
this.context.deleteModelValue(model, key)
} finally {
this.releaseLock()
}
Expand All @@ -190,6 +182,11 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
return Object.keys(this.actions)
}

private get context() {
assert(this.#context !== null, "expected this.#context !== null")
return this.#context
}

protected async execute(context: ExecutionContext): Promise<void | any> {
const { publicKey } = context.signature
const { address } = context
Expand All @@ -208,7 +205,7 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
this.#context = context

try {
const actionContext: ActionContext<DeriveModelTypes<ModelsT>> = {
const thisValue: ActionContext<DeriveModelTypes<ModelsT>> = {
db: this.#db,
id: context.id,
publicKey,
Expand All @@ -217,7 +214,8 @@ export class FunctionRuntime<ModelsT extends ModelSchema> extends AbstractRuntim
blockhash: blockhash ?? null,
timestamp,
}
const result = await action.apply(actionContext, Array.isArray(args) ? [this.#db, ...args] : [this.#db, args])

const result = await action.apply(thisValue, Array.isArray(args) ? [this.#db, ...args] : [this.#db, args])
while (this.#waiting > 0) {
await new Promise((resolve) => setTimeout(resolve, 10))
}
Expand Down

0 comments on commit b303063

Please sign in to comment.