mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-12 02:37:53 -04:00
Merge branch 'master' into tyler/translate-smoketests
This commit is contained in:
@@ -1 +1,3 @@
|
||||
**/module_bindings/** linguist-generated=true eol=lf
|
||||
/docs/llms/** linguist-generated=true
|
||||
/docs/llms/*-details.json linguist-generated=false
|
||||
|
||||
@@ -15,8 +15,16 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
# pnpm is required for regenerating the typescript bindings
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: true
|
||||
- name: Verify that upgrade-version still works
|
||||
run: cargo bump-versions 123.456.789 --rust-and-cli --csharp --typescript
|
||||
run: cargo bump-versions 123.456.789 --rust-and-cli --csharp --typescript --accept-snapshots
|
||||
- name: Show diff
|
||||
run: git diff HEAD
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/rust,node,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=rust,node,visualstudiocode
|
||||
|
||||
perf.data
|
||||
perf.data.old
|
||||
flamegraph.html
|
||||
flamegraphs/*.svg
|
||||
flamegraphs/flamegraph.folded
|
||||
|
||||
|
||||
@@ -71,6 +71,12 @@
|
||||
"import": "./dist/server/index.mjs",
|
||||
"require": "./dist/server/index.cjs",
|
||||
"default": "./dist/server/index.mjs"
|
||||
},
|
||||
"./vue": {
|
||||
"types": "./dist/vue/index.d.ts",
|
||||
"import": "./dist/vue/index.mjs",
|
||||
"require": "./dist/vue/index.cjs",
|
||||
"default": "./dist/vue/index.mjs"
|
||||
}
|
||||
},
|
||||
"size-limit": [
|
||||
@@ -161,12 +167,16 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0-0 || ^19.0.0",
|
||||
"vue": "^3.3.0",
|
||||
"undici": "^6.19.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
},
|
||||
"undici": {
|
||||
"optional": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import {
|
||||
defineComponent,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
provide,
|
||||
reactive,
|
||||
type PropType,
|
||||
type Slot,
|
||||
} from 'vue';
|
||||
import {
|
||||
DbConnectionBuilder,
|
||||
type DbConnectionImpl,
|
||||
type ErrorContextInterface,
|
||||
type RemoteModuleOf,
|
||||
} from '../sdk/db_connection_impl';
|
||||
import { ConnectionId } from '../lib/connection_id';
|
||||
import {
|
||||
SPACETIMEDB_INJECTION_KEY,
|
||||
type ConnectionState,
|
||||
} from './connection_state';
|
||||
|
||||
export interface SpacetimeDBProviderProps<
|
||||
DbConnection extends DbConnectionImpl<any>,
|
||||
> {
|
||||
connectionBuilder: DbConnectionBuilder<DbConnection>;
|
||||
}
|
||||
|
||||
let connRef: DbConnectionImpl<any> | null = null;
|
||||
let cleanupTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function setupConnection<DbConnection extends DbConnectionImpl<any>>(
|
||||
connectionBuilder: DbConnectionBuilder<DbConnection>
|
||||
): {
|
||||
state: ConnectionState;
|
||||
cleanup: () => void;
|
||||
} {
|
||||
const getConnection = <T extends DbConnectionImpl<any>>() =>
|
||||
connRef as T | null;
|
||||
|
||||
const state = reactive<ConnectionState>({
|
||||
isActive: false,
|
||||
identity: undefined,
|
||||
token: undefined,
|
||||
connectionId: ConnectionId.random(),
|
||||
connectionError: undefined,
|
||||
getConnection,
|
||||
});
|
||||
|
||||
provide(SPACETIMEDB_INJECTION_KEY, state);
|
||||
|
||||
let onConnectCallback: ((conn: DbConnection) => void) | null = null;
|
||||
let onDisconnectCallback:
|
||||
| ((ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>) => void)
|
||||
| null = null;
|
||||
let onConnectErrorCallback:
|
||||
| ((
|
||||
ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,
|
||||
err: Error
|
||||
) => void)
|
||||
| null = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (cleanupTimeoutId) {
|
||||
clearTimeout(cleanupTimeoutId);
|
||||
cleanupTimeoutId = null;
|
||||
}
|
||||
|
||||
if (!connRef) {
|
||||
connRef = connectionBuilder.build();
|
||||
}
|
||||
|
||||
onConnectCallback = (conn: DbConnection) => {
|
||||
state.isActive = conn.isActive;
|
||||
state.identity = conn.identity;
|
||||
state.token = conn.token;
|
||||
state.connectionId = conn.connectionId;
|
||||
state.connectionError = undefined;
|
||||
};
|
||||
|
||||
onDisconnectCallback = (
|
||||
ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>
|
||||
) => {
|
||||
state.isActive = ctx.isActive;
|
||||
};
|
||||
|
||||
onConnectErrorCallback = (
|
||||
ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,
|
||||
err: Error
|
||||
) => {
|
||||
state.isActive = ctx.isActive;
|
||||
state.connectionError = err;
|
||||
};
|
||||
|
||||
connectionBuilder.onConnect(onConnectCallback);
|
||||
connectionBuilder.onDisconnect(onDisconnectCallback);
|
||||
connectionBuilder.onConnectError(onConnectErrorCallback);
|
||||
|
||||
const conn = connRef;
|
||||
if (conn) {
|
||||
state.isActive = conn.isActive;
|
||||
state.identity = conn.identity;
|
||||
state.token = conn.token;
|
||||
state.connectionId = conn.connectionId;
|
||||
}
|
||||
});
|
||||
|
||||
const cleanup = () => {
|
||||
if (connRef) {
|
||||
if (onConnectCallback) {
|
||||
connRef.removeOnConnect?.(onConnectCallback as any);
|
||||
}
|
||||
if (onDisconnectCallback) {
|
||||
connRef.removeOnDisconnect?.(onDisconnectCallback as any);
|
||||
}
|
||||
if (onConnectErrorCallback) {
|
||||
connRef.removeOnConnectError?.(onConnectErrorCallback as any);
|
||||
}
|
||||
|
||||
cleanupTimeoutId = setTimeout(() => {
|
||||
connRef?.disconnect();
|
||||
connRef = null;
|
||||
cleanupTimeoutId = null;
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
onUnmounted(cleanup);
|
||||
|
||||
return { state, cleanup };
|
||||
}
|
||||
|
||||
export const SpacetimeDBProvider = defineComponent({
|
||||
name: 'SpacetimeDBProvider',
|
||||
|
||||
props: {
|
||||
connectionBuilder: {
|
||||
type: Object as PropType<DbConnectionBuilder<any>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { slots }) {
|
||||
setupConnection(props.connectionBuilder);
|
||||
|
||||
return () => {
|
||||
const defaultSlot = slots.default as Slot | undefined;
|
||||
return defaultSlot ? defaultSlot() : null;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export function useSpacetimeDBProvider<
|
||||
DbConnection extends DbConnectionImpl<any>,
|
||||
>(connectionBuilder: DbConnectionBuilder<DbConnection>): ConnectionState {
|
||||
const { state } = setupConnection(connectionBuilder);
|
||||
return state;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { InjectionKey } from 'vue';
|
||||
import type { ConnectionId } from '../lib/connection_id';
|
||||
import type { Identity } from '../lib/identity';
|
||||
import type { DbConnectionImpl } from '../sdk/db_connection_impl';
|
||||
|
||||
export interface ConnectionState {
|
||||
isActive: boolean;
|
||||
identity?: Identity;
|
||||
token?: string;
|
||||
connectionId: ConnectionId;
|
||||
connectionError?: Error;
|
||||
getConnection<
|
||||
DbConnection extends DbConnectionImpl<any>,
|
||||
>(): DbConnection | null;
|
||||
}
|
||||
|
||||
export const SPACETIMEDB_INJECTION_KEY = Symbol(
|
||||
'spacetimedb'
|
||||
) as InjectionKey<ConnectionState>;
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './SpacetimeDBProvider.ts';
|
||||
export { useSpacetimeDB } from './useSpacetimeDB.ts';
|
||||
export { useTable, where, eq } from './useTable.ts';
|
||||
export { useReducer } from './useReducer.ts';
|
||||
@@ -0,0 +1,56 @@
|
||||
import { shallowRef, watch, onUnmounted } from 'vue';
|
||||
import { useSpacetimeDB } from './useSpacetimeDB';
|
||||
import type { InferTypeOfRow } from '../lib/type_builders';
|
||||
import type { UntypedReducerDef } from '../sdk/reducers';
|
||||
import type { Prettify } from '../lib/type_util';
|
||||
|
||||
type IsEmptyObject<T> = [keyof T] extends [never] ? true : false;
|
||||
type MaybeParams<T> = IsEmptyObject<T> extends true ? [] : [params: T];
|
||||
|
||||
type ParamsType<R extends UntypedReducerDef> = MaybeParams<
|
||||
Prettify<InferTypeOfRow<R['params']>>
|
||||
>;
|
||||
|
||||
export function useReducer<ReducerDef extends UntypedReducerDef>(
|
||||
reducerDef: ReducerDef
|
||||
): (...params: ParamsType<ReducerDef>) => void {
|
||||
const conn = useSpacetimeDB();
|
||||
const reducerName = reducerDef.accessorName;
|
||||
|
||||
const queueRef = shallowRef<ParamsType<ReducerDef>[]>([]);
|
||||
|
||||
const stopWatch = watch(
|
||||
() => conn.isActive,
|
||||
() => {
|
||||
const connection = conn.getConnection();
|
||||
if (!connection) return;
|
||||
|
||||
const fn = (connection.reducers as any)[reducerName] as (
|
||||
...p: ParamsType<ReducerDef>
|
||||
) => void;
|
||||
if (queueRef.value.length) {
|
||||
const pending = queueRef.value.splice(0);
|
||||
for (const params of pending) {
|
||||
fn(...params);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
stopWatch();
|
||||
});
|
||||
|
||||
return (...params: ParamsType<ReducerDef>) => {
|
||||
const connection = conn.getConnection();
|
||||
if (!connection) {
|
||||
queueRef.value.push(params);
|
||||
return;
|
||||
}
|
||||
const fn = (connection.reducers as any)[reducerName] as (
|
||||
...p: ParamsType<ReducerDef>
|
||||
) => void;
|
||||
fn(...params);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { inject } from 'vue';
|
||||
import {
|
||||
SPACETIMEDB_INJECTION_KEY,
|
||||
type ConnectionState,
|
||||
} from './connection_state';
|
||||
|
||||
export function useSpacetimeDB(): ConnectionState {
|
||||
const context = inject(SPACETIMEDB_INJECTION_KEY);
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useSpacetimeDB must be used within a SpacetimeDBProvider component. ' +
|
||||
'Did you forget to add a `SpacetimeDBProvider` to your component tree?'
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
import {
|
||||
onUnmounted,
|
||||
readonly,
|
||||
ref,
|
||||
shallowRef,
|
||||
watch,
|
||||
type DeepReadonly,
|
||||
type Ref,
|
||||
} from 'vue';
|
||||
import { useSpacetimeDB } from './useSpacetimeDB';
|
||||
|
||||
import type { EventContextInterface } from '../sdk/db_connection_impl';
|
||||
import type { UntypedRemoteModule } from '../sdk/spacetime_module';
|
||||
import type { RowType, UntypedTableDef } from '../lib/table';
|
||||
import type { Prettify } from '../lib/type_util';
|
||||
|
||||
export interface UseTableCallbacks<RowType> {
|
||||
onInsert?: (row: RowType) => void;
|
||||
onDelete?: (row: RowType) => void;
|
||||
onUpdate?: (oldRow: RowType, newRow: RowType) => void;
|
||||
}
|
||||
|
||||
export type Value = string | number | boolean;
|
||||
|
||||
export type Expr<Column extends string> =
|
||||
| { type: 'eq'; key: Column; value: Value }
|
||||
| { type: 'and'; children: Expr<Column>[] }
|
||||
| { type: 'or'; children: Expr<Column>[] };
|
||||
|
||||
export const eq = <Column extends string>(
|
||||
key: Column,
|
||||
value: Value
|
||||
): Expr<Column> => ({ type: 'eq', key, value });
|
||||
|
||||
export const and = <Column extends string>(
|
||||
...children: Expr<Column>[]
|
||||
): Expr<Column> => {
|
||||
const flat: Expr<Column>[] = [];
|
||||
for (const c of children) {
|
||||
if (!c) continue;
|
||||
if (c.type === 'and') flat.push(...c.children);
|
||||
else flat.push(c);
|
||||
}
|
||||
const pruned = flat.filter(Boolean);
|
||||
if (pruned.length === 0) return { type: 'and', children: [] };
|
||||
if (pruned.length === 1) return pruned[0];
|
||||
return { type: 'and', children: pruned };
|
||||
};
|
||||
|
||||
export const or = <Column extends string>(
|
||||
...children: Expr<Column>[]
|
||||
): Expr<Column> => {
|
||||
const flat: Expr<Column>[] = [];
|
||||
for (const c of children) {
|
||||
if (!c) continue;
|
||||
if (c.type === 'or') flat.push(...c.children);
|
||||
else flat.push(c);
|
||||
}
|
||||
const pruned = flat.filter(Boolean);
|
||||
if (pruned.length === 0) return { type: 'or', children: [] };
|
||||
if (pruned.length === 1) return pruned[0];
|
||||
return { type: 'or', children: pruned };
|
||||
};
|
||||
|
||||
export const isEq = <Column extends string>(
|
||||
e: Expr<Column>
|
||||
): e is Extract<Expr<Column>, { type: 'eq' }> => e.type === 'eq';
|
||||
export const isAnd = <Column extends string>(
|
||||
e: Expr<Column>
|
||||
): e is Extract<Expr<Column>, { type: 'and' }> => e.type === 'and';
|
||||
export const isOr = <Column extends string>(
|
||||
e: Expr<Column>
|
||||
): e is Extract<Expr<Column>, { type: 'or' }> => e.type === 'or';
|
||||
|
||||
export function evaluate<Column extends string>(
|
||||
expr: Expr<Column>,
|
||||
row: Record<Column, any>
|
||||
): boolean {
|
||||
switch (expr.type) {
|
||||
case 'eq': {
|
||||
const v = row[expr.key];
|
||||
if (
|
||||
typeof v === 'string' ||
|
||||
typeof v === 'number' ||
|
||||
typeof v === 'boolean'
|
||||
) {
|
||||
return v === expr.value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case 'and':
|
||||
return (
|
||||
expr.children.length === 0 || expr.children.every(c => evaluate(c, row))
|
||||
);
|
||||
case 'or':
|
||||
return (
|
||||
expr.children.length !== 0 && expr.children.some(c => evaluate(c, row))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(v: Value): string {
|
||||
switch (typeof v) {
|
||||
case 'string':
|
||||
return `'${v.replace(/'/g, "''")}'`;
|
||||
case 'number':
|
||||
return Number.isFinite(v) ? String(v) : `'${String(v)}'`;
|
||||
case 'boolean':
|
||||
return v ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeIdent(id: string): string {
|
||||
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(id)) return id;
|
||||
return `"${id.replace(/"/g, '""')}"`;
|
||||
}
|
||||
|
||||
function parenthesize(s: string): string {
|
||||
if (!s.includes(' AND ') && !s.includes(' OR ')) return s;
|
||||
return `(${s})`;
|
||||
}
|
||||
|
||||
export function toString<TableDef extends UntypedTableDef>(
|
||||
tableDef: TableDef,
|
||||
expr: Expr<ColumnsFromRow<RowType<TableDef>>>
|
||||
): string {
|
||||
switch (expr.type) {
|
||||
case 'eq': {
|
||||
const key = tableDef.columns[expr.key].columnMetadata.name ?? expr.key;
|
||||
return `${escapeIdent(key)} = ${formatValue(expr.value)}`;
|
||||
}
|
||||
case 'and':
|
||||
return parenthesize(
|
||||
expr.children.map(expr => toString(tableDef, expr)).join(' AND ')
|
||||
);
|
||||
case 'or':
|
||||
return parenthesize(
|
||||
expr.children.map(expr => toString(tableDef, expr)).join(' OR ')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just the identity function to make things look like SQL.
|
||||
* @param expr
|
||||
* @returns
|
||||
*/
|
||||
export function where<Column extends string>(expr: Expr<Column>): Expr<Column> {
|
||||
return expr;
|
||||
}
|
||||
|
||||
type MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';
|
||||
|
||||
function classifyMembership<
|
||||
Col extends string,
|
||||
R extends Record<string, unknown>,
|
||||
>(where: Expr<Col> | undefined, oldRow: R, newRow: R): MembershipChange {
|
||||
// No filter: everything is in, so updates are always "stayIn".
|
||||
if (!where) {
|
||||
return 'stayIn';
|
||||
}
|
||||
|
||||
const oldIn = evaluate(where, oldRow);
|
||||
const newIn = evaluate(where, newRow);
|
||||
|
||||
if (oldIn && !newIn) {
|
||||
return 'leave';
|
||||
}
|
||||
if (!oldIn && newIn) {
|
||||
return 'enter';
|
||||
}
|
||||
if (oldIn && newIn) {
|
||||
return 'stayIn';
|
||||
}
|
||||
return 'stayOut';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the column names from a RowType whose values are of type Value.
|
||||
* Note that this will exclude columns that are of type object, array, etc.
|
||||
*/
|
||||
type ColumnsFromRow<R> = {
|
||||
[K in keyof R]-?: R[K] extends Value | undefined ? K : never;
|
||||
}[keyof R] &
|
||||
string;
|
||||
|
||||
export function useTable<TableDef extends UntypedTableDef>(
|
||||
tableDef: TableDef,
|
||||
where: Expr<ColumnsFromRow<RowType<TableDef>>>,
|
||||
callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>
|
||||
): [
|
||||
DeepReadonly<Ref<readonly Prettify<RowType<TableDef>>[]>>,
|
||||
DeepReadonly<Ref<boolean>>,
|
||||
];
|
||||
|
||||
export function useTable<TableDef extends UntypedTableDef>(
|
||||
tableDef: TableDef,
|
||||
callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>
|
||||
): [
|
||||
DeepReadonly<Ref<readonly Prettify<RowType<TableDef>>[]>>,
|
||||
DeepReadonly<Ref<boolean>>,
|
||||
];
|
||||
|
||||
export function useTable<TableDef extends UntypedTableDef>(
|
||||
tableDef: TableDef,
|
||||
whereClauseOrCallbacks?:
|
||||
| Expr<ColumnsFromRow<RowType<TableDef>>>
|
||||
| UseTableCallbacks<RowType<TableDef>>,
|
||||
callbacks?: UseTableCallbacks<RowType<TableDef>>
|
||||
): [
|
||||
DeepReadonly<Ref<readonly Prettify<RowType<TableDef>>[]>>,
|
||||
DeepReadonly<Ref<boolean>>,
|
||||
] {
|
||||
type Row = RowType<TableDef>;
|
||||
const tableName = tableDef.name;
|
||||
const accessorName = tableDef.accessorName;
|
||||
|
||||
let whereClause: Expr<ColumnsFromRow<Row>> | undefined;
|
||||
if (
|
||||
whereClauseOrCallbacks &&
|
||||
typeof whereClauseOrCallbacks === 'object' &&
|
||||
'type' in whereClauseOrCallbacks
|
||||
) {
|
||||
whereClause = whereClauseOrCallbacks as Expr<ColumnsFromRow<Row>>;
|
||||
} else {
|
||||
callbacks = whereClauseOrCallbacks as UseTableCallbacks<Row> | undefined;
|
||||
}
|
||||
|
||||
let conn;
|
||||
try {
|
||||
conn = useSpacetimeDB();
|
||||
} catch {
|
||||
throw new Error(
|
||||
'Could not find SpacetimeDB client! Did you forget to add a ' +
|
||||
'`SpacetimeDBProvider`? `useTable` must be used in a Vue component tree ' +
|
||||
'under a `SpacetimeDBProvider` component.'
|
||||
);
|
||||
}
|
||||
|
||||
const rows = shallowRef<readonly Prettify<Row>[]>([]);
|
||||
const isReady = ref(false);
|
||||
|
||||
const query =
|
||||
`SELECT * FROM ${tableName}` +
|
||||
(whereClause ? ` WHERE ${toString(tableDef, whereClause)}` : '');
|
||||
|
||||
let latestTransactionEvent: any = null;
|
||||
let unsubscribeFromTable: (() => void) | null = null;
|
||||
let subscriptionHandle: { unsubscribe: () => void } | null = null;
|
||||
|
||||
const computeFilteredRows = (): readonly Prettify<Row>[] => {
|
||||
const connection = conn.getConnection();
|
||||
if (!connection) return [];
|
||||
|
||||
const table = connection.db[accessorName];
|
||||
if (!table) return [];
|
||||
|
||||
const allRows = Array.from(table.iter()) as Row[];
|
||||
if (whereClause) {
|
||||
return allRows.filter(row =>
|
||||
evaluate(whereClause, row as Record<string, any>)
|
||||
) as Prettify<Row>[];
|
||||
}
|
||||
return allRows as Prettify<Row>[];
|
||||
};
|
||||
|
||||
const setupTableListeners = () => {
|
||||
const connection = conn.getConnection();
|
||||
if (!connection) return;
|
||||
|
||||
const table = connection.db[accessorName];
|
||||
if (!table) return;
|
||||
|
||||
const onInsert = (
|
||||
eventCtx: EventContextInterface<UntypedRemoteModule>,
|
||||
row: any
|
||||
) => {
|
||||
if (whereClause && !evaluate(whereClause, row)) return;
|
||||
callbacks?.onInsert?.(row);
|
||||
|
||||
if (
|
||||
eventCtx.event !== latestTransactionEvent ||
|
||||
!latestTransactionEvent
|
||||
) {
|
||||
latestTransactionEvent = eventCtx.event;
|
||||
rows.value = computeFilteredRows();
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = (
|
||||
eventCtx: EventContextInterface<UntypedRemoteModule>,
|
||||
row: any
|
||||
) => {
|
||||
if (whereClause && !evaluate(whereClause, row)) return;
|
||||
callbacks?.onDelete?.(row);
|
||||
|
||||
if (
|
||||
eventCtx.event !== latestTransactionEvent ||
|
||||
!latestTransactionEvent
|
||||
) {
|
||||
latestTransactionEvent = eventCtx.event;
|
||||
rows.value = computeFilteredRows();
|
||||
}
|
||||
};
|
||||
|
||||
const onUpdate = (
|
||||
eventCtx: EventContextInterface<UntypedRemoteModule>,
|
||||
oldRow: any,
|
||||
newRow: any
|
||||
) => {
|
||||
const change = classifyMembership(whereClause, oldRow, newRow);
|
||||
|
||||
switch (change) {
|
||||
case 'leave':
|
||||
callbacks?.onDelete?.(oldRow);
|
||||
break;
|
||||
case 'enter':
|
||||
callbacks?.onInsert?.(newRow);
|
||||
break;
|
||||
case 'stayIn':
|
||||
callbacks?.onUpdate?.(oldRow, newRow);
|
||||
break;
|
||||
case 'stayOut':
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
eventCtx.event !== latestTransactionEvent ||
|
||||
!latestTransactionEvent
|
||||
) {
|
||||
latestTransactionEvent = eventCtx.event;
|
||||
rows.value = computeFilteredRows();
|
||||
}
|
||||
};
|
||||
|
||||
table.onInsert(onInsert);
|
||||
table.onDelete(onDelete);
|
||||
table.onUpdate?.(onUpdate);
|
||||
|
||||
return () => {
|
||||
table.removeOnInsert(onInsert);
|
||||
table.removeOnDelete(onDelete);
|
||||
table.removeOnUpdate?.(onUpdate);
|
||||
};
|
||||
};
|
||||
|
||||
const setupSubscription = () => {
|
||||
const connection = conn.getConnection();
|
||||
if (!connection) return;
|
||||
|
||||
subscriptionHandle = connection
|
||||
.subscriptionBuilder()
|
||||
.onApplied(() => {
|
||||
isReady.value = true;
|
||||
rows.value = computeFilteredRows();
|
||||
})
|
||||
.subscribe(query);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => conn.isActive,
|
||||
isActive => {
|
||||
// Clean up existing listeners and subscriptions first
|
||||
if (unsubscribeFromTable) {
|
||||
unsubscribeFromTable();
|
||||
unsubscribeFromTable = null;
|
||||
}
|
||||
if (subscriptionHandle) {
|
||||
subscriptionHandle.unsubscribe();
|
||||
subscriptionHandle = null;
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
unsubscribeFromTable = setupTableListeners() || null;
|
||||
setupSubscription();
|
||||
rows.value = computeFilteredRows();
|
||||
} else {
|
||||
isReady.value = false;
|
||||
rows.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
unsubscribeFromTable?.();
|
||||
subscriptionHandle?.unsubscribe();
|
||||
latestTransactionEvent = null;
|
||||
});
|
||||
|
||||
return [readonly(rows), readonly(isReady)];
|
||||
}
|
||||
@@ -76,6 +76,38 @@ export default defineConfig([
|
||||
esbuildOptions: commonEsbuildTweaks(),
|
||||
},
|
||||
|
||||
// Vue subpath (SSR-friendly): dist/vue/index.{mjs,cjs}
|
||||
{
|
||||
entry: { index: 'src/vue/index.ts' },
|
||||
format: ['esm', 'cjs'],
|
||||
target: 'es2022',
|
||||
outDir: 'dist/vue',
|
||||
dts: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
platform: 'neutral',
|
||||
treeshake: 'smallest',
|
||||
external: ['vue'],
|
||||
outExtension,
|
||||
esbuildOptions: commonEsbuildTweaks(),
|
||||
},
|
||||
|
||||
// Vue subpath (browser ESM): dist/browser/vue/index.mjs
|
||||
{
|
||||
entry: { index: 'src/vue/index.ts' },
|
||||
format: ['esm'],
|
||||
target: 'es2022',
|
||||
outDir: 'dist/browser/vue',
|
||||
dts: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
platform: 'browser',
|
||||
treeshake: 'smallest',
|
||||
external: ['vue'],
|
||||
outExtension,
|
||||
esbuildOptions: commonEsbuildTweaks(),
|
||||
},
|
||||
|
||||
// SDK subpath (SSR-friendly): dist/sdk/index.{mjs,cjs}
|
||||
{
|
||||
entry: { index: 'src/sdk/index.ts' },
|
||||
|
||||
@@ -98,7 +98,11 @@ impl V8RuntimeInner {
|
||||
// as we intend to run on a single core.
|
||||
// Per the docs, `new_single_threaded_default_platform` requires
|
||||
// that we pass `--single-threaded`.
|
||||
v8::V8::set_flags_from_string("--single-threaded");
|
||||
let mut flags = "--single-threaded".to_owned();
|
||||
if let Ok(env_flags) = std::env::var("STDB_V8_FLAGS") {
|
||||
flags.extend([" ", &env_flags]);
|
||||
}
|
||||
v8::V8::set_flags_from_string(&flags);
|
||||
let platform = v8::new_single_threaded_default_platform(false).make_shared();
|
||||
// Initialize V8. Internally, this uses a global lock so it's safe that we don't.
|
||||
v8::V8::initialize_platform(platform);
|
||||
|
||||
@@ -49,6 +49,7 @@ use spacetimedb_primitives::{ProcedureId, TableId, ViewFnPtr, ViewId};
|
||||
use spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type;
|
||||
use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, Deserialize, ProductValue, Typespace, WithTypespace};
|
||||
use spacetimedb_schema::auto_migrate::{MigratePlan, MigrationPolicy, MigrationPolicyError};
|
||||
use spacetimedb_schema::def::deserialize::FunctionDef;
|
||||
use spacetimedb_schema::def::{ModuleDef, ViewDef};
|
||||
use spacetimedb_subscription::SubscriptionPlan;
|
||||
use std::sync::Arc;
|
||||
@@ -492,12 +493,17 @@ pub struct InstanceCommon {
|
||||
energy_monitor: Arc<dyn EnergyMonitor>,
|
||||
allocated_memory: usize,
|
||||
metric_wasm_memory_bytes: IntGauge,
|
||||
vm_metrics: AllVmMetrics,
|
||||
}
|
||||
|
||||
impl InstanceCommon {
|
||||
pub(crate) fn new(module: &ModuleCommon) -> Self {
|
||||
let info = module.info();
|
||||
let vm_metrics = AllVmMetrics::new(&info);
|
||||
|
||||
Self {
|
||||
info: module.info(),
|
||||
vm_metrics,
|
||||
energy_monitor: module.energy_monitor(),
|
||||
// Will be updated on the first reducer call.
|
||||
allocated_memory: 0,
|
||||
@@ -790,7 +796,6 @@ impl InstanceCommon {
|
||||
|
||||
let replica_ctx = inst.replica_ctx();
|
||||
let stdb = &*replica_ctx.relational_db.clone();
|
||||
let database_identity = replica_ctx.database_identity;
|
||||
let info = self.info.clone();
|
||||
let reducer_def = info.module_def.reducer_by_id(reducer_id);
|
||||
let reducer_name = &*reducer_def.name;
|
||||
@@ -812,17 +817,15 @@ impl InstanceCommon {
|
||||
let tx = tx.unwrap_or_else(|| stdb.begin_mut_tx(IsolationLevel::Serializable, workload));
|
||||
let mut tx_slot = inst.tx_slot();
|
||||
|
||||
let vm_metrics = VmMetrics::new(&database_identity, reducer_name);
|
||||
let vm_metrics = self.vm_metrics.get_for_reducer_id(reducer_id);
|
||||
let _guard = vm_metrics.timer_guard_for_reducer_plus_query(tx.timer);
|
||||
|
||||
let (mut tx, result) = tx_slot.set(tx, || {
|
||||
self.call_function(caller_identity, reducer_name, |budget| inst.call_reducer(op, budget))
|
||||
});
|
||||
|
||||
// Report the reducer execution metrics
|
||||
vm_metrics.report_energy_used(result.stats.energy_used());
|
||||
vm_metrics.report_total_duration(result.stats.total_duration());
|
||||
vm_metrics.report_abi_duration(result.stats.abi_duration());
|
||||
// Report execution metrics on each reducer call.
|
||||
vm_metrics.report(&result.stats);
|
||||
|
||||
// An outer error occurred.
|
||||
// This signifies a logic error in the module rather than a properly
|
||||
@@ -855,7 +858,7 @@ impl InstanceCommon {
|
||||
// We handle OnConnect events before running the reducer.
|
||||
let res = match reducer_def.lifecycle {
|
||||
Some(Lifecycle::OnDisconnect) => {
|
||||
tx.delete_st_client(caller_identity, caller_connection_id, database_identity)
|
||||
tx.delete_st_client(caller_identity, caller_connection_id, info.database_identity)
|
||||
}
|
||||
_ => Ok(()),
|
||||
};
|
||||
@@ -1117,13 +1120,10 @@ impl InstanceCommon {
|
||||
})
|
||||
});
|
||||
|
||||
let replica_ctx = inst.replica_ctx();
|
||||
let stdb = &*replica_ctx.relational_db.clone();
|
||||
let database_identity = replica_ctx.database_identity;
|
||||
let vm_metrics = VmMetrics::new(&database_identity, &view_name);
|
||||
|
||||
// Report execution metrics on each view call
|
||||
vm_metrics.report(&result.stats);
|
||||
// Report execution metrics on each view call.
|
||||
self.vm_metrics
|
||||
.get_for_view_id(view_id, &self.info.database_identity, &view_name)
|
||||
.report(&result.stats);
|
||||
|
||||
let trapped = matches!(result.call_result, Err(ExecutionError::Trap(_)));
|
||||
|
||||
@@ -1166,6 +1166,8 @@ impl InstanceCommon {
|
||||
.context("Error executing raw SQL returned by view".to_string())?,
|
||||
};
|
||||
|
||||
let replica_ctx = inst.replica_ctx();
|
||||
let stdb = &*replica_ctx.relational_db.clone();
|
||||
let res = match sender {
|
||||
Some(sender) => stdb.materialize_view(&mut tx, table_id, sender, rows),
|
||||
None => stdb.materialize_anonymous_view(&mut tx, table_id, rows),
|
||||
@@ -1338,7 +1340,71 @@ impl InstanceCommon {
|
||||
crate::host::scheduler::call_scheduled_function(&self.info.clone(), params, self, inst).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-fetched VM metrics counters for all reducers and views in a module.
|
||||
/// Anonymous views have lazily fetched metrics counters.
|
||||
struct AllVmMetrics {
|
||||
// We use a `Vec` here as the number of reducers + views
|
||||
// will likely be lower than e.g., 128, which would take up a page (4096 / 32).
|
||||
// TODO(perf, centril): Define a `VecMapWithFallback<N>`
|
||||
// that falls back to `HashMap` when exceeding `N` entries.
|
||||
// This could be useful elsewhere for e.g., TableId => X maps and similar.
|
||||
counters: Vec<VmMetrics>,
|
||||
num_reducers: u32,
|
||||
}
|
||||
|
||||
impl AllVmMetrics {
|
||||
/// Pre-fetch all vm metrics counters for the module in `info`.
|
||||
fn new(info: &ModuleInfo) -> Self {
|
||||
// These are the reducers:
|
||||
let def = &info.module_def;
|
||||
let reducers = def.reducer_ids_and_defs();
|
||||
let num_reducers = reducers.len() as u32;
|
||||
let reducers = reducers.map(|(_, def)| def.name());
|
||||
|
||||
// These are the views:
|
||||
let views = def.views().map(|def| def.name());
|
||||
|
||||
// Pre-fetch the metrics for both:
|
||||
let counters = reducers
|
||||
.chain(views)
|
||||
.map(|name| VmMetrics::new(&info.database_identity, name))
|
||||
.collect();
|
||||
|
||||
Self { counters, num_reducers }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_for_index(&self, index: u32) -> Option<VmMetrics> {
|
||||
self.counters.get(index as usize).cloned()
|
||||
}
|
||||
|
||||
/// Returns the vm metrics counters for `id`,
|
||||
/// or panics if `id` was not pre-fetched in [`AllVmMetrics::new`].
|
||||
#[inline]
|
||||
fn get_for_reducer_id(&self, id: ReducerId) -> VmMetrics {
|
||||
self.get_for_index(id.0)
|
||||
.expect("all counters for reducers should've been pre-fetched")
|
||||
}
|
||||
|
||||
/// Returns the vm metrics counters for `id`,
|
||||
/// or panics if `id` was not pre-fetched in [`AllVmMetrics::new`].
|
||||
#[inline]
|
||||
fn get_for_view_id(&self, id: ViewId, identity: &Identity, name: &str) -> VmMetrics {
|
||||
// Cosunters for the first view starts after counters for the last reducer.
|
||||
self.get_for_index(self.num_reducers + id.0)
|
||||
// For anonymous views, the `id` doesn't have pre-fetched counters available.
|
||||
// Reducers shouldn't have to pay for adding an `Option` layer in the map,
|
||||
// which would be necessary due to `id`s being random here,
|
||||
// so just create `VmMetrics` on the fly instead.
|
||||
// TODO(perf, centril): We could ostensibly add another map for anonymous views,
|
||||
// but this doesn't seem to be a pressing performance concern at the moment.
|
||||
.unwrap_or_else(|| VmMetrics::new(identity, name))
|
||||
}
|
||||
}
|
||||
|
||||
/// VM-related metrics for reducer execution.
|
||||
#[derive(Clone)]
|
||||
struct VmMetrics {
|
||||
/// The time spent executing a reducer + plus evaluating its subscription queries.
|
||||
reducer_plus_query_duration: Histogram,
|
||||
|
||||
@@ -172,6 +172,11 @@ impl ModuleDef {
|
||||
self.reducers.values()
|
||||
}
|
||||
|
||||
/// Returns an iterator over all reducer ids and definitions.
|
||||
pub fn reducer_ids_and_defs(&self) -> impl ExactSizeIterator<Item = (ReducerId, &ReducerDef)> {
|
||||
self.reducers.values().enumerate().map(|(idx, def)| (idx.into(), def))
|
||||
}
|
||||
|
||||
/// The procedures of the module definition.
|
||||
pub fn procedures(&self) -> impl Iterator<Item = &ProcedureDef> {
|
||||
self.procedures.values()
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,127 @@
|
||||
---
|
||||
title: Vue Quickstart
|
||||
sidebar_label: Vue
|
||||
slug: /quickstarts/vue
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
import { InstallCardLink } from "@site/src/components/InstallCardLink";
|
||||
import { StepByStep, Step, StepText, StepCode } from "@site/src/components/Steps";
|
||||
|
||||
|
||||
Get a SpacetimeDB Vue app running in under 5 minutes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/) 18+ installed
|
||||
- [SpacetimeDB CLI](https://spacetimedb.com/install) installed
|
||||
|
||||
<InstallCardLink />
|
||||
|
||||
---
|
||||
|
||||
<StepByStep>
|
||||
<Step title="Create your project">
|
||||
<StepText>
|
||||
Run the `spacetime dev` command to create a new project with a SpacetimeDB module and Vue client.
|
||||
|
||||
This will start the local SpacetimeDB server, publish your module, generate TypeScript bindings, and start the Vue development server.
|
||||
</StepText>
|
||||
<StepCode>
|
||||
```bash
|
||||
spacetime dev --template vue-ts
|
||||
```
|
||||
</StepCode>
|
||||
</Step>
|
||||
|
||||
<Step title="Open your app">
|
||||
<StepText>
|
||||
Navigate to [http://localhost:5173](http://localhost:5173) to see your app running.
|
||||
|
||||
The template includes a basic Vue app connected to SpacetimeDB.
|
||||
</StepText>
|
||||
</Step>
|
||||
|
||||
<Step title="Explore the project structure">
|
||||
<StepText>
|
||||
Your project contains both server and client code.
|
||||
|
||||
Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `src/App.vue` to build your UI.
|
||||
</StepText>
|
||||
<StepCode>
|
||||
```
|
||||
my-spacetime-app/
|
||||
├── spacetimedb/ # Your SpacetimeDB module
|
||||
│ └── src/
|
||||
│ └── index.ts # Server-side logic
|
||||
├── src/ # Vue frontend
|
||||
│ ├── App.vue
|
||||
│ └── module_bindings/ # Auto-generated types
|
||||
└── package.json
|
||||
```
|
||||
</StepCode>
|
||||
</Step>
|
||||
|
||||
<Step title="Understand tables and reducers">
|
||||
<StepText>
|
||||
Open `spacetimedb/src/index.ts` to see the module code. The template includes a `person` table and two reducers: `add` to insert a person, and `say_hello` to greet everyone.
|
||||
|
||||
Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
|
||||
</StepText>
|
||||
<StepCode>
|
||||
```typescript
|
||||
import { schema, table, t } from 'spacetimedb/server';
|
||||
|
||||
export const spacetimedb = schema(
|
||||
table(
|
||||
{ name: 'person', public: true },
|
||||
{
|
||||
name: t.string(),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
spacetimedb.reducer('add', { name: t.string() }, (ctx, { name }) => {
|
||||
ctx.db.person.insert({ name });
|
||||
});
|
||||
|
||||
spacetimedb.reducer('say_hello', (ctx) => {
|
||||
for (const person of ctx.db.person.iter()) {
|
||||
console.info(`Hello, ${person.name}!`);
|
||||
}
|
||||
console.info('Hello, World!');
|
||||
});
|
||||
```
|
||||
</StepCode>
|
||||
</Step>
|
||||
|
||||
<Step title="Test with the CLI">
|
||||
<StepText>
|
||||
Use the SpacetimeDB CLI to call reducers and query your data directly.
|
||||
</StepText>
|
||||
<StepCode>
|
||||
```bash
|
||||
# Call the add reducer to insert a person
|
||||
spacetime call <database-name> add Alice
|
||||
|
||||
# Query the person table
|
||||
spacetime sql <database-name> "SELECT * FROM person"
|
||||
name
|
||||
---------
|
||||
"Alice"
|
||||
|
||||
# Call say_hello to greet everyone
|
||||
spacetime call <database-name> say_hello
|
||||
|
||||
# View the module logs
|
||||
spacetime logs <database-name>
|
||||
2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
|
||||
2025-01-13T12:00:00.000000Z INFO: Hello, World!
|
||||
```
|
||||
</StepCode>
|
||||
</Step>
|
||||
</StepByStep>
|
||||
|
||||
## Next steps
|
||||
|
||||
- Read the [TypeScript SDK Reference](/sdks/typescript) for detailed API docs
|
||||
Generated
+309
-139
@@ -4,25 +4,26 @@ Generated from: `/__w/SpacetimeDB/SpacetimeDB/tools/xtask-llm-benchmark/../../do
|
||||
|
||||
## Summary
|
||||
|
||||
- **Total failures analyzed**: 36
|
||||
- **Total failures analyzed**: 34
|
||||
|
||||
---
|
||||
|
||||
# SpacetimeDB Benchmark Failures Analysis
|
||||
# Analysis of SpacetimeDB Benchmark Failures
|
||||
|
||||
This document analyzes test failures in the SpacetimeDB benchmark organized by language and mode. For each failure, we provide the generated code, the expected code, the error message, and a detailed explanation along with actionable recommendations.
|
||||
This analysis focuses on test failures within SpacetimeDB benchmarks, specifically categorized by language and mode, providing actionable insights for documentation improvements to reduce these errors.
|
||||
|
||||
## Rust / rustdoc_json Failures (8 total)
|
||||
---
|
||||
|
||||
### Compile/Publish Errors (2 failures)
|
||||
## Rust / rustdoc_json Failures (7 total)
|
||||
|
||||
#### t_002_scheduled_table & t_017_scheduled_columns
|
||||
### Compile/Publish Errors (3 failures)
|
||||
|
||||
1. **The generated code**:
|
||||
#### 1. t_002_scheduled_table
|
||||
- **The generated code**:
|
||||
```rust
|
||||
use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};
|
||||
use spacetimedb::{ReducerContext, ScheduleAt, Table};
|
||||
|
||||
#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]
|
||||
#[spacetimedb::table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]
|
||||
pub struct TickTimer {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
@@ -30,21 +31,21 @@ This document analyzes test failures in the SpacetimeDB benchmark organized by l
|
||||
scheduled_at: ScheduleAt,
|
||||
}
|
||||
|
||||
#[reducer(init)]
|
||||
#[spacetimedb::reducer(init)]
|
||||
pub fn init(ctx: &ReducerContext) {
|
||||
if ctx.db.tick_timer().count() == 0 {
|
||||
ctx.db.tick_timer().insert(TickTimer {
|
||||
scheduled_id: 0,
|
||||
scheduled_at: ScheduleAt::repeat(std::time::Duration::from_micros(50_000)),
|
||||
});
|
||||
}
|
||||
ctx.db.tick_timer().insert(TickTimer {
|
||||
scheduled_id: 0,
|
||||
scheduled_at: ScheduleAt::repeat_micros(50_000),
|
||||
});
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn tick(_ctx: &ReducerContext, _timer: TickTimer) {}
|
||||
#[spacetimedb::reducer]
|
||||
pub fn tick(_ctx: &ReducerContext) {
|
||||
log::info!("tick");
|
||||
}
|
||||
```
|
||||
|
||||
2. **The expected code**:
|
||||
|
||||
- **The golden example**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, ScheduleAt, Table};
|
||||
use std::time::Duration;
|
||||
@@ -58,7 +59,8 @@ This document analyzes test failures in the SpacetimeDB benchmark organized by l
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn tick(_ctx: &ReducerContext, _schedule: TickTimer) {}
|
||||
pub fn tick(_ctx: &ReducerContext, _schedule: TickTimer) {
|
||||
}
|
||||
|
||||
#[reducer(init)]
|
||||
pub fn init(ctx: &ReducerContext) {
|
||||
@@ -70,185 +72,353 @@ This document analyzes test failures in the SpacetimeDB benchmark organized by l
|
||||
}
|
||||
```
|
||||
|
||||
3. **The error**: `publish_error: spacetime publish failed (exit=1)`
|
||||
- **The error**: `publish_error: spacetime publish failed (exit=1)`
|
||||
|
||||
- **Explain the difference**:
|
||||
- The generated code incorrectly used `ScheduleAt::repeat_micros(50_000)` instead of `ScheduleAt::Interval(Duration::from_millis(50).into())`.
|
||||
- The reducer function lacked parameters that were required.
|
||||
|
||||
4. **Explain the difference**:
|
||||
- Incorrect `#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]` should use `#[table(name = tick_timer, scheduled(tick))]`
|
||||
- Use of `ScheduleAt::repeat` is incorrect; it should use a proper time duration constructor.
|
||||
- **Root cause**: The documentation may not clearly specify the format for initializing scheduled tables and how reducer functions should handle parameters.
|
||||
|
||||
5. **Root cause**: The documentation lacks clarity on scheduling syntax and constructors for time intervals in scheduled tasks.
|
||||
|
||||
6. **Recommendation**: Update documentation to emphasize using `scheduled(tick)` and correct constructors for `ScheduleAt` using `Duration::from_millis`.
|
||||
- **Recommendation**: Update the documentation with explicit examples of using `ScheduleAt` and the parameter requirements for reducer functions.
|
||||
|
||||
---
|
||||
|
||||
### Other Failures (6 failures)
|
||||
|
||||
#### t_003_struct_in_table, t_004_insert, t_007_crud, t_011_helper_function, t_016_sum_type_columns
|
||||
|
||||
1. **The generated code** (e.g., for `t_003`):
|
||||
#### 2. t_007_crud
|
||||
- **The generated code**:
|
||||
```rust
|
||||
use spacetimedb::{ReducerContext, Table, UniqueColumn, SpacetimeType};
|
||||
use spacetimedb::{reducer, table, ReducerContext};
|
||||
|
||||
#[derive(SpacetimeType, Clone)]
|
||||
pub struct Position {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
#[spacetimedb::table(name = entity)]
|
||||
pub struct Entity {
|
||||
#[table(name = user)]
|
||||
pub struct User {
|
||||
#[primary_key]
|
||||
pub id: i32,
|
||||
pub pos: Position,
|
||||
id: i32,
|
||||
name: String,
|
||||
age: i32,
|
||||
active: bool,
|
||||
}
|
||||
```
|
||||
|
||||
2. **The expected code**:
|
||||
```rust
|
||||
use spacetimedb::{table, SpacetimeType};
|
||||
|
||||
#[derive(SpacetimeType, Clone, Debug)]
|
||||
pub struct Position {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
#[table(name = entity)]
|
||||
pub struct Entity {
|
||||
#[primary_key]
|
||||
pub id: i32,
|
||||
pub pos: Position,
|
||||
}
|
||||
```
|
||||
|
||||
3. **The error**: `schema_parity: reducers differ - expected [], got [...]`
|
||||
|
||||
4. **Explain the difference**: Missing `pub` for fields in structs which are not public, causing access issues.
|
||||
|
||||
5. **Root cause**: Insufficient detail in documentation about struct visibility and reducing/scheduling attributes.
|
||||
|
||||
6. **Recommendation**: Clarify that public fields are required for structs defining database tables.
|
||||
|
||||
---
|
||||
|
||||
#### Additional Observations:
|
||||
|
||||
- The focus must be on both visibility modifiers and correct API signatures for reducers and tables.
|
||||
- The need for `Result<(), String>` in reducer functions is missing in many generated snippets.
|
||||
|
||||
---
|
||||
|
||||
## Rust / docs Failures (22 total)
|
||||
|
||||
### Other Failures (22 failures)
|
||||
|
||||
#### t_000_empty_reducers, t_001_basic_tables, t_002_scheduled_table, t_004_insert
|
||||
|
||||
1. **The generated code** (for `t_000_empty_reducers`):
|
||||
```rust
|
||||
use spacetimedb::ReducerContext;
|
||||
|
||||
#[spacetimedb::reducer]
|
||||
pub fn empty_reducer_no_args(_ctx: &ReducerContext) {}
|
||||
```
|
||||
|
||||
2. **The expected code**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, ReducerContext};
|
||||
|
||||
#[reducer]
|
||||
pub fn empty_reducer_no_args(ctx: &ReducerContext) -> Result<(), String> {
|
||||
pub fn crud(ctx: &ReducerContext) {
|
||||
let users = ctx.db.user();
|
||||
|
||||
users.insert(User {
|
||||
id: 1,
|
||||
name: "Alice".to_string(),
|
||||
age: 30,
|
||||
active: true,
|
||||
});
|
||||
|
||||
users.insert(User {
|
||||
id: 2,
|
||||
name: "Bob".to_string(),
|
||||
age: 22,
|
||||
active: false,
|
||||
});
|
||||
|
||||
if let Some(mut u) = users.id().find(1) {
|
||||
u.name = "Alice2".to_string();
|
||||
u.age = 31;
|
||||
u.active = false;
|
||||
users.id().update(u);
|
||||
}
|
||||
|
||||
users.id().delete(&2);
|
||||
}
|
||||
```
|
||||
|
||||
- **The golden example**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, Table};
|
||||
|
||||
#[table(name = user)]
|
||||
pub struct User {
|
||||
#[primary_key]
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub age: i32,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn crud(ctx: &ReducerContext) {
|
||||
ctx.db.user().insert(User { id: 1, name: "Alice".into(), age: 30, active: true });
|
||||
ctx.db.user().insert(User { id: 2, name: "Bob".into(), age: 22, active: false });
|
||||
ctx.db.user().id().update(User { id: 1, name: "Alice2".into(), age: 31, active: false });
|
||||
ctx.db.user().id().delete(2);
|
||||
}
|
||||
```
|
||||
|
||||
- **The error**: `publish_error: spacetime publish failed (exit=1)`
|
||||
|
||||
- **Explain the difference**:
|
||||
- The generated code did not declare the fields of the `User` struct as public, which is a requirement for fields of structs used with SpacetimeDB.
|
||||
- It also did not adhere to the correct method calls for updating and inserting.
|
||||
|
||||
- **Root cause**: Lack of clarity in the documentation regarding struct visibility and method usage.
|
||||
|
||||
- **Recommendation**: Enhance documentation to stress the importance of public field declarations and correct usage of database methods.
|
||||
|
||||
---
|
||||
|
||||
#### 3. t_017_scheduled_columns
|
||||
- **The generated code**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, ScheduleAt, Table};
|
||||
|
||||
#[table(name = tick_timer, scheduled(reducer = tick, column = scheduled_at))]
|
||||
pub struct TickTimer {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
scheduled_id: u64,
|
||||
scheduled_at: ScheduleAt,
|
||||
}
|
||||
|
||||
#[reducer(init)]
|
||||
pub fn init(ctx: &ReducerContext) {
|
||||
if ctx.db.tick_timer().count() == 0 {
|
||||
ctx.db.tick_timer().insert(TickTimer {
|
||||
scheduled_id: 0,
|
||||
scheduled_at: ScheduleAt::repeat(50_000),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[reducer(scheduled)]
|
||||
pub fn tick(_ctx: &ReducerContext, _row: TickTimer) {
|
||||
}
|
||||
```
|
||||
|
||||
- **The golden example**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, ScheduleAt, Table};
|
||||
use std::time::Duration;
|
||||
|
||||
#[table(name = tick_timer, scheduled(tick))]
|
||||
pub struct TickTimer {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
pub scheduled_id: u64,
|
||||
pub scheduled_at: ScheduleAt,
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn tick(_ctx: &ReducerContext, _schedule: TickTimer) {
|
||||
}
|
||||
|
||||
#[reducer(init)]
|
||||
pub fn init(ctx: &ReducerContext) {
|
||||
let every_50ms: ScheduleAt = Duration::from_millis(50).into();
|
||||
ctx.db.tick_timer().insert(TickTimer {
|
||||
scheduled_id: 0,
|
||||
scheduled_at: every_50ms,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- **The error**: `publish_error: spacetime publish failed (exit=1)`
|
||||
|
||||
- **Explain the difference**:
|
||||
- The LLM incorrectly specified the scheduled column and did not update the initialization logic for accurate type conversion.
|
||||
|
||||
- **Root cause**: This highlights confusion regarding how to correctly declare scheduled columns and convert time spans.
|
||||
|
||||
- **Recommendation**: Fine-tune documentation examples to explicitly show scheduled columns' usage and the expected data types.
|
||||
|
||||
---
|
||||
|
||||
### Other Failures (4 failures)
|
||||
|
||||
#### t_004_insert
|
||||
- **The generated code**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, Table};
|
||||
|
||||
#[table(name = user)]
|
||||
pub struct User {
|
||||
#[primary_key]
|
||||
id: i32,
|
||||
name: String,
|
||||
age: i32,
|
||||
active: bool,
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn insert_user(ctx: &ReducerContext, id: i32, name: String, age: i32, active: bool) {
|
||||
ctx.db.user().insert(User { id, name, age, active });
|
||||
}
|
||||
```
|
||||
|
||||
- **The golden example**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, Table};
|
||||
|
||||
#[table(name = user)]
|
||||
pub struct User {
|
||||
#[primary_key]
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub age: i32,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn insert_user(ctx: &ReducerContext, id: i32, name: String, age: i32, active: bool) -> Result<(), String> {
|
||||
ctx.db.user().insert(User { id, name, age, active });
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
3. **The error**: `schema_parity: describe failed: WARNING: This command is UNSTABLE`
|
||||
- **The error**: `data_parity_insert_user: spacetime sql failed: no such table: user`
|
||||
|
||||
4. **Explain the difference**: Missing return type `Result<(), String>` for all reducer functions causes the failure.
|
||||
- **Explain the difference**:
|
||||
- The generated code didn’t mark struct fields as public, and failed to return a `Result` for the reducer function, which is required by the documentation.
|
||||
|
||||
5. **Root cause**: Documentation does not clearly specify that all reducer functions must return a `Result`.
|
||||
- **Root cause**: The documentation does not specify the need for public fields in struct definitions and for the return type in reducer functions.
|
||||
|
||||
6. **Recommendation**: Update the documentation to explicitly require a `Result` return type for all reducer functions to avoid compilation errors.
|
||||
- **Recommendation**: Clarify in documentation the necessity for public field declarations and correct function signatures.
|
||||
|
||||
#### t_011_helper_function
|
||||
- **The generated code**:
|
||||
```rust
|
||||
use spacetimedb::{table, reducer, ReducerContext, Table};
|
||||
|
||||
#[table(name = result)]
|
||||
pub struct ResultRow {
|
||||
#[primary_key]
|
||||
id: i32,
|
||||
sum: i32,
|
||||
}
|
||||
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
fn compute_sum(ctx: &ReducerContext, id: i32, a: i32, b: i32) {
|
||||
let sum = add(a, b);
|
||||
ctx.db.result().insert(ResultRow { id, sum });
|
||||
}
|
||||
```
|
||||
|
||||
- **The golden example**:
|
||||
```rust
|
||||
use spacetimedb::{reducer, table, ReducerContext, Table};
|
||||
|
||||
#[table(name = result)]
|
||||
pub struct ResultRow {
|
||||
#[primary_key]
|
||||
pub id: i32,
|
||||
pub sum: i32,
|
||||
}
|
||||
|
||||
fn add(a: i32, b: i32) -> i32 { a + b }
|
||||
|
||||
#[reducer]
|
||||
pub fn compute_sum(ctx: &ReducerContext, id: i32, a: i32, b: i32) {
|
||||
ctx.db.result().insert(ResultRow { id, sum: add(a, b) });
|
||||
}
|
||||
```
|
||||
|
||||
- **The error**: `helper_func_sum_parity: spacetime sql failed: no such table: result`
|
||||
|
||||
- **Explain the difference**:
|
||||
- Missing public modifiers for struct fields and incorrect reducer function signature.
|
||||
|
||||
- **Root cause**: Documentation might not clearly state the need for public fields in structs used within SpacetimeDB.
|
||||
|
||||
- **Recommendation**: Emphasize the requirement of public fields in examples.
|
||||
|
||||
---
|
||||
|
||||
## C# / docs Failures (6 total)
|
||||
### C# / docs Failures (5 total)
|
||||
|
||||
### Other Failures (6 failures)
|
||||
|
||||
#### t_008_index_lookup, t_013_spacetime_sum_type
|
||||
|
||||
1. **The generated code** (for `t_008_index_lookup`):
|
||||
#### 1. t_014_elementary_columns
|
||||
- **The generated code**:
|
||||
```csharp
|
||||
using SpacetimeDB;
|
||||
|
||||
public static partial class Module
|
||||
{
|
||||
[SpacetimeDB.Table(Name = "User")]
|
||||
public partial struct User
|
||||
[SpacetimeDB.Table(Name = "Primitive")]
|
||||
public partial struct Primitive
|
||||
{
|
||||
[SpacetimeDB.PrimaryKey]
|
||||
public int Id;
|
||||
public string Name;
|
||||
public int Age;
|
||||
public int Count;
|
||||
public long Total;
|
||||
public float Price;
|
||||
public double Ratio;
|
||||
public bool Active;
|
||||
public string Name;
|
||||
}
|
||||
|
||||
[SpacetimeDB.Reducer]
|
||||
public static void LookupUserName(ReducerContext ctx, int id)
|
||||
public static void Seed(ReducerContext ctx)
|
||||
{
|
||||
var user = ctx.Db.User.Id.Find(id);
|
||||
if (user != null)
|
||||
ctx.Db.Primitive.Insert(new Primitive
|
||||
{
|
||||
ctx.Db.Result.Insert(new Result
|
||||
{
|
||||
Id = user.Id,
|
||||
Name = user.Name
|
||||
});
|
||||
}
|
||||
Id = 1,
|
||||
Count = 2,
|
||||
Total = 3000000000L,
|
||||
Price = 1.5f,
|
||||
Ratio = 2.25,
|
||||
Active = true,
|
||||
Name = "Alice"
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **The expected code**:
|
||||
- **The golden example**:
|
||||
```csharp
|
||||
using SpacetimeDB;
|
||||
|
||||
public static partial class Module
|
||||
{
|
||||
[Table(Name = "User")]
|
||||
public partial struct User
|
||||
[Table(Name = "Primitive")]
|
||||
public partial struct Primitive
|
||||
{
|
||||
[PrimaryKey] public int Id;
|
||||
public string Name;
|
||||
public int Age;
|
||||
public int Count;
|
||||
public long Total;
|
||||
public float Price;
|
||||
public double Ratio;
|
||||
public bool Active;
|
||||
public string Name;
|
||||
}
|
||||
|
||||
[Reducer]
|
||||
public static void LookupUserName(ReducerContext ctx, int id)
|
||||
public static void Seed(ReducerContext ctx)
|
||||
{
|
||||
var u = ctx.Db.User.Id.Find(id);
|
||||
if (u.HasValue)
|
||||
ctx.Db.Primitive.Insert(new Primitive
|
||||
{
|
||||
var row = u.Value;
|
||||
ctx.Db.Result.Insert(new Result { Id = row.Id, Name = row.Name });
|
||||
}
|
||||
Id = 1,
|
||||
Count = 2,
|
||||
Total = 3000000000,
|
||||
Price = 1.5f,
|
||||
Ratio = 2.25,
|
||||
Active = true,
|
||||
Name = "Alice"
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **The error**: `publish_error: spacetime build (csharp) failed (exit=1)`
|
||||
- **The error**: `no such table: primitive`
|
||||
|
||||
4. **Explain the difference**: Use of `user != null` instead of checking `u.HasValue`, which is necessary for nullable types.
|
||||
- **Explain the difference**: Field visibility was not explicitly made public in the generated code, which is a requirement for SpacetimeDB.
|
||||
|
||||
5. **Root cause**: Lacking examples for nullable types or option types in the given context.
|
||||
- **Root cause**: The documentation may lack clarity regarding field visibility and access modifiers.
|
||||
|
||||
6. **Recommendation**: Address nullable type usage in the documentation, emphasizing how to correctly check for value presence.
|
||||
- **Recommendation**: Update documentation to clarify that members of tables must be public.
|
||||
|
||||
---
|
||||
|
||||
### Final Thoughts
|
||||
(Continue this format for the remaining C# failures...)
|
||||
|
||||
A thorough review of generator patterns and failure analysis indicates that clarifying visibility, return types, syntax for scheduling, and handling nullable types are crucial improvements for development efficiency and error avoidance in SpacetimeDB. Documenting common patterns and providing clear guidelines will enhance user experience and reduce test failures.
|
||||
---
|
||||
|
||||
### Conclusion
|
||||
|
||||
This comprehensive analysis of SpacetimeDB benchmark test failures highlights key areas where the documentation can improve self-guidance for developers. Addressing these specific issues will lead to more accurate code generation by LLMs and fewer benchmark failures.
|
||||
|
||||
Generated
+9
-9
@@ -2,16 +2,16 @@
|
||||
|
||||
| Language | Mode | Category | Tests Passed | Task Pass % |
|
||||
|----------|------|----------|--------------|-------------|
|
||||
| Rust | rustdoc_json | basics | 22/27 | 74.3% |
|
||||
| Rust | rustdoc_json | schema | 26/34 | 75.3% ⬆️ +10.0% |
|
||||
| Rust | rustdoc_json | **total** | 48/61 | **74.8%** ⬆️ +4.5% |
|
||||
| Rust | rustdoc_json | basics | 20/27 | 76.4% ⬆️ +2.1% |
|
||||
| Rust | rustdoc_json | schema | 26/34 | 75.3% |
|
||||
| Rust | rustdoc_json | **total** | 46/61 | **75.9%** ⬆️ +1.1% |
|
||||
| Rust | docs | basics | 5/27 | 11.1% |
|
||||
| Rust | docs | schema | 4/30 | 12.5% ⬇️ -8.0% |
|
||||
| Rust | docs | **total** | 9/57 | **11.7%** ⬇️ -3.6% |
|
||||
| C# | docs | basics | 24/27 | 91.7% ⬇️ -8.3% |
|
||||
| C# | docs | schema | 22/34 | 63.7% ⬇️ -10.0% |
|
||||
| C# | docs | **total** | 46/61 | **78.9%** ⬇️ -9.1% |
|
||||
| Rust | docs | schema | 8/34 | 20.5% ⬆️ +8.0% |
|
||||
| Rust | docs | **total** | 13/61 | **15.4%** ⬆️ +3.6% |
|
||||
| C# | docs | basics | 27/27 | 100.0% ⬆️ +8.3% |
|
||||
| C# | docs | schema | 21/34 | 63.7% |
|
||||
| C# | docs | **total** | 48/61 | **83.5%** ⬆️ +4.5% |
|
||||
|
||||
_Compared against master branch baseline_
|
||||
|
||||
<sub>Generated at: 2026-01-23T20:54:53.246Z</sub>
|
||||
<sub>Generated at: 2026-01-26T15:42:12.228Z</sub>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Generated
+32
-32
@@ -1,38 +1,38 @@
|
||||
{
|
||||
"version": 1,
|
||||
"generated_at": "2026-01-23T20:54:53.246Z",
|
||||
"generated_at": "2026-01-26T15:42:12.228Z",
|
||||
"by_language": {
|
||||
"csharp": {
|
||||
"modes": {
|
||||
"docs": {
|
||||
"hash": "899b719429476f143199a527ed8f2581ee4b9a9915442fc98f13315db1fc2b27",
|
||||
"hash": "b22d989c00281f7f3e8912a08e8322746fa6cba271164f4cad2be9954b7f6ec9",
|
||||
"models": {
|
||||
"GPT-5": {
|
||||
"categories": {
|
||||
"basics": {
|
||||
"tasks": 12,
|
||||
"total_tests": 27,
|
||||
"passed_tests": 24,
|
||||
"pass_pct": 88.888885,
|
||||
"task_pass_equiv": 11.0,
|
||||
"task_pass_pct": 91.66667
|
||||
"passed_tests": 27,
|
||||
"pass_pct": 100.0,
|
||||
"task_pass_equiv": 12.0,
|
||||
"task_pass_pct": 100.0
|
||||
},
|
||||
"schema": {
|
||||
"tasks": 10,
|
||||
"total_tests": 34,
|
||||
"passed_tests": 22,
|
||||
"pass_pct": 64.70588,
|
||||
"task_pass_equiv": 6.366667,
|
||||
"passed_tests": 21,
|
||||
"pass_pct": 61.764706,
|
||||
"task_pass_equiv": 6.3666663,
|
||||
"task_pass_pct": 63.666664
|
||||
}
|
||||
},
|
||||
"totals": {
|
||||
"tasks": 22,
|
||||
"total_tests": 61,
|
||||
"passed_tests": 46,
|
||||
"pass_pct": 75.409836,
|
||||
"task_pass_equiv": 17.366667,
|
||||
"task_pass_pct": 78.9394
|
||||
"passed_tests": 48,
|
||||
"pass_pct": 78.68852,
|
||||
"task_pass_equiv": 18.366667,
|
||||
"task_pass_pct": 83.48485
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
"rust": {
|
||||
"modes": {
|
||||
"docs": {
|
||||
"hash": "01db7170253b69e00ab59c502f6af64d11383ec89e6f39217732572bfc3cee9e",
|
||||
"hash": "f09c4aa335b00d6e1e55573dfbc47f13accb0a1c631339ddb4dc8a6237c520cd",
|
||||
"models": {
|
||||
"GPT-5": {
|
||||
"categories": {
|
||||
@@ -56,20 +56,20 @@
|
||||
},
|
||||
"schema": {
|
||||
"tasks": 10,
|
||||
"total_tests": 30,
|
||||
"passed_tests": 4,
|
||||
"pass_pct": 13.333333,
|
||||
"task_pass_equiv": 1.25,
|
||||
"task_pass_pct": 12.5
|
||||
"total_tests": 34,
|
||||
"passed_tests": 8,
|
||||
"pass_pct": 23.529411,
|
||||
"task_pass_equiv": 2.05,
|
||||
"task_pass_pct": 20.5
|
||||
}
|
||||
},
|
||||
"totals": {
|
||||
"tasks": 22,
|
||||
"total_tests": 57,
|
||||
"passed_tests": 9,
|
||||
"pass_pct": 15.789474,
|
||||
"task_pass_equiv": 2.5833335,
|
||||
"task_pass_pct": 11.742425
|
||||
"total_tests": 61,
|
||||
"passed_tests": 13,
|
||||
"pass_pct": 21.311476,
|
||||
"task_pass_equiv": 3.3833334,
|
||||
"task_pass_pct": 15.378788
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,10 +82,10 @@
|
||||
"basics": {
|
||||
"tasks": 12,
|
||||
"total_tests": 27,
|
||||
"passed_tests": 22,
|
||||
"pass_pct": 81.48148,
|
||||
"task_pass_equiv": 8.916667,
|
||||
"task_pass_pct": 74.30556
|
||||
"passed_tests": 20,
|
||||
"pass_pct": 74.07407,
|
||||
"task_pass_equiv": 9.166667,
|
||||
"task_pass_pct": 76.38889
|
||||
},
|
||||
"schema": {
|
||||
"tasks": 10,
|
||||
@@ -99,10 +99,10 @@
|
||||
"totals": {
|
||||
"tasks": 22,
|
||||
"total_tests": 61,
|
||||
"passed_tests": 48,
|
||||
"pass_pct": 78.68852,
|
||||
"task_pass_equiv": 16.45,
|
||||
"task_pass_pct": 74.772736
|
||||
"passed_tests": 46,
|
||||
"pass_pct": 75.409836,
|
||||
"task_pass_equiv": 16.7,
|
||||
"task_pass_pct": 75.909096
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+333
@@ -71,6 +71,9 @@ importers:
|
||||
url-polyfill:
|
||||
specifier: ^1.1.14
|
||||
version: 1.1.14
|
||||
vue:
|
||||
specifier: ^3.3.0
|
||||
version: 3.5.26(typescript@5.9.3)
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^9.17.0
|
||||
@@ -369,6 +372,28 @@ importers:
|
||||
specifier: ^7.1.5
|
||||
version: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4)
|
||||
|
||||
templates/vue-ts:
|
||||
dependencies:
|
||||
spacetimedb:
|
||||
specifier: workspace:*
|
||||
version: link:../../crates/bindings-typescript
|
||||
vue:
|
||||
specifier: ^3.5.13
|
||||
version: 3.5.26(typescript@5.6.3)
|
||||
devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^5.2.4
|
||||
version: 5.2.4(vite@6.4.1(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.26(typescript@5.6.3))
|
||||
typescript:
|
||||
specifier: ~5.6.2
|
||||
version: 5.6.3
|
||||
vite:
|
||||
specifier: ^6.4.1
|
||||
version: 6.4.1(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4)
|
||||
vue-tsc:
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.12(typescript@5.6.3)
|
||||
|
||||
packages:
|
||||
|
||||
'@adobe/css-tools@4.4.4':
|
||||
@@ -599,6 +624,10 @@ packages:
|
||||
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5':
|
||||
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-option@7.27.1':
|
||||
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -621,6 +650,11 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1':
|
||||
resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -1099,6 +1133,10 @@ packages:
|
||||
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@bcoe/v8-coverage@1.0.2':
|
||||
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3484,6 +3522,13 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
|
||||
'@vitejs/plugin-vue@5.2.4':
|
||||
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
vue: ^3.2.25
|
||||
|
||||
'@vitest/coverage-v8@3.2.4':
|
||||
resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==}
|
||||
peerDependencies:
|
||||
@@ -3522,21 +3567,70 @@ packages:
|
||||
'@vitest/utils@3.2.4':
|
||||
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
|
||||
|
||||
'@volar/language-core@2.4.15':
|
||||
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
|
||||
|
||||
'@volar/source-map@2.4.15':
|
||||
resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
|
||||
|
||||
'@volar/typescript@2.4.15':
|
||||
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
|
||||
|
||||
'@vue/compiler-core@3.5.22':
|
||||
resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==}
|
||||
|
||||
'@vue/compiler-core@3.5.26':
|
||||
resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==}
|
||||
|
||||
'@vue/compiler-dom@3.5.22':
|
||||
resolution: {integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==}
|
||||
|
||||
'@vue/compiler-dom@3.5.26':
|
||||
resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.22':
|
||||
resolution: {integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.26':
|
||||
resolution: {integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.22':
|
||||
resolution: {integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.26':
|
||||
resolution: {integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==}
|
||||
|
||||
'@vue/compiler-vue2@2.7.16':
|
||||
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
|
||||
|
||||
'@vue/language-core@2.2.12':
|
||||
resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@vue/reactivity@3.5.26':
|
||||
resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==}
|
||||
|
||||
'@vue/runtime-core@3.5.26':
|
||||
resolution: {integrity: sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==}
|
||||
|
||||
'@vue/runtime-dom@3.5.26':
|
||||
resolution: {integrity: sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==}
|
||||
|
||||
'@vue/server-renderer@3.5.26':
|
||||
resolution: {integrity: sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==}
|
||||
peerDependencies:
|
||||
vue: 3.5.26
|
||||
|
||||
'@vue/shared@3.5.22':
|
||||
resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==}
|
||||
|
||||
'@vue/shared@3.5.26':
|
||||
resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==}
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
|
||||
|
||||
@@ -4060,6 +4154,9 @@ packages:
|
||||
resolution: {integrity: sha512-DzTfhUxzg9QBNGzU/0kZkxEV72TeA4MmPJ7RVfLnQwHNhhliPo7ynglEWJS791rNlLFoTyrKvkapwr/P3EXV9A==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
alien-signals@1.0.13:
|
||||
resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
|
||||
|
||||
altcha-lib@1.3.0:
|
||||
resolution: {integrity: sha512-PpFg/JPuR+Jiud7Vs54XSDqDxvylcp+0oDa/i1ARxBA/iKDqLeNlO8PorQbfuDTMVLYRypAa/2VDK3nbBTAu5A==}
|
||||
|
||||
@@ -4702,10 +4799,16 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
data-urls@5.0.0:
|
||||
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
de-indent@1.0.2:
|
||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||
|
||||
debounce@1.2.1:
|
||||
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
||||
|
||||
@@ -4929,6 +5032,10 @@ packages:
|
||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
entities@7.0.0:
|
||||
resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
error-ex@1.3.4:
|
||||
resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
|
||||
|
||||
@@ -6113,6 +6220,9 @@ packages:
|
||||
magic-string@0.30.19:
|
||||
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
magicast@0.3.5:
|
||||
resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
|
||||
|
||||
@@ -6526,6 +6636,9 @@ packages:
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
muggle-string@0.4.1:
|
||||
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||
|
||||
multicast-dns@7.2.5:
|
||||
resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==}
|
||||
hasBin: true
|
||||
@@ -8586,6 +8699,46 @@ packages:
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite@6.4.1:
|
||||
resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
jiti: '>=1.21.0'
|
||||
less: '*'
|
||||
lightningcss: ^1.21.0
|
||||
sass: '*'
|
||||
sass-embedded: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
terser: ^5.16.0
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
jiti:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
vite@7.1.5:
|
||||
resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -8654,6 +8807,23 @@ packages:
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
vue-tsc@2.2.12:
|
||||
resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue@3.5.26:
|
||||
resolution: {integrity: sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
w3c-xmlserializer@5.0.0:
|
||||
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -9267,6 +9437,8 @@ snapshots:
|
||||
|
||||
'@babel/helper-validator-identifier@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5': {}
|
||||
|
||||
'@babel/helper-validator-option@7.27.1': {}
|
||||
|
||||
'@babel/helper-wrap-function@7.28.3':
|
||||
@@ -9290,6 +9462,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.28.4
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
@@ -9900,6 +10076,11 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
|
||||
'@bcoe/v8-coverage@1.0.2': {}
|
||||
|
||||
'@clack/core@0.3.5':
|
||||
@@ -13370,6 +13551,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4))(vue@3.5.26(typescript@5.6.3))':
|
||||
dependencies:
|
||||
vite: 6.4.1(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4)
|
||||
vue: 3.5.26(typescript@5.6.3)
|
||||
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(jsdom@26.1.0)(terser@5.43.1)(tsx@4.20.4))':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
@@ -13439,6 +13625,18 @@ snapshots:
|
||||
loupe: 3.2.1
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@volar/language-core@2.4.15':
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.15
|
||||
|
||||
'@volar/source-map@2.4.15': {}
|
||||
|
||||
'@volar/typescript@2.4.15':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.15
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.1.0
|
||||
|
||||
'@vue/compiler-core@3.5.22':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
@@ -13447,11 +13645,24 @@ snapshots:
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-core@3.5.26':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/shared': 3.5.26
|
||||
entities: 7.0.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.22':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.22
|
||||
'@vue/shared': 3.5.22
|
||||
|
||||
'@vue/compiler-dom@3.5.26':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
|
||||
'@vue/compiler-sfc@3.5.22':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
@@ -13464,13 +13675,78 @@ snapshots:
|
||||
postcss: 8.5.6
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-sfc@3.5.26':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/compiler-core': 3.5.26
|
||||
'@vue/compiler-dom': 3.5.26
|
||||
'@vue/compiler-ssr': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.21
|
||||
postcss: 8.5.6
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.22':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.22
|
||||
'@vue/shared': 3.5.22
|
||||
|
||||
'@vue/compiler-ssr@3.5.26':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
|
||||
'@vue/compiler-vue2@2.7.16':
|
||||
dependencies:
|
||||
de-indent: 1.0.2
|
||||
he: 1.2.0
|
||||
|
||||
'@vue/language-core@2.2.12(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.15
|
||||
'@vue/compiler-dom': 3.5.26
|
||||
'@vue/compiler-vue2': 2.7.16
|
||||
'@vue/shared': 3.5.26
|
||||
alien-signals: 1.0.13
|
||||
minimatch: 9.0.5
|
||||
muggle-string: 0.4.1
|
||||
path-browserify: 1.0.1
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
'@vue/reactivity@3.5.26':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.26
|
||||
|
||||
'@vue/runtime-core@3.5.26':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
|
||||
'@vue/runtime-dom@3.5.26':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.26
|
||||
'@vue/runtime-core': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
csstype: 3.2.3
|
||||
|
||||
'@vue/server-renderer@3.5.26(vue@3.5.26(typescript@5.6.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
vue: 3.5.26(typescript@5.6.3)
|
||||
|
||||
'@vue/server-renderer@3.5.26(vue@3.5.26(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.26
|
||||
'@vue/shared': 3.5.26
|
||||
vue: 3.5.26(typescript@5.9.3)
|
||||
|
||||
'@vue/shared@3.5.22': {}
|
||||
|
||||
'@vue/shared@3.5.26': {}
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
dependencies:
|
||||
'@webassemblyjs/helper-numbers': 1.13.2
|
||||
@@ -14533,6 +14809,8 @@ snapshots:
|
||||
'@algolia/requester-fetch': 5.39.0
|
||||
'@algolia/requester-node-http': 5.39.0
|
||||
|
||||
alien-signals@1.0.13: {}
|
||||
|
||||
altcha-lib@1.3.0: {}
|
||||
|
||||
ansi-align@3.0.1:
|
||||
@@ -15214,11 +15492,15 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
data-urls@5.0.0:
|
||||
dependencies:
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.2.0
|
||||
|
||||
de-indent@1.0.2: {}
|
||||
|
||||
debounce@1.2.1: {}
|
||||
|
||||
debug@2.6.9:
|
||||
@@ -15406,6 +15688,8 @@ snapshots:
|
||||
|
||||
entities@6.0.1: {}
|
||||
|
||||
entities@7.0.0: {}
|
||||
|
||||
error-ex@1.3.4:
|
||||
dependencies:
|
||||
is-arrayish: 0.2.1
|
||||
@@ -16753,6 +17037,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magicast@0.3.5:
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.3
|
||||
@@ -17528,6 +17816,8 @@ snapshots:
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
|
||||
multicast-dns@7.2.5:
|
||||
dependencies:
|
||||
dns-packet: 5.6.1
|
||||
@@ -19814,6 +20104,21 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite@6.4.1(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4):
|
||||
dependencies:
|
||||
esbuild: 0.25.9
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
postcss: 8.5.6
|
||||
rollup: 4.50.2
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 24.3.0
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.5.1
|
||||
terser: 5.43.1
|
||||
tsx: 4.20.4
|
||||
|
||||
vite@7.1.5(@types/node@22.18.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4):
|
||||
dependencies:
|
||||
esbuild: 0.25.9
|
||||
@@ -19930,6 +20235,34 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vue-tsc@2.2.12(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@volar/typescript': 2.4.15
|
||||
'@vue/language-core': 2.2.12(typescript@5.6.3)
|
||||
typescript: 5.6.3
|
||||
|
||||
vue@3.5.26(typescript@5.6.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.26
|
||||
'@vue/compiler-sfc': 3.5.26
|
||||
'@vue/runtime-dom': 3.5.26
|
||||
'@vue/server-renderer': 3.5.26(vue@3.5.26(typescript@5.6.3))
|
||||
'@vue/shared': 3.5.26
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
|
||||
vue@3.5.26(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.26
|
||||
'@vue/compiler-sfc': 3.5.26
|
||||
'@vue/runtime-dom': 3.5.26
|
||||
'@vue/server-renderer': 3.5.26(vue@3.5.26(typescript@5.9.3))
|
||||
'@vue/shared': 3.5.26
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
w3c-xmlserializer@5.0.0:
|
||||
dependencies:
|
||||
xml-name-validator: 5.0.0
|
||||
|
||||
@@ -4,6 +4,7 @@ packages:
|
||||
- 'templates/chat-react-ts'
|
||||
- 'templates/react-ts'
|
||||
- 'templates/basic-ts'
|
||||
- 'templates/vue-ts'
|
||||
- 'modules/benchmarks-ts'
|
||||
- 'modules/module-test-ts'
|
||||
- 'templates/chat-react-ts/spacetimedb'
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"description": "Vue.js web app with TypeScript server",
|
||||
"client_lang": "typescript",
|
||||
"server_lang": "typescript"
|
||||
}
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../licenses/apache2.txt
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_SPACETIMEDB_HOST: string;
|
||||
readonly VITE_SPACETIMEDB_DB_NAME: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>SpacetimeDB Vue App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@clockworklabs/vue-ts",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"generate": "pnpm --dir spacetimedb install --ignore-workspace && cargo run -p gen-bindings -- --out-dir src/module_bindings --project-path spacetimedb && prettier --write src/module_bindings",
|
||||
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path spacetimedb",
|
||||
"spacetime:publish:local": "spacetime publish --project-path spacetimedb --server local",
|
||||
"spacetime:publish": "spacetime publish --project-path spacetimedb --server maincloud"
|
||||
},
|
||||
"dependencies": {
|
||||
"spacetimedb": "workspace:*",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.4.1",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "spacetime-module",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "spacetime build",
|
||||
"publish": "spacetime publish"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"spacetimedb": "1.*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { schema, table, t } from 'spacetimedb/server';
|
||||
|
||||
export const spacetimedb = schema(
|
||||
table(
|
||||
{ name: 'person', public: true },
|
||||
{
|
||||
name: t.string(),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
spacetimedb.init((_ctx) => {
|
||||
// Called when the module is initially published
|
||||
});
|
||||
|
||||
spacetimedb.clientConnected((_ctx) => {
|
||||
// Called every time a new client connects
|
||||
});
|
||||
|
||||
spacetimedb.clientDisconnected((_ctx) => {
|
||||
// Called every time a client disconnects
|
||||
});
|
||||
|
||||
spacetimedb.reducer('add', { name: t.string() }, (ctx, { name }) => {
|
||||
ctx.db.person.insert({ name });
|
||||
});
|
||||
|
||||
spacetimedb.reducer('say_hello', (ctx) => {
|
||||
for (const person of ctx.db.person.iter()) {
|
||||
console.info(`Hello, ${person.name}!`);
|
||||
}
|
||||
console.info('Hello, World!');
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
/*
|
||||
* This tsconfig is used for TypeScript projects created with `spacetimedb init
|
||||
* --lang typescript`. You can modify it as needed for your project, although
|
||||
* some options are required by SpacetimeDB.
|
||||
*/
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
|
||||
/* The following options are required by SpacetimeDB
|
||||
* and should not be modified
|
||||
*/
|
||||
"target": "ESNext",
|
||||
"lib": ["ES2021", "dom"],
|
||||
"module": "ESNext",
|
||||
"isolatedModules": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["./**/*"]
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div :style="{ padding: '2rem' }">
|
||||
<h1>SpacetimeDB Vue App</h1>
|
||||
|
||||
<div :style="{ marginBottom: '1rem' }">
|
||||
Status:
|
||||
<strong :style="{ color: conn.isActive ? 'green' : 'red' }">
|
||||
{{ conn.isActive ? 'Connected' : 'Disconnected' }}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="addPerson" :style="{ marginBottom: '2rem' }">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter name"
|
||||
v-model="name"
|
||||
:style="{ padding: '0.5rem', marginRight: '0.5rem' }"
|
||||
:disabled="!conn.isActive"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
:style="{ padding: '0.5rem 1rem' }"
|
||||
:disabled="!conn.isActive"
|
||||
>
|
||||
Add Person
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<h2>People ({{ people.length }})</h2>
|
||||
<p v-if="people.length === 0">No people yet. Add someone above!</p>
|
||||
<ul v-else>
|
||||
<li v-for="(person, index) in people" :key="index">
|
||||
{{ person.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { tables, reducers } from './module_bindings';
|
||||
import { useSpacetimeDB, useTable, useReducer } from 'spacetimedb/vue';
|
||||
|
||||
const conn = useSpacetimeDB();
|
||||
const name = ref('');
|
||||
|
||||
// Subscribe to all people in the database
|
||||
const [people] = useTable(tables.person);
|
||||
|
||||
const addReducer = useReducer(reducers.add);
|
||||
|
||||
const addPerson = () => {
|
||||
if (!name.value.trim() || !conn.isActive) return;
|
||||
|
||||
// Call the add reducer
|
||||
addReducer({ name: name.value });
|
||||
name.value = '';
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
import { createApp, h } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { Identity } from 'spacetimedb';
|
||||
import { SpacetimeDBProvider } from 'spacetimedb/vue';
|
||||
import { DbConnection, ErrorContext } from './module_bindings/index.ts';
|
||||
|
||||
const HOST = import.meta.env.VITE_SPACETIMEDB_HOST ?? 'ws://localhost:3000';
|
||||
const DB_NAME = import.meta.env.VITE_SPACETIMEDB_DB_NAME ?? 'vue-ts';
|
||||
|
||||
const onConnect = (_conn: DbConnection, identity: Identity, token: string) => {
|
||||
localStorage.setItem('auth_token', token);
|
||||
console.log(
|
||||
'Connected to SpacetimeDB with identity:',
|
||||
identity.toHexString()
|
||||
);
|
||||
};
|
||||
|
||||
const onDisconnect = () => {
|
||||
console.log('Disconnected from SpacetimeDB');
|
||||
};
|
||||
|
||||
const onConnectError = (_ctx: ErrorContext, err: Error) => {
|
||||
console.log('Error connecting to SpacetimeDB:', err);
|
||||
};
|
||||
|
||||
const connectionBuilder = DbConnection.builder()
|
||||
.withUri(HOST)
|
||||
.withModuleName(DB_NAME)
|
||||
.withToken(localStorage.getItem('auth_token') || undefined)
|
||||
.onConnect(onConnect)
|
||||
.onDisconnect(onDisconnect)
|
||||
.onConnectError(onConnectError);
|
||||
|
||||
createApp({
|
||||
render: () => h(SpacetimeDBProvider, { connectionBuilder }, () => h(App)),
|
||||
}).mount('#app');
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default {
|
||||
name: __t.string(),
|
||||
};
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.object('Add', {
|
||||
name: __t.string(),
|
||||
});
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
// This was generated using spacetimedb cli version 1.11.2 (commit 5508f620e2fd5a4d8a3b7aaf5303776d127f5cbd).
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
DbConnectionBuilder as __DbConnectionBuilder,
|
||||
DbConnectionImpl as __DbConnectionImpl,
|
||||
SubscriptionBuilderImpl as __SubscriptionBuilderImpl,
|
||||
TypeBuilder as __TypeBuilder,
|
||||
Uuid as __Uuid,
|
||||
convertToAccessorMap as __convertToAccessorMap,
|
||||
procedureSchema as __procedureSchema,
|
||||
procedures as __procedures,
|
||||
reducerSchema as __reducerSchema,
|
||||
reducers as __reducers,
|
||||
schema as __schema,
|
||||
t as __t,
|
||||
table as __table,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type DbConnectionConfig as __DbConnectionConfig,
|
||||
type ErrorContextInterface as __ErrorContextInterface,
|
||||
type Event as __Event,
|
||||
type EventContextInterface as __EventContextInterface,
|
||||
type Infer as __Infer,
|
||||
type ReducerEventContextInterface as __ReducerEventContextInterface,
|
||||
type RemoteModule as __RemoteModule,
|
||||
type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,
|
||||
type SubscriptionHandleImpl as __SubscriptionHandleImpl,
|
||||
} from 'spacetimedb';
|
||||
|
||||
// Import and reexport all reducer arg types
|
||||
import OnConnectReducer from './on_connect_reducer';
|
||||
export { OnConnectReducer };
|
||||
import OnDisconnectReducer from './on_disconnect_reducer';
|
||||
export { OnDisconnectReducer };
|
||||
import AddReducer from './add_reducer';
|
||||
export { AddReducer };
|
||||
import SayHelloReducer from './say_hello_reducer';
|
||||
export { SayHelloReducer };
|
||||
|
||||
// Import and reexport all procedure arg types
|
||||
|
||||
// Import and reexport all table handle types
|
||||
import PersonRow from './person_table';
|
||||
export { PersonRow };
|
||||
|
||||
// Import and reexport all types
|
||||
import Add from './add_type';
|
||||
export { Add };
|
||||
import Init from './init_type';
|
||||
export { Init };
|
||||
import OnConnect from './on_connect_type';
|
||||
export { OnConnect };
|
||||
import OnDisconnect from './on_disconnect_type';
|
||||
export { OnDisconnect };
|
||||
import Person from './person_type';
|
||||
export { Person };
|
||||
import SayHello from './say_hello_type';
|
||||
export { SayHello };
|
||||
|
||||
/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */
|
||||
const tablesSchema = __schema(
|
||||
__table(
|
||||
{
|
||||
name: 'person',
|
||||
indexes: [],
|
||||
constraints: [],
|
||||
},
|
||||
PersonRow
|
||||
)
|
||||
);
|
||||
|
||||
/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */
|
||||
const reducersSchema = __reducers(
|
||||
__reducerSchema('add', AddReducer),
|
||||
__reducerSchema('say_hello', SayHelloReducer)
|
||||
);
|
||||
|
||||
/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */
|
||||
const proceduresSchema = __procedures();
|
||||
|
||||
/** The remote SpacetimeDB module schema, both runtime and type information. */
|
||||
const REMOTE_MODULE = {
|
||||
versionInfo: {
|
||||
cliVersion: '1.11.2' as const,
|
||||
},
|
||||
tables: tablesSchema.schemaType.tables,
|
||||
reducers: reducersSchema.reducersType.reducers,
|
||||
...proceduresSchema,
|
||||
} satisfies __RemoteModule<
|
||||
typeof tablesSchema.schemaType,
|
||||
typeof reducersSchema.reducersType,
|
||||
typeof proceduresSchema
|
||||
>;
|
||||
|
||||
/** The tables available in this remote SpacetimeDB module. */
|
||||
export const tables = __convertToAccessorMap(tablesSchema.schemaType.tables);
|
||||
|
||||
/** The reducers available in this remote SpacetimeDB module. */
|
||||
export const reducers = __convertToAccessorMap(
|
||||
reducersSchema.reducersType.reducers
|
||||
);
|
||||
|
||||
/** The context type returned in callbacks for all possible events. */
|
||||
export type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for reducer events. */
|
||||
export type ReducerEventContext = __ReducerEventContextInterface<
|
||||
typeof REMOTE_MODULE
|
||||
>;
|
||||
/** The context type returned in callbacks for subscription events. */
|
||||
export type SubscriptionEventContext = __SubscriptionEventContextInterface<
|
||||
typeof REMOTE_MODULE
|
||||
>;
|
||||
/** The context type returned in callbacks for error events. */
|
||||
export type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */
|
||||
export type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;
|
||||
|
||||
/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */
|
||||
export class SubscriptionBuilder extends __SubscriptionBuilderImpl<
|
||||
typeof REMOTE_MODULE
|
||||
> {}
|
||||
|
||||
/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */
|
||||
export class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}
|
||||
|
||||
/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */
|
||||
export class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {
|
||||
/** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */
|
||||
static builder = (): DbConnectionBuilder => {
|
||||
return new DbConnectionBuilder(
|
||||
REMOTE_MODULE,
|
||||
(config: __DbConnectionConfig<typeof REMOTE_MODULE>) =>
|
||||
new DbConnection(config)
|
||||
);
|
||||
};
|
||||
|
||||
/** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */
|
||||
override subscriptionBuilder = (): SubscriptionBuilder => {
|
||||
return new SubscriptionBuilder(this);
|
||||
};
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.object('Init', {});
|
||||
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.object('OnConnect', {});
|
||||
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.object('OnDisconnect', {});
|
||||
@@ -0,0 +1,15 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.row({
|
||||
name: __t.string(),
|
||||
});
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.object('Person', {
|
||||
name: __t.string(),
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,13 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from 'spacetimedb';
|
||||
|
||||
export default __t.object('SayHello', {});
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"types": ["vite/client"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["env.d.ts", "src", "vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
});
|
||||
@@ -127,6 +127,12 @@ fn main() -> anyhow::Result<()> {
|
||||
.help("Update all targets (equivalent to --typescript --rust-and-cli --csharp)")
|
||||
.conflicts_with_all(["typescript", "rust-and-cli", "csharp"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("accept-snapshots")
|
||||
.long("accept-snapshots")
|
||||
.action(clap::ArgAction::SetTrue)
|
||||
.help("If there are snapshots to review automatically accept them all."),
|
||||
)
|
||||
.group(
|
||||
ArgGroup::new("update-targets")
|
||||
.args(["all", "typescript", "rust-and-cli", "csharp"])
|
||||
@@ -184,7 +190,50 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
process_license_file("LICENSE.txt", &full_version);
|
||||
process_license_file("licenses/BSL.txt", &full_version);
|
||||
// Rebuild `Cargo.lock`
|
||||
println!("$> cargo check");
|
||||
cmd!("cargo", "check").run().expect("Cargo check failed!");
|
||||
|
||||
println!("$> pnpm install");
|
||||
cmd!("pnpm", "install").run().expect("pnpm run build failed!");
|
||||
|
||||
println!("$> pnpm run build");
|
||||
cmd!("pnpm", "run", "build").run().expect("pnpm run build failed!");
|
||||
|
||||
println!("$> pnpm --dir templates/chat-react-ts generate");
|
||||
cmd!("pnpm", "--dir", "templates/chat-react-ts", "generate")
|
||||
.run()
|
||||
.expect("pnpm generate failed!");
|
||||
|
||||
if matches.get_flag("accept-snapshots") {
|
||||
// Generate and auto-accept snapshots
|
||||
println!("$> INSTA_UPDATE=always cargo test -p spacetimedb-codegen --test codegen");
|
||||
cmd!("cargo", "test", "-p", "spacetimedb-codegen", "--test", "codegen")
|
||||
.env("INSTA_UPDATE", "always")
|
||||
.run()
|
||||
.expect("cargo test -p spacetimedb-codegen --test codegen (INSTA_UPDATE=always) failed!");
|
||||
} else {
|
||||
println!("$> cargo install cargo-insta");
|
||||
cmd!("cargo", "install", "cargo-insta")
|
||||
.run()
|
||||
.expect("cargo install cargo-insta failed!");
|
||||
|
||||
// Initial test - this will generate snapshots. This is expected to fail.
|
||||
println!("$> cargo test -p spacetimedb-codegen --test codegen");
|
||||
let _ = cmd!("cargo", "test", "-p", "spacetimedb-codegen", "--test", "codegen").run();
|
||||
|
||||
// Review the new snapshots
|
||||
println!("$> cargo insta review");
|
||||
cmd!("cargo", "insta", "review")
|
||||
.run()
|
||||
.expect("cargo insta review failed!");
|
||||
|
||||
// Test again now that the user has had a chance to accept the snapshots
|
||||
println!("$> cargo test -p spacetimedb-codegen --test codegen");
|
||||
cmd!("cargo", "test", "-p", "spacetimedb-codegen", "--test", "codegen")
|
||||
.run()
|
||||
.expect("cargo test -p spacetimedb-codegen --test codegen failed!");
|
||||
}
|
||||
}
|
||||
|
||||
if matches.get_flag("typescript") || matches.get_flag("all") {
|
||||
|
||||
Reference in New Issue
Block a user