Skip to content

Commit 4e3d36d

Browse files
committed
Basic procedure calling
1 parent 64606ac commit 4e3d36d

File tree

17 files changed

+477
-180
lines changed

17 files changed

+477
-180
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { AlgebraicType, ProductType } from '../lib/algebraic_type';
2+
import type { ConnectionId } from '../lib/connection_id';
3+
import type { Identity } from '../lib/identity';
4+
import type { Timestamp } from '../lib/timestamp';
5+
import type { ParamsObj } from './reducers';
6+
import { MODULE_DEF, type UntypedSchemaDef } from './schema';
7+
import type { Infer, InferTypeOfRow, TypeBuilder } from './type_builders';
8+
import { bsatnBaseSize } from './util';
9+
10+
export type ProcedureFn<
11+
S extends UntypedSchemaDef,
12+
Params extends ParamsObj,
13+
Ret extends TypeBuilder<any, any>,
14+
> = (ctx: ProcedureCtx<S>, args: InferTypeOfRow<Params>) => Infer<Ret>;
15+
16+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
17+
export interface ProcedureCtx<S extends UntypedSchemaDef> {
18+
readonly sender: Identity;
19+
readonly identity: Identity;
20+
readonly timestamp: Timestamp;
21+
readonly connectionId: ConnectionId | null;
22+
}
23+
24+
export function procedure<
25+
S extends UntypedSchemaDef,
26+
Params extends ParamsObj,
27+
Ret extends TypeBuilder<any, any>,
28+
>(name: string, params: Params, ret: Ret, fn: ProcedureFn<S, Params, Ret>) {
29+
const paramsType: ProductType = {
30+
elements: Object.entries(params).map(([n, c]) => ({
31+
name: n,
32+
algebraicType:
33+
'typeBuilder' in c ? c.typeBuilder.algebraicType : c.algebraicType,
34+
})),
35+
};
36+
const returnType = ret.algebraicType;
37+
38+
MODULE_DEF.miscExports.push({
39+
tag: 'Procedure',
40+
value: {
41+
name,
42+
params: paramsType,
43+
returnType,
44+
},
45+
});
46+
47+
PROCEDURES.push({
48+
fn,
49+
paramsType,
50+
returnType,
51+
returnTypeBaseSize: bsatnBaseSize(MODULE_DEF.typespace, returnType),
52+
});
53+
}
54+
55+
export const PROCEDURES: Array<{
56+
fn: ProcedureFn<any, any, any>;
57+
paramsType: ProductType;
58+
returnType: AlgebraicType;
59+
returnTypeBaseSize: number;
60+
}> = [];

crates/bindings-typescript/src/lib/schema.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
} from './views';
4848
import RawIndexDefV9 from './autogen/raw_index_def_v_9_type';
4949
import type { IndexOpts } from './indexes';
50+
import { procedure, type ProcedureFn } from './procedures';
5051

5152
export type TableNamesOf<S extends UntypedSchemaDef> =
5253
S['tables'][number]['name'];
@@ -547,6 +548,32 @@ class Schema<S extends UntypedSchemaDef> {
547548
// }
548549
// }
549550

551+
procedure<Params extends ParamsObj, Ret extends TypeBuilder<any, any>>(
552+
name: string,
553+
params: Params,
554+
ret: Ret,
555+
fn: ProcedureFn<S, Params, Ret>
556+
): ProcedureFn<S, Params, Ret>;
557+
procedure<Ret extends TypeBuilder<any, any>>(
558+
name: string,
559+
ret: Ret,
560+
fn: ProcedureFn<S, {}, Ret>
561+
): ProcedureFn<S, {}, Ret>;
562+
procedure<Params extends ParamsObj, Ret extends TypeBuilder<any, any>>(
563+
name: string,
564+
paramsOrRet: Ret | Params,
565+
retOrFn: ProcedureFn<S, {}, Ret> | Ret,
566+
maybeFn?: ProcedureFn<S, Params, Ret>
567+
): ProcedureFn<S, Params, Ret> {
568+
if (typeof retOrFn === 'function') {
569+
procedure(name, {}, paramsOrRet as Ret, retOrFn);
570+
return retOrFn;
571+
} else {
572+
procedure(name, paramsOrRet as Params, retOrFn, maybeFn!);
573+
return maybeFn!;
574+
}
575+
}
576+
550577
clientVisibilityFilter = {
551578
sql(filter: string): void {
552579
MODULE_DEF.rowLevelSecurity.push({ sql: filter });
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { AlgebraicType, ProductType } from '../lib/algebraic_type';
2+
import BinaryReader from '../lib/binary_reader';
3+
import BinaryWriter from '../lib/binary_writer';
4+
import type { ConnectionId } from '../lib/connection_id';
5+
import { Identity } from '../lib/identity';
6+
import { PROCEDURES, type ProcedureCtx } from '../lib/procedures';
7+
import { MODULE_DEF, type UntypedSchemaDef } from '../lib/schema';
8+
import type { Timestamp } from '../lib/timestamp';
9+
import { sys } from './runtime';
10+
11+
const { freeze } = Object;
12+
13+
export function callProcedure(
14+
id: number,
15+
sender: Identity,
16+
connectionId: ConnectionId | null,
17+
timestamp: Timestamp,
18+
argsBuf: Uint8Array
19+
): Uint8Array {
20+
const { fn, paramsType, returnType, returnTypeBaseSize } = PROCEDURES[id];
21+
const args = ProductType.deserializeValue(
22+
new BinaryReader(argsBuf),
23+
paramsType,
24+
MODULE_DEF.typespace
25+
);
26+
27+
const ctx: ProcedureCtx<UntypedSchemaDef> = freeze({
28+
sender,
29+
timestamp,
30+
connectionId,
31+
get identity() {
32+
return new Identity(sys.identity().__identity__);
33+
},
34+
});
35+
36+
const ret = fn(ctx, args);
37+
const retBuf = new BinaryWriter(returnTypeBaseSize);
38+
AlgebraicType.serializeValue(retBuf, returnType, ret, MODULE_DEF.typespace);
39+
return retBuf.getBuffer();
40+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { register_hooks } from 'spacetime:sys@1.0';
22
import { register_hooks as register_hooks_v1_1 } from 'spacetime:sys@1.1';
3-
import { hooks, hooks_v1_1 } from './runtime';
3+
import { register_hooks as register_hooks_v1_2 } from 'spacetime:sys@1.2';
4+
import { hooks, hooks_v1_1, hooks_v1_2 } from './runtime';
45

56
register_hooks(hooks);
67
register_hooks_v1_1(hooks_v1_1);
8+
register_hooks_v1_2(hooks_v1_2);

crates/bindings-typescript/src/server/runtime.ts

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,48 @@
1-
import { AlgebraicType } from '../lib/algebraic_type';
1+
import * as _syscalls1_0 from 'spacetime:sys@1.0';
2+
import * as _syscalls1_2 from 'spacetime:sys@1.2';
3+
4+
import type { ModuleHooks, u16, u32 } from 'spacetime:sys@1.0';
5+
import { AlgebraicType, ProductType } from '../lib/algebraic_type';
26
import RawModuleDef from '../lib/autogen/raw_module_def_type';
37
import type RawModuleDefV9 from '../lib/autogen/raw_module_def_v_9_type';
48
import type RawTableDefV9 from '../lib/autogen/raw_table_def_v_9_type';
59
import type Typespace from '../lib/autogen/typespace_type';
6-
import { ConnectionId } from '../lib/connection_id';
7-
import { Identity } from '../lib/identity';
8-
import { Timestamp } from '../lib/timestamp';
910
import BinaryReader from '../lib/binary_reader';
1011
import BinaryWriter from '../lib/binary_writer';
11-
import { SenderError, SpacetimeHostError } from './errors';
12-
import { Range, type Bound } from './range';
12+
import { ConnectionId } from '../lib/connection_id';
13+
import { Identity } from '../lib/identity';
1314
import {
1415
type Index,
1516
type IndexVal,
16-
type UniqueIndex,
1717
type RangedIndex,
18+
type UniqueIndex,
1819
} from '../lib/indexes';
19-
import { type RowType, type Table, type TableMethods } from '../lib/table';
20+
import { callProcedure } from './procedures';
2021
import {
21-
type ReducerCtx,
2222
REDUCERS,
23-
type JwtClaims,
2423
type AuthCtx,
2524
type JsonObject,
25+
type JwtClaims,
26+
type ReducerCtx,
2627
} from '../lib/reducers';
2728
import { MODULE_DEF } from '../lib/schema';
28-
29-
import * as _syscalls from 'spacetime:sys@1.0';
30-
import type { u16, u32, ModuleHooks } from 'spacetime:sys@1.0';
31-
import type { DbView } from './db_view';
32-
import { toCamelCase } from '../lib/util';
29+
import { type RowType, type Table, type TableMethods } from '../lib/table';
30+
import { Timestamp } from '../lib/timestamp';
3331
import type { Infer } from '../lib/type_builders';
34-
import { bsatnBaseSize } from '../lib/util';
32+
import { bsatnBaseSize, toCamelCase } from '../lib/util';
3533
import {
3634
ANON_VIEWS,
3735
VIEWS,
3836
type AnonymousViewCtx,
3937
type ViewCtx,
4038
} from '../lib/views';
39+
import type { DbView } from './db_view';
40+
import { SenderError, SpacetimeHostError } from './errors';
41+
import { Range, type Bound } from './range';
4142

4243
const { freeze } = Object;
4344

44-
const sys: typeof _syscalls = freeze(
45-
Object.fromEntries(
46-
Object.entries(_syscalls).map(([name, syscall]) => [
47-
name,
48-
wrapSyscall(syscall),
49-
])
50-
) as typeof _syscalls
51-
);
45+
export const sys = freeze(wrapSyscalls(_syscalls1_0, _syscalls1_2));
5246

5347
export function parseJsonObject(json: string): JsonObject {
5448
let value: unknown;
@@ -235,9 +229,9 @@ export const hooks_v1_1: import('spacetime:sys@1.1').ModuleHooks = {
235229
// at runtime
236230
db: getDbView(),
237231
});
238-
const args = AlgebraicType.deserializeValue(
232+
const args = ProductType.deserializeValue(
239233
new BinaryReader(argsBuf),
240-
AlgebraicType.Product(params),
234+
params,
241235
MODULE_DEF.typespace
242236
);
243237
const ret = fn(ctx, args);
@@ -253,9 +247,9 @@ export const hooks_v1_1: import('spacetime:sys@1.1').ModuleHooks = {
253247
// at runtime
254248
db: getDbView(),
255249
});
256-
const args = AlgebraicType.deserializeValue(
250+
const args = ProductType.deserializeValue(
257251
new BinaryReader(argsBuf),
258-
AlgebraicType.Product(params),
252+
params,
259253
MODULE_DEF.typespace
260254
);
261255
const ret = fn(ctx, args);
@@ -265,6 +259,18 @@ export const hooks_v1_1: import('spacetime:sys@1.1').ModuleHooks = {
265259
},
266260
};
267261

262+
export const hooks_v1_2: import('spacetime:sys@1.2').ModuleHooks = {
263+
__call_procedure__(id, sender, connection_id, timestamp, args) {
264+
return callProcedure(
265+
id,
266+
new Identity(sender),
267+
ConnectionId.nullIfZero(new ConnectionId(connection_id)),
268+
new Timestamp(timestamp),
269+
args
270+
);
271+
},
272+
};
273+
268274
let DB_VIEW: DbView<any> | null = null;
269275
function getDbView() {
270276
DB_VIEW ??= makeDbView(MODULE_DEF);
@@ -587,6 +593,21 @@ class TableIterator implements IterableIterator<any, undefined> {
587593
}
588594
}
589595

596+
type Intersections<Ts extends readonly any[]> = Ts extends [
597+
infer T,
598+
...infer Rest,
599+
]
600+
? T & Intersections<Rest>
601+
: unknown;
602+
603+
function wrapSyscalls<
604+
Modules extends Record<string, (...args: any[]) => any>[],
605+
>(...modules: Modules): Intersections<Modules> {
606+
return Object.fromEntries(
607+
modules.flatMap(Object.entries).map(([k, v]) => [k, wrapSyscall(v)])
608+
) as Intersections<Modules>;
609+
}
610+
590611
function wrapSyscall<F extends (...args: any[]) => any>(
591612
func: F
592613
): (...args: Parameters<F>) => ReturnType<F> {

crates/bindings-typescript/src/server/sys.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,17 @@ declare module 'spacetime:sys@1.1' {
7575

7676
export function register_hooks(hooks: ModuleHooks);
7777
}
78+
79+
declare module 'spacetime:sys@1.2' {
80+
export type ModuleHooks = {
81+
__call_procedure__(
82+
id: u32,
83+
sender: u256,
84+
connection_id: u128,
85+
timestamp: bigint,
86+
args: Uint8Array
87+
): Uint8Array;
88+
};
89+
90+
export function register_hooks(hooks: ModuleHooks);
91+
}

crates/core/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ pub enum NodesError {
267267
BadColumn,
268268
#[error("can't perform operation; not inside transaction")]
269269
NotInTransaction,
270+
#[error("can't perform operation; not inside anonymous transaction")]
271+
NotInAnonTransaction,
270272
#[error("ABI call not allowed while holding open a transaction: {0}")]
271273
WouldBlockTransaction(AbiCall),
272274
#[error("table with name {0:?} already exists")]

crates/core/src/host/host_controller.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ pub struct ProcedureCallResult {
179179
pub start_timestamp: Timestamp,
180180
}
181181

182+
#[derive(Debug)]
182183
pub struct CallProcedureReturn {
183184
pub result: Result<ProcedureCallResult, ProcedureCallError>,
184185
pub tx_offset: Option<TransactionOffset>,

0 commit comments

Comments
 (0)