From 652982712640a33e8dddfbe8a2e137630d23f460 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 2 Nov 2025 21:00:43 -0500 Subject: [PATCH 01/23] Big prototype --- .../examples/quickstart-chat/src/App.tsx | 3 +- .../src/module_bindings/index.ts | 6 +- .../src/module_bindings/user_table.ts | 8 +- .../src/react/SpacetimeDBProvider.ts | 96 +++- .../src/react/connection_state.ts | 12 + crates/bindings-typescript/src/react/index.ts | 1 + .../src/react/useReducer.ts | 17 + .../src/react/useSpacetimeDB.ts | 14 +- .../bindings-typescript/src/react/useTable.ts | 34 +- .../src/sdk/client_api/index.ts | 2 +- .../src/sdk/client_cache.ts | 105 +++- .../src/sdk/db_connection_builder.ts | 48 +- .../src/sdk/db_connection_impl.ts | 276 ++++++---- .../bindings-typescript/src/sdk/db_context.ts | 24 +- crates/bindings-typescript/src/sdk/db_view.ts | 14 + crates/bindings-typescript/src/sdk/event.ts | 5 +- .../src/sdk/event_context.ts | 40 +- crates/bindings-typescript/src/sdk/index.ts | 4 +- .../src/sdk/reducer_event.ts | 5 +- .../src/sdk/reducer_handle.ts | 12 + .../bindings-typescript/src/sdk/reducers.ts | 25 + .../src/sdk/set_reducer_flags.ts | 3 + .../src/sdk/spacetime_module.ts | 19 +- .../src/sdk/subscription_builder_impl.ts | 57 +-- .../src/sdk/table_cache.ts | 113 +++-- .../src/sdk/table_handle.ts | 80 ++- .../bindings-typescript/src/server/db_view.ts | 25 + .../bindings-typescript/src/server/index.ts | 2 + .../bindings-typescript/src/server/indexes.ts | 53 +- .../src/server/reducers.ts | 122 ++++- .../bindings-typescript/src/server/runtime.ts | 44 +- .../bindings-typescript/src/server/schema.ts | 30 +- .../bindings-typescript/src/server/table.ts | 37 +- .../src/server/type_util.ts | 12 + .../bindings-typescript/test-app/src/App.tsx | 4 +- .../test-app/src/module_bindings/index.ts | 473 +++++++++++------- .../src/module_bindings/player_table.ts | 10 +- .../src/module_bindings/user_table.ts | 6 +- .../src/module_bindings/counter_table.ts | 6 +- .../src/module_bindings/offline_user_table.ts | 6 +- .../src/module_bindings/user_table.ts | 6 +- crates/codegen/src/typescript.rs | 13 +- 42 files changed, 1276 insertions(+), 596 deletions(-) create mode 100644 crates/bindings-typescript/src/react/connection_state.ts create mode 100644 crates/bindings-typescript/src/react/useReducer.ts create mode 100644 crates/bindings-typescript/src/sdk/db_view.ts create mode 100644 crates/bindings-typescript/src/sdk/reducer_handle.ts create mode 100644 crates/bindings-typescript/src/sdk/reducers.ts create mode 100644 crates/bindings-typescript/src/sdk/set_reducer_flags.ts create mode 100644 crates/bindings-typescript/src/server/db_view.ts diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx b/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx index 78ae2da57a0..0fa172b20ab 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx +++ b/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx @@ -16,8 +16,9 @@ function App() { const [settingName, setSettingName] = useState(false); const [systemMessages, setSystemMessages] = useState([] as Message[]); const [newMessage, setNewMessage] = useState(''); - const conn = useSpacetimeDB(); + + conn.setReducerFlags() const { identity, isActive: connected } = conn; // Subscribe to all messages in the chat diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts index f30b1c1253b..b104c9eced0 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts +++ b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts @@ -61,9 +61,9 @@ const REMOTE_MODULE = { user: { tableName: 'user' as const, rowType: User.getTypeScriptAlgebraicType(), - primaryKey: 'identity', + primaryKey: 'identity' as const, primaryKeyInfo: { - colName: 'identity', + colName: 'identity' as const, colType: ( User.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product ).value.elements[0].algebraicType, @@ -119,7 +119,7 @@ const REMOTE_MODULE = { setReducerFlagsConstructor: () => { return new SetReducerFlags(); }, -}; +} satisfies RemoteModule; // A type representing all the possible variants of a reducer. export type Reducer = diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts index 85687a99eff..9b35746a2ab 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts +++ b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts @@ -47,7 +47,7 @@ declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; * like `ctx.db.user.on_insert(...)`. */ export class UserTableHandle - implements __TableHandle + extends ClientTable { // phantom type to track the table name readonly tableName!: TableName; @@ -76,11 +76,11 @@ export class UserTableHandle * Get a handle on the `identity` unique index on the table `user`. */ identity = { - // Find the subscribed row whose `identity` column value is equal to `col_val`, + // Find the subscribed row whose `identity` column value is equal to `colVal`, // if such a row is present in the client cache. - find: (col_val: __Identity): User | undefined => { + find: (colVal: __Identity): User | undefined => { for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, col_val)) { + if (__deepEqual(row.identity, colVal)) { return row; } } diff --git a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts index 0af29acf067..6899bed3610 100644 --- a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts @@ -1,38 +1,96 @@ import { DbConnectionBuilder, type DbConnectionImpl, + type ErrorContextInterface, + type SubscriptionEventContextInterface, } from '../sdk/db_connection_impl'; import * as React from 'react'; import { SpacetimeDBContext } from './useSpacetimeDB'; +import type { ConnectionState } from './connection_state'; +import { ConnectionId } from '../lib/connection_id'; export interface SpacetimeDBProviderProps< DbConnection extends DbConnectionImpl, - ErrorContext, - SubscriptionEventContext, + ErrorContext extends ErrorContextInterface, + SubscriptionEventContext extends SubscriptionEventContextInterface, > { - connectionBuilder: DbConnectionBuilder< - DbConnection, - ErrorContext, - SubscriptionEventContext - >; + connectionBuilder: DbConnectionBuilder; children?: React.ReactNode; } export function SpacetimeDBProvider< DbConnection extends DbConnectionImpl, - ErrorContext, - SubscriptionEventContext, ->({ - connectionBuilder, - children, -}: SpacetimeDBProviderProps< - DbConnection, - ErrorContext, - SubscriptionEventContext ->): React.JSX.Element { + ErrorContext extends ErrorContextInterface, + SubscriptionEventContext extends SubscriptionEventContextInterface, +>({ connectionBuilder, children }: SpacetimeDBProviderProps) { + // Holds the imperative connection instance when (and only when) we’re on the client. + const connRef = React.useRef(null); + const getConnection = React.useCallback(() => connRef.current, []); + + const [state, setState] = React.useState>({ + isActive: false, + identity: undefined, + token: undefined, + connectionId: ConnectionId.random(), + connectionError: undefined, + getConnection, + }); + + // Build on the client only; useEffect won't run during SSR. + React.useEffect(() => { + // Register callback for onConnect to update state + const onConnect = (conn: DbConnectionImpl) => { + setState(s => ({ + ...s, + isActive: conn.isActive, + identity: conn.identity, + token: conn.token, + connectionId: conn.connectionId, + })); + }; + const onDisconnect = (ctx: ErrorContextInterface) => { + setState(s => ({ + ...s, + isActive: ctx.isActive, + })); + }; + const onConnectError = (ctx: ErrorContextInterface, err: Error) => { + setState(s => ({ + ...s, + isActive: ctx.isActive, + connectionError: err, + })); + }; + connectionBuilder.onConnect(onConnect); + connectionBuilder.onDisconnect(onDisconnect); + connectionBuilder.onConnectError(onConnectError); + + const conn = connRef.current!; + setState(s => ({ + ...s, + isActive: conn.isActive, + identity: conn.identity, + token: conn.token, + connectionId: conn.connectionId, + })); + + // Lazily build once + if (!connRef.current) { + connRef.current = connectionBuilder.build(); + } + + return () => { + connRef.current?.removeOnConnect(onConnect); + connRef.current?.removeOnDisconnect(onDisconnect); + connRef.current?.removeOnConnectError(onConnectError); + connRef.current?.disconnect(); + connRef.current = null; + }; + }, [connectionBuilder]); + return React.createElement( SpacetimeDBContext.Provider, - { value: connectionBuilder.build() }, // May need to modify this to do it lazily in server-side rendering + { value: state }, children ); -} + } \ No newline at end of file diff --git a/crates/bindings-typescript/src/react/connection_state.ts b/crates/bindings-typescript/src/react/connection_state.ts new file mode 100644 index 00000000000..4f45c6b902e --- /dev/null +++ b/crates/bindings-typescript/src/react/connection_state.ts @@ -0,0 +1,12 @@ +import type { ConnectionId } from "../lib/connection_id"; +import type { Identity } from "../lib/identity"; +import type { DbConnectionImpl } from "../sdk/db_connection_impl"; + +export type ConnectionState = { + isActive: boolean; + identity?: Identity; + token?: string; + connectionId: ConnectionId; + connectionError?: Error; + getConnection(): DbConnection | null; +}; diff --git a/crates/bindings-typescript/src/react/index.ts b/crates/bindings-typescript/src/react/index.ts index b99fe7f78f4..6bcf01a6a28 100644 --- a/crates/bindings-typescript/src/react/index.ts +++ b/crates/bindings-typescript/src/react/index.ts @@ -1,3 +1,4 @@ export * from './SpacetimeDBProvider.ts'; export { useSpacetimeDB } from './useSpacetimeDB.ts'; export { useTable, where, eq } from './useTable.ts'; +export { useReducer } from './useReducer.ts'; diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts new file mode 100644 index 00000000000..dab84320ed2 --- /dev/null +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -0,0 +1,17 @@ +import type { DbConnectionImpl } from "../sdk/db_connection_impl"; +import type { ReducerNamesFromReducers } from "../sdk/reducer_handle"; +import { useSpacetimeDB } from "./useSpacetimeDB"; + +// export function useReducer< +// DbConnection extends DbConnectionImpl, +// ReducerName extends ReducerNamesFromReducers = ReducerNamesFromReducers< +// DbConnection['reducers'] +// >, +// ReducerType = DbConnection['reducers'][ReducerName & keyof DbConnection['reducers']] +// >( +// reducerName: ReducerName, +// ): ReducerType { +// const connectionState = useSpacetimeDB(); +// const connection = connectionState.getConnection()!; +// return connection.reducers[reducerName as keyof typeof connection.reducers]; +// } \ No newline at end of file diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index 505c8143742..17f180b9878 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -1,19 +1,17 @@ -import { createContext, useContext, type Context } from 'react'; +import { createContext, useContext } from 'react'; import type { DbConnectionImpl } from '../sdk/db_connection_impl'; +import type { ConnectionState } from './connection_state'; -export const SpacetimeDBContext: Context = - createContext(undefined); +export const SpacetimeDBContext = createContext | undefined>(undefined); // Throws an error if used outside of a SpacetimeDBProvider // Error is caught by other hooks like useTable so they can provide better error messages -export function useSpacetimeDB< - DbConnection extends DbConnectionImpl, ->(): DbConnection { - const context = useContext(SpacetimeDBContext) as DbConnection | undefined; +export function useSpacetimeDB(): ConnectionState { + const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; 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; -} +} \ No newline at end of file diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index 78f675f6e31..5c81fdf2ed4 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -8,6 +8,7 @@ import { import { useSpacetimeDB } from './useSpacetimeDB'; import { DbConnectionImpl, TableCache } from '../sdk/db_connection_impl'; import type { TableNamesFromDb } from '../sdk/table_handle'; +import type { ConnectionState } from './connection_state'; export interface UseQueryCallbacks { onInsert?: (row: RowType) => void; @@ -292,9 +293,9 @@ export function useTable< | undefined; } const [subscribeApplied, setSubscribeApplied] = useState(false); - let spacetime: DbConnection | undefined; + let connectionState: ConnectionState | undefined; try { - spacetime = useSpacetimeDB(); + connectionState = useSpacetimeDB(); } catch { throw new Error( 'Could not find SpacetimeDB client! Did you forget to add a ' + @@ -302,7 +303,6 @@ export function useTable< 'under a `SpacetimeDBProvider` component.' ); } - const client = spacetime; const query = `SELECT * FROM ${tableName}` + @@ -314,8 +314,12 @@ export function useTable< const whereKey = whereClause ? toString(whereClause) : ''; const computeSnapshot = useCallback((): Snapshot => { - const table = client.db[ - tableName as keyof typeof client.db + const connection = connectionState.getConnection(); + if (!connection) { + return { rows: [], state: 'loading' }; + } + const table = connection.db[ + tableName as keyof typeof connection.db ] as unknown as TableCache; const result: readonly RowType[] = whereClause ? table.iter().filter(row => evaluate(whereClause, row)) @@ -325,11 +329,12 @@ export function useTable< state: subscribeApplied ? 'ready' : 'loading', }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [client, tableName, whereKey, subscribeApplied]); + }, [connectionState, tableName, whereKey, subscribeApplied]); useEffect(() => { - if (client.isActive) { - const cancel = client + const connection = connectionState.getConnection()!; + if (connectionState.isActive && connection) { + const cancel = connection .subscriptionBuilder() .onApplied(() => { setSubscribeApplied(true); @@ -339,7 +344,7 @@ export function useTable< cancel.unsubscribe(); }; } - }, [query, client.isActive, client]); + }, [query, connectionState.isActive, connectionState]); const subscribe = useCallback( (onStoreChange: () => void) => { @@ -400,8 +405,13 @@ export function useTable< } }; - const table = client.db[ - tableName as keyof typeof client.db + const connection = connectionState.getConnection(); + if (!connection) { + return () => {}; + } + + const table = connection.db[ + tableName as keyof typeof connection.db ] as unknown as TableCache; table.onInsert(onInsert); table.onDelete(onDelete); @@ -415,7 +425,7 @@ export function useTable< }, // eslint-disable-next-line react-hooks/exhaustive-deps [ - client, + connectionState, tableName, whereKey, callbacks?.onDelete, diff --git a/crates/bindings-typescript/src/sdk/client_api/index.ts b/crates/bindings-typescript/src/sdk/client_api/index.ts index 5e3cc809133..2441d1d8732 100644 --- a/crates/bindings-typescript/src/sdk/client_api/index.ts +++ b/crates/bindings-typescript/src/sdk/client_api/index.ts @@ -112,7 +112,7 @@ const REMOTE_MODULE = { // SDK, but if in the future we wanted to create a class this would be // necessary because classes have methods, so we'll keep it. eventContextConstructor: ( - imp: __DbConnectionImpl, + imp: __DbConnectionImpl ) => { return { diff --git a/crates/bindings-typescript/src/sdk/client_cache.ts b/crates/bindings-typescript/src/sdk/client_cache.ts index 81beec64ba5..40b8c98bc08 100644 --- a/crates/bindings-typescript/src/sdk/client_cache.ts +++ b/crates/bindings-typescript/src/sdk/client_cache.ts @@ -1,47 +1,100 @@ -import type { TableRuntimeTypeInfo } from './spacetime_module.ts'; +import type { UntypedSchemaDef } from '../server/schema.ts'; +import type { UntypedTableDef } from '../server/table.ts'; +import type { UntypedReducersDef } from './reducers.ts'; import { TableCache } from './table_cache.ts'; -export class ClientCache { +type TableName = + [SchemaDef] extends [UntypedSchemaDef] ? SchemaDef['tables'][number]['name'] : string; + +export type TableDefForTableName = + [SchemaDef] extends [UntypedSchemaDef] + ? Extract + : UntypedTableDef; + +type TableCacheForTableName< + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, + N +> = TableCache>; + +/** + * This is a helper class that provides a mapping from table names to their corresponding TableCache instances + * while preserving the correspondence between the key and value type. + */ +class TableMap { + private readonly map: Map>> = new Map(); + + get>(key: K): TableCacheForTableName | undefined { + // Cast required: a Map can't refine the union to the exact K-specific member on get(key: K). + return this.map.get(key) as TableCacheForTableName | undefined; + } + + set>(key: K, value: TableCacheForTableName): this { + this.map.set(key, value); + return this; + } + + has(key: TableName): boolean { + return this.map.has(key); + } + + delete(key: TableName): boolean { + return this.map.delete(key); + } + + // optional: iteration stays broadly typed (cannot express per-key relation here) + keys(): IterableIterator { return this.map.keys(); } + values(): IterableIterator>> { return this.map.values(); } + entries(): IterableIterator<[string, TableCacheForTableName>]> { return this.map.entries(); } + [Symbol.iterator]() { return this.entries(); } +} + +/** + * ClientCache maintains a cache of TableCache instances for each table in the database. + * It provides methods to get or create TableCache instances by table name, + * ensuring type safety based on the provided SchemaDef. + */ +export class ClientCache { /** * The tables in the database. */ - tables: Map>; - - constructor() { - this.tables = new Map(); - } + readonly tables = new TableMap(); /** * Returns the table with the given name. - * @param name The name of the table. - * @returns The table + * - If SchemaDef is a concrete schema, `name` is constrained to known table names, + * and the return type matches that table. + * - If SchemaDef is undefined, `name` is string and the return type is untyped. */ - getTable>( - name: string - ): TableCache { + getTable>(name: N): TableCacheForTableName { const table = this.tables.get(name); - - // ! This should not happen as the table should be available but an exception is thrown just in case. if (!table) { console.error( 'The table has not been registered for this client. Please register the table before using it. If you have registered global tables using the SpacetimeDBClient.registerTables() or `registerTable()` method, please make sure that is executed first!' ); - throw new Error(`Table ${name} does not exist`); + throw new Error(`Table ${String(name)} does not exist`); } - return table; } - getOrCreateTable>( - tableTypeInfo: TableRuntimeTypeInfo - ): TableCache { - let table: TableCache; - if (!this.tables.has(tableTypeInfo.tableName)) { - table = new TableCache(tableTypeInfo); - this.tables.set(tableTypeInfo.tableName, table); - } else { - table = this.tables.get(tableTypeInfo.tableName)!; + /** + * Returns the table with the given name, creating it if needed. + * - Typed mode: `tableTypeInfo.tableName` is constrained to known names and + * the return type matches that table. + * - Untyped mode: accepts any string and returns an untyped TableCache. + */ + getOrCreateTable>( + tableDef: TableDefForTableName + ): TableCacheForTableName { + const name = tableDef.name as N; + + let table = this.tables.get(name); + if (table) { + return table; } - return table; + + const newTable = new TableCache>(tableDef); + this.tables.set(name, newTable); + return newTable; } } diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index a891dd3cb6b..b585cf35564 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -1,17 +1,19 @@ import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl'; import { EventEmitter } from './event_emitter'; -import type { Identity } from '../'; -import type RemoteModule from './spacetime_module'; +import type { DbConnectionConfig, ErrorContextInterface, Identity, SubscriptionEventContextInterface } from '../'; +import { type RemoteModule, type RemoteModule2 } from './spacetime_module'; import { ensureMinimumVersionOrThrow } from './version'; import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; +import type { UntypedSchemaDef } from '../server/schema'; +import type { UntypedReducersDef } from './reducers'; /** * The database client connection to a SpacetimeDB server. */ export class DbConnectionBuilder< - DbConnection, - ErrorContext, - _SubscriptionEventContext, + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, + DbConnection extends DbConnectionImpl, > { #uri?: URL; #nameOrAddress?: string; @@ -32,8 +34,8 @@ export class DbConnectionBuilder< * @param dbConnectionConstructor The constructor to use to create a new `DbConnection`. */ constructor( - private remoteModule: RemoteModule, - private dbConnectionConstructor: (imp: DbConnectionImpl) => DbConnection + private remoteModule: RemoteModule2, + private dbConnectionCtor: (config: DbConnectionConfig) => DbConnection ) { this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn; } @@ -179,7 +181,7 @@ export class DbConnectionBuilder< * }); * ``` */ - onConnectError(callback: (ctx: ErrorContext, error: Error) => void): this { + onConnectError(callback: (ctx: ErrorContextInterface, error: Error) => void): this { this.#emitter.on('connectError', callback); return this; } @@ -211,7 +213,7 @@ export class DbConnectionBuilder< * @throws {Error} Throws an error if called multiple times on the same `DbConnectionBuilder`. */ onDisconnect( - callback: (ctx: ErrorContext, error?: Error | undefined) => void + callback: (ctx: ErrorContextInterface, error?: Error | undefined) => void ): this { this.#emitter.on('disconnect', callback); return this; @@ -231,7 +233,7 @@ export class DbConnectionBuilder< * DbConnection.builder().withUri(host).withModuleName(name_or_address).withToken(auth_token).build(); * ``` */ - build(): DbConnection { + build(): DbConnectionImpl { if (!this.#uri) { throw new Error('URI is required to connect to SpacetimeDB'); } @@ -245,19 +247,17 @@ export class DbConnectionBuilder< // Ideally, it would be a compile time error, but I'm not sure how to accomplish that. ensureMinimumVersionOrThrow(this.remoteModule.versionInfo?.cliVersion); - return this.dbConnectionConstructor( - new DbConnectionImpl({ - uri: this.#uri, - nameOrAddress: this.#nameOrAddress, - identity: this.#identity, - token: this.#token, - emitter: this.#emitter, - compression: this.#compression, - lightMode: this.#lightMode, - confirmedReads: this.#confirmedReads, - createWSFn: this.#createWSFn, - remoteModule: this.remoteModule, - }) - ); + return this.dbConnectionCtor({ + uri: this.#uri, + nameOrAddress: this.#nameOrAddress, + identity: this.#identity, + token: this.#token, + emitter: this.#emitter, + compression: this.#compression, + lightMode: this.#lightMode, + confirmedReads: this.#confirmedReads, + createWSFn: this.#createWSFn, + remoteModule: this.remoteModule, + }) } } diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 16720d8837b..64c7b435554 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -1,4 +1,4 @@ -import { ConnectionId } from '../'; +import { ConnectionId, ProductType } from '../'; import { AlgebraicType, type AlgebraicTypeVariants, @@ -22,6 +22,7 @@ import { type EventContextInterface, type ReducerEventContextInterface, type SubscriptionEventContextInterface, + type UntypedEventContext, } from './event_context.ts'; import { EventEmitter } from './event_emitter.ts'; import { decompress } from './decompress.ts'; @@ -33,7 +34,7 @@ import type { UnsubscribeAppliedMessage, } from './message_types.ts'; import type { ReducerEvent } from './reducer_event.ts'; -import type RemoteModule from './spacetime_module.ts'; +import { type RemoteModule, type RemoteModule2 } from './spacetime_module.ts'; import { TableCache, type Operation, @@ -49,8 +50,13 @@ import { type SubscribeEvent, } from './subscription_builder_impl.ts'; import { stdbLogger } from './logger.ts'; -import { type ReducerRuntimeTypeInfo } from './spacetime_module.ts'; import { fromByteArray } from 'base64-js'; +import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; +import type { UntypedSchemaDef } from '../server/schema.ts'; +import type { DbView } from '../server/db_view.ts'; +import type { RowType, UntypedTableDef } from '../server/table.ts'; +import type { CamelCase } from '../server/type_util.ts'; +import { toCamelCase } from '../server/runtime.ts'; export { DbConnectionBuilder, SubscriptionBuilderImpl, TableCache, type Event }; @@ -66,12 +72,13 @@ export type { export type ConnectionEvent = 'connect' | 'disconnect' | 'connectError'; export type CallReducerFlags = 'FullUpdate' | 'NoSuccessNotify'; -type ReducerEventCallback = ( - ctx: ReducerEventContextInterface, +type ReducerEventCallback = ( + ctx: ReducerEventContextInterface, ...args: ReducerArgs ) => void; -type SubscriptionEventCallback = ( - ctx: SubscriptionEventContextInterface + +type SubscriptionEventCallback = ( + ctx: SubscriptionEventContextInterface, ) => void; function callReducerFlagsToNumber(flags: CallReducerFlags): number { @@ -83,25 +90,23 @@ function callReducerFlagsToNumber(flags: CallReducerFlags): number { } } -type DbConnectionConfig = { +export type DbConnectionConfig = { uri: URL; nameOrAddress: string; identity?: Identity; token?: string; emitter: EventEmitter; - remoteModule: RemoteModule; createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn; compression: 'gzip' | 'none'; lightMode: boolean; confirmedReads?: boolean; + remoteModule: RemoteModule2; }; export class DbConnectionImpl< - DBView = any, - Reducers = any, - SetReducerFlags = any, -> implements DbContext -{ + SchemaDef extends UntypedSchemaDef, + ReducersDef extends UntypedReducersDef, +> implements DbContext { /** * Whether or not the connection is active. */ @@ -121,20 +126,20 @@ export class DbConnectionImpl< * The accessor field to access the tables in the database and associated * callback functions. */ - db: DBView; + db: DbView; /** * The accessor field to access the reducers in the database and associated * callback functions. */ - reducers: Reducers; + reducers: ReducersView; /** * The accessor field to access functions related to setting flags on * reducers regarding how the server should handle the reducer call and * the events that it sends back to the client. */ - setReducerFlags: SetReducerFlags; + setReducerFlags: SetReducerFlags; /** * The `ConnectionId` of the connection to to the database. @@ -144,18 +149,19 @@ export class DbConnectionImpl< // These fields are meant to be strictly private. #queryId = 0; #emitter: EventEmitter; - #reducerEmitter: EventEmitter = + #reducerEmitter: EventEmitter> = new EventEmitter(); - #onApplied?: SubscriptionEventCallback; - #remoteModule: RemoteModule; + #onApplied?: SubscriptionEventCallback; #messageQueue = Promise.resolve(); - #subscriptionManager = new SubscriptionManager(); + #subscriptionManager = new SubscriptionManager(); + #remoteModule: RemoteModule2; + #callReducerFlags = new Map(); // These fields are not part of the public API, but in a pinch you // could use JavaScript to access them by bypassing TypeScript's // private fields. // We use them in testing. - private clientCache: ClientCache; + private clientCache: ClientCache; private ws?: WebsocketDecompressAdapter | WebsocketTestAdapter; private wsPromise: Promise< WebsocketDecompressAdapter | WebsocketTestAdapter | undefined @@ -172,7 +178,7 @@ export class DbConnectionImpl< compression, lightMode, confirmedReads, - }: DbConnectionConfig) { + }: DbConnectionConfig) { stdbLogger('info', 'Connecting to SpacetimeDB WS...'); // We use .toString() here because some versions of React Native contain a bug where the URL constructor @@ -192,13 +198,10 @@ export class DbConnectionImpl< const connectionId = this.connectionId.toHexString(); url.searchParams.set('connection_id', connectionId); - this.clientCache = new ClientCache(); - this.db = this.#remoteModule.dbViewConstructor(this); - this.setReducerFlags = this.#remoteModule.setReducerFlagsConstructor(); - this.reducers = this.#remoteModule.reducersConstructor( - this, - this.setReducerFlags - ); + this.clientCache = new ClientCache(); + this.db = this.#makeDbView(remoteModule.tables); + this.reducers = this.#makeReducers(remoteModule.reducers); + this.setReducerFlags = this.#makeSetReducerFlags(remoteModule.reducers); this.wsPromise = createWSFn({ url, @@ -236,6 +239,74 @@ export class DbConnectionImpl< return queryId; }; + #makeDbView(def: SchemaDef): DbView { + const view = Object.create(null) as DbView; + + for (const tbl of def.tables) { + // DbView uses this name verbatim + const key = tbl.accessorName; + Object.defineProperty(view, key, { + enumerable: true, + configurable: false, + get: () => { + return this.clientCache.getOrCreateTable(tbl); + }, + }); + } + + return view; + } + + #makeReducers(def: ReducersDef): ReducersView { + const out: Record = {}; + + for (const reducer of def.reducers) { + const key = toCamelCase(reducer.name); + + (out as any)[key] = (params: typeof reducer.params) => { + const flags = this.#callReducerFlags.get(reducer.name) ?? 'FullUpdate'; + this.callReducerWithParams( + reducer.name, + reducer.paramsSpacetimeType, + params, + flags + ); + }; + } + + return out as ReducersView; + } + + #makeSetReducerFlags(defs: ReducersDef): SetReducerFlags { + const out = Object.create(null) as SetReducerFlags; + for (const r of defs.reducers) { + const key = toCamelCase(r.name); + Object.defineProperty(out, key, { + enumerable: true, + configurable: false, + value: (flags: CallReducerFlags) => { + this.#callReducerFlags.set(r.name, flags); + }, + }); + } + return out; + } + + #makeEventContext( + event: Event + ): EventContextInterface { + // Bind methods to preserve `this` (#private fields safe) + return { + db: this.db, + reducers: this.reducers, + setReducerFlags: this.setReducerFlags, + isActive: this.isActive, + subscriptionBuilder: this.subscriptionBuilder.bind(this), + disconnect: this.disconnect.bind(this), + event, + }; + } + // NOTE: This is very important!!! This is the actual function that // gets called when you call `connection.subscriptionBuilder()`. // The `subscriptionBuilder` function which is generated, just shadows @@ -243,13 +314,13 @@ export class DbConnectionImpl< // Do not remove this function, or shoot yourself in the foot please. // It's not clear what would be a better way to do this at this exact // moment. - subscriptionBuilder = (): SubscriptionBuilderImpl => { + subscriptionBuilder = (): SubscriptionBuilderImpl => { return new SubscriptionBuilderImpl(this); }; registerSubscription( - handle: SubscriptionHandleImpl, - handleEmitter: EventEmitter, + handle: SubscriptionHandleImpl, + handleEmitter: EventEmitter>, querySql: string[] ): number { const queryId = this.#getNextQueryId(); @@ -292,16 +363,21 @@ export class DbConnectionImpl< const buffer = rowList.rowsData; const reader = new BinaryReader(buffer); const rows: Operation[] = []; - const rowType = this.#remoteModule.tables[tableName]!.rowType; - const primaryKeyInfo = - this.#remoteModule.tables[tableName]!.primaryKeyInfo; + + // TODO: performance + const table = this.#remoteModule.tables.tables.find(t => t.name === tableName); + const rowType = table!.rowType; + const columnsArray = Object.entries(table!.columns); + const primaryKeyColumnEntry = columnsArray.find(col => col[1].columnMetadata.isPrimaryKey); while (reader.remaining > 0) { - const row = AlgebraicType.deserializeValue(reader, rowType); + const row = ProductType.deserializeValue(reader, rowType); let rowId: ComparablePrimitive | undefined = undefined; - if (primaryKeyInfo !== undefined) { + if (primaryKeyColumnEntry !== undefined) { + const primaryKeyColName = primaryKeyColumnEntry[0]; + const primaryKeyColType = primaryKeyColumnEntry[1].typeBuilder.algebraicType; rowId = AlgebraicType.intoMapKey( - primaryKeyInfo.colType, - row[primaryKeyInfo.colName] + primaryKeyColType, + row[primaryKeyColName] ); } else { // Get a view of the bytes for this row. @@ -322,7 +398,7 @@ export class DbConnectionImpl< const parseTableUpdate = async ( rawTableUpdate: RawTableUpdate - ): Promise => { + ): Promise> => { const tableName = rawTableUpdate.tableName; let operations: Operation[] = []; for (const update of rawTableUpdate.updates) { @@ -354,8 +430,8 @@ export class DbConnectionImpl< const parseDatabaseUpdate = async ( dbUpdate: DatabaseUpdate - ): Promise => { - const tableUpdates: CacheTableUpdate[] = []; + ): Promise[]> => { + const tableUpdates: CacheTableUpdate[] = []; for (const rawTableUpdate of dbUpdate.tables) { tableUpdates.push(await parseTableUpdate(rawTableUpdate)); } @@ -393,7 +469,7 @@ export class DbConnectionImpl< const args = txUpdate.reducerCall.args; const energyQuantaUsed = txUpdate.energyQuantaUsed; - let tableUpdates: CacheTableUpdate[] = []; + let tableUpdates: CacheTableUpdate[] = []; let errMessage = ''; switch (txUpdate.status.tag) { case 'Committed': @@ -418,9 +494,9 @@ export class DbConnectionImpl< let reducerInfo: | { - reducerName: string; - args: Uint8Array; - } + reducerName: string; + args: Uint8Array; + } | undefined; if (reducerName !== '') { reducerInfo = { @@ -516,15 +592,16 @@ export class DbConnectionImpl< } #applyTableUpdates( - tableUpdates: CacheTableUpdate[], - eventContext: EventContextInterface + tableUpdates: CacheTableUpdate[], + eventContext: EventContextInterface ): PendingCallback[] { const pendingCallbacks: PendingCallback[] = []; for (const tableUpdate of tableUpdates) { // Get table information for the table being updated const tableName = tableUpdate.tableName; - const tableTypeInfo = this.#remoteModule.tables[tableName]!; - const table = this.clientCache.getOrCreateTable(tableTypeInfo); + // TODO: performance + const tableDef = this.#remoteModule.tables.tables.find(t => t.name === tableName)!; + const table = this.clientCache.getOrCreateTable(tableDef); const newCallbacks = table.applyOperations( tableUpdate.operations, eventContext @@ -545,11 +622,7 @@ export class DbConnectionImpl< switch (message.tag) { case 'InitialSubscription': { const event: Event = { tag: 'SubscribeApplied' }; - - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); // Remove the event from the subscription event context // It is not a field in the type narrowed SubscriptionEventContext const { event: _, ...subscriptionEventContext } = eventContext; @@ -568,10 +641,7 @@ export class DbConnectionImpl< } case 'TransactionUpdateLight': { const event: Event = { tag: 'UnknownTransaction' }; - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); const callbacks = this.#applyTableUpdates( message.tableUpdates, eventContext @@ -585,17 +655,16 @@ export class DbConnectionImpl< let reducerInfo = message.reducerInfo; let unknownTransaction = false; let reducerArgs: any | undefined; - let reducerTypeInfo: ReducerRuntimeTypeInfo | undefined; + const reducer = this.#remoteModule.reducers.reducers.find(t => t.name === reducerInfo!.reducerName)!; if (!reducerInfo) { unknownTransaction = true; } else { - reducerTypeInfo = - this.#remoteModule.reducers[reducerInfo.reducerName]; + // TODO: performance try { const reader = new BinaryReader(reducerInfo.args as Uint8Array); - reducerArgs = AlgebraicType.deserializeValue( + reducerArgs = ProductType.deserializeValue( reader, - reducerTypeInfo.argsType + reducer?.paramsSpacetimeType ); } catch { // This should only be printed in development, since it's @@ -608,10 +677,7 @@ export class DbConnectionImpl< if (unknownTransaction) { const event: Event = { tag: 'UnknownTransaction' }; - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); const callbacks = this.#applyTableUpdates( message.tableUpdates, eventContext @@ -626,7 +692,6 @@ export class DbConnectionImpl< // At this point, we know that `reducerInfo` is not null because // we return if `unknownTransaction` is true. reducerInfo = reducerInfo!; - reducerTypeInfo = reducerTypeInfo!; // Thus this must be a reducer event create it and emit it. const reducerEvent = { @@ -637,17 +702,16 @@ export class DbConnectionImpl< energyConsumed: message.energyConsumed, reducer: { name: reducerInfo.reducerName, - args: reducerArgs, + // TODO(cloutiertyler): rename back to args to maintain API compatibility + params: reducerArgs, + paramsSpacetimeType: reducer.paramsSpacetimeType, }, }; const event: Event = { tag: 'Reducer', value: reducerEvent, }; - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); const reducerEventContext = { ...eventContext, event: reducerEvent, @@ -660,8 +724,8 @@ export class DbConnectionImpl< const argsArray: any[] = []; ( - reducerTypeInfo.argsType as AlgebraicTypeVariants.Product - ).value.elements.forEach(element => { + reducer.paramsSpacetimeType + ).elements.forEach(element => { argsArray.push(reducerArgs[element.name!]); }); this.#reducerEmitter.emit( @@ -696,10 +760,7 @@ export class DbConnectionImpl< break; } const event: Event = { tag: 'SubscribeApplied' }; - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); const { event: _, ...subscriptionEventContext } = eventContext; const callbacks = this.#applyTableUpdates( message.tableUpdates, @@ -724,10 +785,7 @@ export class DbConnectionImpl< break; } const event: Event = { tag: 'UnsubscribeApplied' }; - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); const { event: _, ...subscriptionEventContext } = eventContext; const callbacks = this.#applyTableUpdates( message.tableUpdates, @@ -743,10 +801,7 @@ export class DbConnectionImpl< case 'SubscriptionError': { const error = Error(message.error); const event: Event = { tag: 'Error', value: error }; - const eventContext = this.#remoteModule.eventContextConstructor( - this, - event - ); + const eventContext = this.#makeEventContext(event); const errorContext = { ...eventContext, event: error, @@ -807,6 +862,23 @@ export class DbConnectionImpl< this.#sendMessage(message); } + /** + * Call a reducer on your SpacetimeDB module with typed arguments. + * @param reducerSchema The schema of the reducer to call + * @param callReducerFlags The flags for the reducer call + * @param params The arguments to pass to the reducer + */ + callReducerWithParams(reducerName: string, paramsType: ProductType, params: object, flags: CallReducerFlags) { + let writer = new BinaryWriter(1024); + ProductType.serializeValue(writer, paramsType, params); + let argsBuffer = writer.getBuffer(); + this.callReducer( + reducerName, + argsBuffer, + flags + ); + } + /** * Close the current connection. * @@ -827,63 +899,63 @@ export class DbConnectionImpl< private on( eventName: ConnectionEvent, - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on(eventName, callback); } private off( eventName: ConnectionEvent, - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off(eventName, callback); } private onConnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on('connect', callback); } private onDisconnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on('disconnect', callback); } private onConnectError( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on('connectError', callback); } - private removeOnConnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + removeOnConnect( + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off('connect', callback); } - private removeOnDisconnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + removeOnDisconnect( + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off('disconnect', callback); } - private removeOnConnectError( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + removeOnConnectError( + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off('connectError', callback); } // Note: This is required to be public because it needs to be // called from the `RemoteReducers` class. - onReducer(reducerName: string, callback: ReducerEventCallback): void { + onReducer(reducerName: string, callback: ReducerEventCallback): void { this.#reducerEmitter.on(reducerName, callback); } // Note: This is required to be public because it needs to be // called from the `RemoteReducers` class. - offReducer(reducerName: string, callback: ReducerEventCallback): void { + offReducer(reducerName: string, callback: ReducerEventCallback): void { this.#reducerEmitter.off(reducerName, callback); } } diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index 533ce32e923..7d5d0da464c 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,20 +1,19 @@ +import type { DbView } from '../server/db_view'; +import type { UntypedSchemaDef } from '../server/schema'; +import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; /** * Interface representing a database context. * - * @template DBView - Type representing the database view. - * @template Reducers - Type representing the reducers. + * @template DbView - Type representing the database view. + * @template ReducersDef - Type representing the reducers. * @template SetReducerFlags - Type representing the reducer flags collection. */ -export interface DbContext< - DBView = any, - Reducers = any, - SetReducerFlags = any, -> { - db: DBView; - reducers: Reducers; - setReducerFlags: SetReducerFlags; +export interface DbContext { + db: DbView; + reducers: ReducersView; + setReducerFlags: SetReducerFlags; isActive: boolean; /** @@ -23,9 +22,8 @@ export interface DbContext< * @returns The subscription builder. */ subscriptionBuilder(): SubscriptionBuilderImpl< - DBView, - Reducers, - SetReducerFlags + SchemaDef, + ReducersDef >; /** diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts new file mode 100644 index 00000000000..664fcbe2483 --- /dev/null +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -0,0 +1,14 @@ +import type { DbView } from "../server/db_view"; +import type { UntypedSchemaDef } from "../server/schema"; + +/** + * An untyped database view, where the table names and row types are not known. + * Each key is a camelCased version of the table name, and each value is an untyped table handle. + * + * For example, a database with tables "user_profile" and "game_stats" would have the type: + * { + * userProfile: TableHandle<"user_profile", any>; + * gameStats: TableHandle<"game_stats", any>; + * } + */ +export type UntypedDbView = DbView; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/event.ts b/crates/bindings-typescript/src/sdk/event.ts index 8be677e47ab..98254af3ca5 100644 --- a/crates/bindings-typescript/src/sdk/event.ts +++ b/crates/bindings-typescript/src/sdk/event.ts @@ -1,6 +1,7 @@ -import type { ReducerEvent, ReducerInfoType } from './reducer_event'; +import type { ReducerEvent } from './reducer_event'; +import type { UntypedReducerDef } from './reducers'; -export type Event = +export type Event = | { tag: 'Reducer'; value: ReducerEvent } | { tag: 'SubscribeApplied' } | { tag: 'UnsubscribeApplied' } diff --git a/crates/bindings-typescript/src/sdk/event_context.ts b/crates/bindings-typescript/src/sdk/event_context.ts index 2ed7112d431..459cfbfeaf0 100644 --- a/crates/bindings-typescript/src/sdk/event_context.ts +++ b/crates/bindings-typescript/src/sdk/event_context.ts @@ -1,41 +1,39 @@ +import type { UntypedSchemaDef } from '../server/schema.ts'; import type { DbContext } from './db_context'; import type { Event } from './event.ts'; -import type { ReducerEvent, ReducerInfoType } from './reducer_event.ts'; +import type { ReducerEvent } from './reducer_event.ts'; +import type { UntypedReducersDef } from './reducers.ts'; + +export type UntypedEventContext = EventContextInterface; export interface EventContextInterface< - DBView = any, - Reducers = any, - SetReducerFlags = any, - Reducer extends ReducerInfoType = never, -> extends DbContext { + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, +> extends DbContext { /** Enum with variants for all possible events. */ - event: Event; + event: Event; } export interface ReducerEventContextInterface< - DBView = any, - Reducers = any, - SetReducerFlags = any, - Reducer extends ReducerInfoType = never, -> extends DbContext { + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, +> extends DbContext { /** Enum with variants for all possible events. */ - event: ReducerEvent; + event: ReducerEvent; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface SubscriptionEventContextInterface< - DBView = any, - Reducers = any, - SetReducerFlags = any, -> extends DbContext { + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, +> extends DbContext { /** No event is provided **/ } export interface ErrorContextInterface< - DBView = any, - Reducers = any, - SetReducerFlags = any, -> extends DbContext { + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, +> extends DbContext { /** Enum with variants for all possible events. */ event?: Error; } diff --git a/crates/bindings-typescript/src/sdk/index.ts b/crates/bindings-typescript/src/sdk/index.ts index 814bc67a54e..568e04bd5b5 100644 --- a/crates/bindings-typescript/src/sdk/index.ts +++ b/crates/bindings-typescript/src/sdk/index.ts @@ -2,4 +2,6 @@ export * from './db_connection_impl.ts'; export * from './client_cache.ts'; export * from './message_types.ts'; -export { type TableHandle } from './table_handle.ts'; +export { type ClientTable } from './table_handle.ts'; +export { type RemoteModule } from './spacetime_module.ts'; +export { type SetReducerFlags } from './reducers.ts'; diff --git a/crates/bindings-typescript/src/sdk/reducer_event.ts b/crates/bindings-typescript/src/sdk/reducer_event.ts index aaebd27fabb..1ffe5c79177 100644 --- a/crates/bindings-typescript/src/sdk/reducer_event.ts +++ b/crates/bindings-typescript/src/sdk/reducer_event.ts @@ -2,10 +2,9 @@ import { ConnectionId } from '../'; import { Timestamp } from '../'; import type { UpdateStatus } from './client_api/index.ts'; import { Identity } from '../'; +import type { UntypedReducerDef } from './reducers.ts'; -export type ReducerInfoType = { name: string; args?: any } | never; - -export type ReducerEvent = { +export type ReducerEvent = { /** * The time when the reducer started running. * diff --git a/crates/bindings-typescript/src/sdk/reducer_handle.ts b/crates/bindings-typescript/src/sdk/reducer_handle.ts new file mode 100644 index 00000000000..a4e76eda4a4 --- /dev/null +++ b/crates/bindings-typescript/src/sdk/reducer_handle.ts @@ -0,0 +1,12 @@ +export type ReducerHandle = { + /** Phantom reducer name */ + readonly reducerName?: ReducerName; +}; + +export type ReducerNamesFromReducers = R extends object + ? { + [K in keyof R]: R[K] extends ReducerHandle + ? ReducerName + : never; + }[keyof R] + : never; diff --git a/crates/bindings-typescript/src/sdk/reducers.ts b/crates/bindings-typescript/src/sdk/reducers.ts new file mode 100644 index 00000000000..e25b805c97f --- /dev/null +++ b/crates/bindings-typescript/src/sdk/reducers.ts @@ -0,0 +1,25 @@ +import type { ProductType } from "../lib/algebraic_type"; +import type { ParamsObj } from "../server/reducers"; +import type { CamelCase } from "../server/type_util"; +import type { CallReducerFlags } from "./db_connection_impl"; + +export type ReducersView = { + [I in keyof R['reducers'] as CamelCase]: + (params: R['reducers'][number]['params']) => void +}; + +export type UntypedReducers = Record void>; + +export type UntypedReducerDef = { + name: string; + params: ParamsObj; + paramsSpacetimeType: ProductType; +}; + +export type UntypedReducersDef = { + reducers: readonly UntypedReducerDef[]; +}; + +export type SetReducerFlags = { + [K in keyof R['reducers'] as CamelCase]: (flags: CallReducerFlags) => void; +}; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/set_reducer_flags.ts b/crates/bindings-typescript/src/sdk/set_reducer_flags.ts new file mode 100644 index 00000000000..a5648b67d36 --- /dev/null +++ b/crates/bindings-typescript/src/sdk/set_reducer_flags.ts @@ -0,0 +1,3 @@ +import type { CallReducerFlags } from "./db_connection_impl"; + +export type UntypedSetReducerFlags = Record void>; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/spacetime_module.ts b/crates/bindings-typescript/src/sdk/spacetime_module.ts index 4a76006f93f..72ad623703e 100644 --- a/crates/bindings-typescript/src/sdk/spacetime_module.ts +++ b/crates/bindings-typescript/src/sdk/spacetime_module.ts @@ -1,9 +1,12 @@ import type { AlgebraicType } from '../'; +import type { UntypedSchemaDef } from '../server/schema'; import type { DbConnectionImpl } from './db_connection_impl'; +import type { UntypedReducersDef } from './reducers'; export interface TableRuntimeTypeInfo { tableName: string; rowType: AlgebraicType; + primaryKey?: string; primaryKeyInfo?: PrimaryKeyInfo; } @@ -17,13 +20,21 @@ export interface ReducerRuntimeTypeInfo { argsType: AlgebraicType; } -export default interface RemoteModule { +export type RemoteModule2 = { + versionInfo: { + cliVersion: CLI; + }; + tables: SchemaDef; + reducers: ReducersDef; +} + +export interface RemoteModule { tables: { [name: string]: TableRuntimeTypeInfo }; reducers: { [name: string]: ReducerRuntimeTypeInfo }; - eventContextConstructor: (imp: DbConnectionImpl, event: any) => any; - dbViewConstructor: (connection: DbConnectionImpl) => any; + eventContextConstructor: (imp: DbConnectionImpl, event: any) => any; + dbViewConstructor: (connection: DbConnectionImpl) => any; reducersConstructor: ( - connection: DbConnectionImpl, + connection: DbConnectionImpl, setReducerFlags: any ) => any; setReducerFlagsConstructor: () => any; diff --git a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts index 5fffd581237..a0856367b7b 100644 --- a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts +++ b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts @@ -1,23 +1,24 @@ +import type { UntypedSchemaDef } from '../server/schema'; import type { DbConnectionImpl } from './db_connection_impl'; import type { ErrorContextInterface, SubscriptionEventContextInterface, } from './event_context'; import { EventEmitter } from './event_emitter'; +import type { UntypedReducersDef } from './reducers'; export class SubscriptionBuilderImpl< - DBView = any, - Reducers = any, - SetReducerFlags = any, + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, > { #onApplied?: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void = undefined; #onError?: ( - ctx: ErrorContextInterface + ctx: ErrorContextInterface ) => void = undefined; constructor( - private db: DbConnectionImpl + private db: DbConnectionImpl ) {} /** @@ -37,9 +38,9 @@ export class SubscriptionBuilderImpl< */ onApplied( cb: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void - ): SubscriptionBuilderImpl { + ): SubscriptionBuilderImpl { this.#onApplied = cb; return this; } @@ -65,8 +66,8 @@ export class SubscriptionBuilderImpl< * @returns The current `SubscriptionBuilder` instance. */ onError( - cb: (ctx: ErrorContextInterface) => void - ): SubscriptionBuilderImpl { + cb: (ctx: ErrorContextInterface) => void + ): SubscriptionBuilderImpl { this.#onError = cb; return this; } @@ -89,7 +90,7 @@ export class SubscriptionBuilderImpl< */ subscribe( query_sql: string | string[] - ): SubscriptionHandleImpl { + ): SubscriptionHandleImpl { const queries = Array.isArray(query_sql) ? query_sql : [query_sql]; if (queries.length === 0) { throw new Error('Subscriptions must have at least one query'); @@ -126,17 +127,16 @@ export class SubscriptionBuilderImpl< export type SubscribeEvent = 'applied' | 'error' | 'end'; -export class SubscriptionManager { +export class SubscriptionManager { subscriptions: Map< number, - { handle: SubscriptionHandleImpl; emitter: EventEmitter } + { handle: SubscriptionHandleImpl; emitter: EventEmitter } > = new Map(); } export class SubscriptionHandleImpl< - DBView = any, - Reducers = any, - SetReducerFlags = any, + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, > { #queryId: number; #unsubscribeCalled: boolean = false; @@ -146,13 +146,13 @@ export class SubscriptionHandleImpl< new EventEmitter(); constructor( - private db: DbConnectionImpl, + private db: DbConnectionImpl, querySql: string[], onApplied?: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void, onError?: ( - ctx: ErrorContextInterface, + ctx: ErrorContextInterface, error: Error ) => void ) { @@ -160,9 +160,8 @@ export class SubscriptionHandleImpl< 'applied', ( ctx: SubscriptionEventContextInterface< - DBView, - Reducers, - SetReducerFlags + SchemaDef, + Reducers > ) => { this.#activeState = true; @@ -174,7 +173,7 @@ export class SubscriptionHandleImpl< this.#emitter.on( 'error', ( - ctx: ErrorContextInterface, + ctx: ErrorContextInterface, error: Error ) => { this.#activeState = false; @@ -202,9 +201,8 @@ export class SubscriptionHandleImpl< 'end', ( _ctx: SubscriptionEventContextInterface< - DBView, - Reducers, - SetReducerFlags + SchemaDef, + Reducers > ) => { this.#endedState = true; @@ -225,7 +223,7 @@ export class SubscriptionHandleImpl< */ unsubscribeThen( onEnd: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void ): void { if (this.#endedState) { @@ -240,9 +238,8 @@ export class SubscriptionHandleImpl< 'end', ( ctx: SubscriptionEventContextInterface< - DBView, - Reducers, - SetReducerFlags + SchemaDef, + Reducers > ) => { this.#endedState = true; diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index dc9fce02431..cd79debf72e 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -3,7 +3,11 @@ import type { TableRuntimeTypeInfo } from './spacetime_module.ts'; import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; -import type { EventContextInterface } from './index.ts'; +import type { EventContextInterface, ClientTable } from './index.ts'; +import type { RowType, Table, UntypedTableDef } from '../server/table.ts'; +import type { UntypedSchemaDef } from '../server/schema.ts'; +import type { UntypedReducersDef } from './reducers.ts'; +import type { ClientTableCore } from './table_handle.ts'; export type Operation< RowType extends Record = Record, @@ -16,10 +20,10 @@ export type Operation< }; export type TableUpdate< - RowType extends Record = Record, + TableDef extends UntypedTableDef, > = { tableName: string; - operations: Operation[]; + operations: Operation>[]; }; export type PendingCallback = { @@ -31,10 +35,12 @@ export type PendingCallback = { * Builder to generate calls to query a `table` in the database */ export class TableCache< - RowType extends Record = Record, -> { - private rows: Map; - private tableTypeInfo: TableRuntimeTypeInfo; + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, + TableDef extends UntypedTableDef, +> implements ClientTableCore { + private rows: Map, number]>; + private tableDef: TableDef; private emitter: EventEmitter<'insert' | 'delete' | 'update'>; /** @@ -43,8 +49,8 @@ export class TableCache< * @param primaryKey column name designated as `#[primarykey]` * @param entityClass the entityClass */ - constructor(tableTypeInfo: TableRuntimeTypeInfo) { - this.tableTypeInfo = tableTypeInfo; + constructor(tableDef: TableDef) { + this.tableDef = tableDef; this.rows = new Map(); this.emitter = new EventEmitter(); } @@ -52,30 +58,45 @@ export class TableCache< /** * @returns number of rows in the table */ - count(): number { - return this.rows.size; + count(): bigint { + return BigInt(this.rows.size); } /** * @returns The values of the rows in the table */ - iter(): RowType[] { - return Array.from(this.rows.values()).map(([row]) => row); + iter(): IterableIterator> { + function* generator(rows: Map, number]>): IterableIterator> { + for (const [row] of rows.values()) { + yield row; + } + } + return generator(this.rows); + } + + /** + * Allows iteration over the rows in the table + * @returns An iterator over the rows in the table + */ + [Symbol.iterator](): IterableIterator> { + return this.iter(); } applyOperations = ( - operations: Operation[], - ctx: EventContextInterface + operations: Operation>[], + ctx: EventContextInterface ): PendingCallback[] => { const pendingCallbacks: PendingCallback[] = []; - if (this.tableTypeInfo.primaryKeyInfo !== undefined) { + // TODO: performance + const hasPrimaryKey = Object.values(this.tableDef.columns).some(col => col.columnMetadata.isPrimaryKey === true); + if (hasPrimaryKey) { const insertMap = new Map< ComparablePrimitive, - [Operation, number] + [Operation>, number] >(); const deleteMap = new Map< ComparablePrimitive, - [Operation, number] + [Operation>, number] >(); for (const op of operations) { if (op.type === 'insert') { @@ -136,9 +157,9 @@ export class TableCache< }; update = ( - ctx: EventContextInterface, + ctx: EventContextInterface, rowId: ComparablePrimitive, - newRow: RowType, + newRow: RowType, refCountDelta: number = 0 ): PendingCallback | undefined => { const existingEntry = this.rows.get(rowId); @@ -146,7 +167,7 @@ export class TableCache< // TODO: this should throw an error and kill the connection. stdbLogger( 'error', - `Updating a row that was not present in the cache. Table: ${this.tableTypeInfo.tableName}, RowId: ${rowId}` + `Updating a row that was not present in the cache. Table: ${this.tableDef.name}, RowId: ${rowId}` ); return undefined; } @@ -155,7 +176,7 @@ export class TableCache< if (previousCount + refCountDelta <= 0) { stdbLogger( 'error', - `Negative reference count for in table ${this.tableTypeInfo.tableName} row ${rowId} (${previousCount} + ${refCountDelta})` + `Negative reference count for in table ${this.tableDef.name} row ${rowId} (${previousCount} + ${refCountDelta})` ); return undefined; } @@ -164,11 +185,11 @@ export class TableCache< if (previousCount === 0) { stdbLogger( 'error', - `Updating a row id in table ${this.tableTypeInfo.tableName} which was not present in the cache (rowId: ${rowId})` + `Updating a row id in table ${this.tableDef.name} which was not present in the cache (rowId: ${rowId})` ); return { type: 'insert', - table: this.tableTypeInfo.tableName, + table: this.tableDef.name, cb: () => { this.emitter.emit('insert', ctx, newRow); }, @@ -176,7 +197,7 @@ export class TableCache< } return { type: 'update', - table: this.tableTypeInfo.tableName, + table: this.tableDef.name, cb: () => { this.emitter.emit('update', ctx, oldRow, newRow); }, @@ -184,8 +205,8 @@ export class TableCache< }; insert = ( - ctx: EventContextInterface, - operation: Operation, + ctx: EventContextInterface, + operation: Operation>, count: number = 1 ): PendingCallback | undefined => { const [_, previousCount] = this.rows.get(operation.rowId) || [ @@ -196,7 +217,7 @@ export class TableCache< if (previousCount === 0) { return { type: 'insert', - table: this.tableTypeInfo.tableName, + table: this.tableDef.name, cb: () => { this.emitter.emit('insert', ctx, operation.row); }, @@ -207,8 +228,8 @@ export class TableCache< }; delete = ( - ctx: EventContextInterface, - operation: Operation, + ctx: EventContextInterface, + operation: Operation>, count: number = 1 ): PendingCallback | undefined => { const [_, previousCount] = this.rows.get(operation.rowId) || [ @@ -226,7 +247,7 @@ export class TableCache< this.rows.delete(operation.rowId); return { type: 'delete', - table: this.tableTypeInfo.tableName, + table: this.tableDef.name, cb: () => { this.emitter.emit('delete', ctx, operation.row); }, @@ -240,7 +261,7 @@ export class TableCache< * Register a callback for when a row is newly inserted into the database. * * ```ts - * User.onInsert((user, reducerEvent) => { + * ctx.db.user.onInsert((reducerEvent, user) => { * if (reducerEvent) { * console.log("New user on reducer", reducerEvent, user); * } else { @@ -251,8 +272,8 @@ export class TableCache< * * @param cb Callback to be called when a new row is inserted */ - onInsert = ( - cb: (ctx: EventContext, row: RowType) => void + onInsert = ( + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.on('insert', cb); }; @@ -261,7 +282,7 @@ export class TableCache< * Register a callback for when a row is deleted from the database. * * ```ts - * User.onDelete((user, reducerEvent) => { + * ctx.db.user.onDelete((reducerEvent, user) => { * if (reducerEvent) { * console.log("Deleted user on reducer", reducerEvent, user); * } else { @@ -272,8 +293,8 @@ export class TableCache< * * @param cb Callback to be called when a new row is inserted */ - onDelete = ( - cb: (ctx: EventContext, row: RowType) => void + onDelete = ( + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.on('delete', cb); }; @@ -282,7 +303,7 @@ export class TableCache< * Register a callback for when a row is updated into the database. * * ```ts - * User.onInsert((user, reducerEvent) => { + * ctx.db.user.onInsert((reducerEvent, oldUser, user) => { * if (reducerEvent) { * console.log("Updated user on reducer", reducerEvent, user); * } else { @@ -293,8 +314,8 @@ export class TableCache< * * @param cb Callback to be called when a new row is inserted */ - onUpdate = ( - cb: (ctx: EventContext, oldRow: RowType, row: RowType) => void + onUpdate = ( + cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void ): void => { this.emitter.on('update', cb); }; @@ -304,8 +325,8 @@ export class TableCache< * * @param cb Callback to be removed */ - removeOnInsert = ( - cb: (ctx: EventContext, row: RowType) => void + removeOnInsert = ( + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.off('insert', cb); }; @@ -315,8 +336,8 @@ export class TableCache< * * @param cb Callback to be removed */ - removeOnDelete = ( - cb: (ctx: EventContext, row: RowType) => void + removeOnDelete = ( + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.off('delete', cb); }; @@ -326,8 +347,8 @@ export class TableCache< * * @param cb Callback to be removed */ - removeOnUpdate = ( - cb: (ctx: EventContext, oldRow: RowType, row: RowType) => void + removeOnUpdate = ( + cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void ): void => { this.emitter.off('update', cb); }; diff --git a/crates/bindings-typescript/src/sdk/table_handle.ts b/crates/bindings-typescript/src/sdk/table_handle.ts index a648be11325..757caae4e80 100644 --- a/crates/bindings-typescript/src/sdk/table_handle.ts +++ b/crates/bindings-typescript/src/sdk/table_handle.ts @@ -1,12 +1,72 @@ -export type TableHandle = { - /** Phantom table name */ - readonly tableName?: TableName; +import type { ReadonlyIndexes } from "../server/indexes"; +import type { UntypedSchemaDef } from "../server/schema"; +import type { ReadonlyTableMethods, RowType, TableIndexes, UntypedTableDef } from "../server/table"; +import type { Prettify } from "../server/type_util"; +import type { EventContextInterface } from "./event_context"; +import type { UntypedReducersDef } from "./reducers"; + +export type ClientTableMethods< + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, + TableDef extends UntypedTableDef, +> = { + /** + * Registers a callback to be invoked when a row is inserted into the table. + */ + onInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; + + /** + * Removes a previously registered insert event listener. + * @param cb The callback to remove from the insert event listeners. + */ + removeOnInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; + + /** + * Registers a callback to be invoked when a row is deleted from the table. + */ + onDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; + + /** + * Removes a previously registered delete event listener. + * @param cb The callback to remove from the delete event listeners. + */ + removeOnDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; }; -export type TableNamesFromDb = Db extends object - ? { - [K in keyof Db]: Db[K] extends TableHandle - ? TableName - : never; - }[keyof Db] - : never; +/** + * Table + * + * - Row: row shape + * - UCV: unique-constraint violation error type (never if none) + * - AIO: auto-increment overflow error type (never if none) + */ +export type ClientTable< + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, + TableDef extends UntypedTableDef +> = Prettify< + ClientTableCore & + ReadonlyIndexes> +>; + +/** + * Core methods of ClientTable, without the indexes mixed in. + * Includes only staticly known methods. + */ +export type ClientTableCore< + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, + TableDef extends UntypedTableDef, +> = + ReadonlyTableMethods & + ClientTableMethods; + +/** + * Client database view, mapping table names to their corresponding ClientTable handles. + */ +export type ClientDbView< + SchemaDef extends UntypedSchemaDef, + Reducers extends UntypedReducersDef, +> = { + readonly [Tbl in SchemaDef['tables'][number] as Tbl['name']]: ClientTable; +}; \ No newline at end of file diff --git a/crates/bindings-typescript/src/server/db_view.ts b/crates/bindings-typescript/src/server/db_view.ts new file mode 100644 index 00000000000..3ba53c0da55 --- /dev/null +++ b/crates/bindings-typescript/src/server/db_view.ts @@ -0,0 +1,25 @@ +import type { ClientTable } from "../sdk"; +import type { UntypedReducersDef } from "../sdk/reducers"; +import type { UntypedSchemaDef } from "./schema"; +import type { ReadonlyTable, Table } from "./table"; + +/** + * A type representing a read-only database view, mapping table names to their corresponding read-only Table handles. + */ +export type ReadonlyDbView = { + readonly [Tbl in SchemaDef['tables'][number] as Tbl['accessorName']]: ReadonlyTable; +}; + +/** + * A type representing a client-side database view, mapping table names to their corresponding client Table handles. + */ +export type ClientDbView = { + readonly [Tbl in SchemaDef['tables'][number] as Tbl['accessorName']]: ClientTable; +}; + +/** + * A type representing the database view, mapping table names to their corresponding Table handles. + */ +export type DbView = { + readonly [Tbl in SchemaDef['tables'][number] as Tbl['accessorName']]: Table; +}; \ No newline at end of file diff --git a/crates/bindings-typescript/src/server/index.ts b/crates/bindings-typescript/src/server/index.ts index c602d704184..6db590d7d07 100644 --- a/crates/bindings-typescript/src/server/index.ts +++ b/crates/bindings-typescript/src/server/index.ts @@ -1,9 +1,11 @@ export * from './type_builders'; export { schema, type InferSchema } from './schema'; export { table } from './table'; +export { reducers } from './reducers'; export * as errors from './errors'; export { SenderError } from './errors'; export { type Reducer, type ReducerCtx } from './reducers'; +export { type DbView } from './db_view'; import './polyfills'; // Ensure polyfills are loaded import './register_hooks'; // Ensure module hooks are registered diff --git a/crates/bindings-typescript/src/server/indexes.ts b/crates/bindings-typescript/src/server/indexes.ts index 7af552012f6..f85bd92cbe2 100644 --- a/crates/bindings-typescript/src/server/indexes.ts +++ b/crates/bindings-typescript/src/server/indexes.ts @@ -56,6 +56,37 @@ export type Index< ? UniqueIndex : RangedIndex; +/** + * A type representing a collection of read-only indexes defined on a table. + */ +export type ReadonlyIndexes< + TableDef extends UntypedTableDef, + I extends Record>, +> = { + [k in keyof I]: ReadonlyIndex; +}; + +/** + * A type representing a read-only database index, which can be either unique or ranged. + * This type only exposes read-only operations. + */ +export type ReadonlyIndex< + TableDef extends UntypedTableDef, + I extends UntypedIndex, +> = I['unique'] extends true + ? ReadonlyUniqueIndex + : ReadonlyRangedIndex; + +/** + * A type representing a read-only unique index on a database table. + */ +export type ReadonlyUniqueIndex< + TableDef extends UntypedTableDef, + I extends UntypedIndex, +> = { + find(colVal: IndexVal): RowType | null; +}; + /** * A type representing a unique index on a database table. * Unique indexes enforce that the indexed columns contain unique values. @@ -63,23 +94,31 @@ export type Index< export type UniqueIndex< TableDef extends UntypedTableDef, I extends UntypedIndex, -> = { - find(col_val: IndexVal): RowType | null; - delete(col_val: IndexVal): boolean; - update(col_val: RowType): RowType; +> = ReadonlyUniqueIndex & { + delete(colVal: IndexVal): boolean; + update(colVal: RowType): RowType; }; /** - * A type representing a ranged index on a database table. - * Ranged indexes allow for range queries on the indexed columns. + * A type representing a read-only ranged index on a database table. */ -export type RangedIndex< +export type ReadonlyRangedIndex< TableDef extends UntypedTableDef, I extends UntypedIndex, > = { filter( range: IndexScanRangeBounds ): IterableIterator>; +}; + +/** + * A type representing a ranged index on a database table. + * Ranged indexes allow for range queries on the indexed columns. + */ +export type RangedIndex< + TableDef extends UntypedTableDef, + I extends UntypedIndex, +> = ReadonlyRangedIndex & { delete(range: IndexScanRangeBounds): number; }; diff --git a/crates/bindings-typescript/src/server/reducers.ts b/crates/bindings-typescript/src/server/reducers.ts index 0735cc004c1..55233d5a5b1 100644 --- a/crates/bindings-typescript/src/server/reducers.ts +++ b/crates/bindings-typescript/src/server/reducers.ts @@ -4,12 +4,14 @@ import type RawReducerDefV9 from '../lib/autogen/raw_reducer_def_v_9_type'; import type { ConnectionId } from '../lib/connection_id'; import type { Identity } from '../lib/identity'; import type { Timestamp } from '../lib/timestamp'; +import type { UntypedReducersDef } from '../sdk/reducers'; +import type { DbView } from './db_view'; import { MODULE_DEF, type UntypedSchemaDef } from './schema'; -import type { Table } from './table'; import type { InferTypeOfRow, RowBuilder, RowObj, + StringBuilder, TypeBuilder, } from './type_builders'; @@ -58,13 +60,6 @@ export type Reducer< payload: ParamsAsObject ) => void | { tag: 'ok' } | { tag: 'err'; value: string }; -/** - * A type representing the database view, mapping table names to their corresponding Table handles. - */ -export type DbView = { - readonly [Tbl in SchemaDef['tables'][number] as Tbl['name']]: Table; -}; - /** * Authentication information for the caller of a reducer. */ @@ -133,8 +128,9 @@ export function pushReducer( fn: Reducer, lifecycle?: RawReducerDefV9['lifecycle'] ): void { - if (existingReducers.has(name)) + if (existingReducers.has(name)) { throw new TypeError(`There is already a reducer with the name '${name}'`); + } existingReducers.add(name); const paramType: ProductType = { @@ -263,3 +259,111 @@ export function clientDisconnected< >(name: string, params: Params, fn: Reducer): void { pushReducer(name, params, fn, Lifecycle.OnDisconnect); } + +/** + * Represents a handle to a database reducer, including its name and argument type. + */ +export type ReducerSchema< + ReducerName extends string, + Params extends ParamsObj, +> = { + /** + * The name of the reducer. + */ + readonly reducerName: ReducerName; + + /** + * The type of the parameters object expected by the reducer. + */ + readonly paramsType: Params; + + /** + * + */ + readonly paramsSpacetimeType: ProductType; + + /** + * The {@link RawReducerDefV9} of the configured reducer. + */ + readonly reducerDef: RawReducerDefV9; +}; + +class Reducers { + /** + * Phantom type to track the reducers definition + */ + reducersType!: ReducersDef; +} + +/** + * Helper type to convert an array of TableSchema into a schema definition + */ +type ReducersToSchema[]> = { + reducers: { + /** @type {UntypedReducerDef} */ + readonly [i in keyof T]: { + name: T[i]['reducerName']; + params: T[i]['paramsType']; + paramsSpacetimeType: T[i]['paramsSpacetimeType']; + }; + }; +}; + +/** + * Creates a schema from table definitions + * @param handles - Array of table handles created by table() function + * @returns ColumnBuilder representing the complete database schema + * @example + * ```ts + * const s = schema( + * table({ name: 'user' }, userType), + * table({ name: 'post' }, postType) + * ); + * ``` + */ +export function reducers[]>( + ...handles: H +): Reducers>; + +/** + * Creates a schema from table definitions (array overload) + * @param handles - Array of table handles created by table() function + * @returns ColumnBuilder representing the complete database schema + */ +export function reducers[]>( + handles: H +): Reducers>; + +export function reducers( + ...args: + | [readonly ReducerSchema[]] + | readonly ReducerSchema[] +): Reducers { + return new Reducers(); +} + +export function reducerSchema< + ReducerName extends string, + Params extends ParamsObj, +>( + name: ReducerName, + params: Params +): ReducerSchema { + const paramType: ProductType = { + elements: Object.entries(params).map(([n, c]) => ({ + name: n, + algebraicType: c.algebraicType, + })), + }; + return { + reducerName: name, + paramsType: params, + paramsSpacetimeType: paramType, + reducerDef: { + name, + params: paramType, + lifecycle: undefined, + }, + }; +} + diff --git a/crates/bindings-typescript/src/server/runtime.ts b/crates/bindings-typescript/src/server/runtime.ts index e9d1ecff775..74533661373 100644 --- a/crates/bindings-typescript/src/server/runtime.ts +++ b/crates/bindings-typescript/src/server/runtime.ts @@ -18,7 +18,6 @@ import { } from './indexes'; import { type RowType, type Table, type TableMethods } from './table'; import { - type DbView, type ReducerCtx, REDUCERS, type JwtClaims, @@ -29,9 +28,20 @@ import { MODULE_DEF } from './schema'; import * as _syscalls from 'spacetime:sys@1.0'; import type { u16, u32, ModuleHooks } from 'spacetime:sys@1.0'; +import type { DbView } from './db_view'; +import type { CamelCase } from './type_util'; const { freeze } = Object; +/** + * Type safe conversion from a string like "some_identifier-name" to "someIdentifierName". + * @param str The string to convert + * @returns The converted string + */ +export function toCamelCase(str: T): CamelCase { + return str.replace(/[-_]+(\w)/g, (_, c) => c.toUpperCase()) as CamelCase; +} + const sys: typeof _syscalls = freeze( Object.fromEntries( Object.entries(_syscalls).map(([name, syscall]) => [ @@ -218,12 +228,12 @@ function getDbView() { return DB_VIEW; } -function makeDbView(module_def: RawModuleDefV9): DbView { +function makeDbView(moduleDef: RawModuleDefV9): DbView { return freeze( Object.fromEntries( - module_def.tables.map(table => [ - table.name, - makeTableView(module_def.typespace, table), + moduleDef.tables.map(table => [ + toCamelCase(table.name), + makeTableView(moduleDef.typespace, table), ]) ) ); @@ -232,7 +242,9 @@ function makeDbView(module_def: RawModuleDefV9): DbView { function makeTableView(typespace: Typespace, table: RawTableDefV9): Table { const table_id = sys.table_id_from_name(table.name); const rowType = typespace.types[table.productTypeRef]; - if (rowType.tag !== 'Product') throw 'impossible'; + if (rowType.tag !== 'Product') { + throw 'impossible'; + } const baseSize = bsatnBaseSize(typespace, rowType); @@ -339,19 +351,19 @@ function makeTableView(typespace: Typespace, table: RawTableDefV9): Table { let index: Index; if (isUnique) { - const serializeBound = (col_val: any[]): IndexScanArgs => { - if (col_val.length !== numColumns) + const serializeBound = (colVal: any[]): IndexScanArgs => { + if (colVal.length !== numColumns) throw new TypeError('wrong number of elements'); const writer = new BinaryWriter(baseSize + 1); const prefix_elems = numColumns - 1; - serializePrefix(writer, col_val, prefix_elems); + serializePrefix(writer, colVal, prefix_elems); const rstartOffset = writer.offset; writer.writeU8(0); AlgebraicType.serializeValue( writer, indexType.value.elements[numColumns - 1].algebraicType, - col_val[numColumns - 1], + colVal[numColumns - 1], typespace ); const buffer = writer.getBuffer(); @@ -360,9 +372,9 @@ function makeTableView(typespace: Typespace, table: RawTableDefV9): Table { return [prefix, prefix_elems, rstart, rstart]; }; index = { - find: (col_val: IndexVal): RowType | null => { - if (numColumns === 1) col_val = [col_val]; - const args = serializeBound(col_val); + find: (colVal: IndexVal): RowType | null => { + if (numColumns === 1) colVal = [colVal]; + const args = serializeBound(colVal); const iter = new TableIterator( sys.datastore_index_scan_range_bsatn(index_id, ...args), rowType @@ -375,9 +387,9 @@ function makeTableView(typespace: Typespace, table: RawTableDefV9): Table { ); return value; }, - delete: (col_val: IndexVal): boolean => { - if (numColumns === 1) col_val = [col_val]; - const args = serializeBound(col_val); + delete: (colVal: IndexVal): boolean => { + if (numColumns === 1) colVal = [colVal]; + const args = serializeBound(colVal); const num = sys.datastore_delete_by_index_scan_range_bsatn( index_id, ...args diff --git a/crates/bindings-typescript/src/server/schema.ts b/crates/bindings-typescript/src/server/schema.ts index 6684091b788..a8b775121e7 100644 --- a/crates/bindings-typescript/src/server/schema.ts +++ b/crates/bindings-typescript/src/server/schema.ts @@ -22,6 +22,7 @@ import { type AlgebraicTypeVariants, } from '../lib/algebraic_type'; import type RawScopedTypeNameV9 from '../lib/autogen/raw_scoped_type_name_v_9_type'; +import type { CamelCase } from './type_util'; /** * The global module definition that gets populated by calls to `reducer()` and lifecycle hooks. @@ -86,7 +87,9 @@ type TablesToSchema[]> = { /** @type {UntypedTableDef} */ readonly [i in keyof T]: { name: T[i]['tableName']; + accessorName: CamelCase; columns: T[i]['rowType']['row']; + rowType: T[i]['rowSpacetimeType']; indexes: T[i]['idxs']; }; }; @@ -314,22 +317,6 @@ export function schema[]>( ...handles: H ): Schema>; -/** - * Creates a schema from table definitions - * @param handles - Array of table handles created by table() function - * @returns ColumnBuilder representing the complete database schema - * @example - * ```ts - * const s = schema( - * table({ name: 'user' }, userType), - * table({ name: 'post' }, postType) - * ); - * ``` - */ -export function schema[]>( - ...handles: H -): Schema>; - /** * Creates a schema from table definitions (array overload) * @param handles - Array of table handles created by table() function @@ -351,13 +338,12 @@ export function schema[]>( * ); * ``` */ -export function schema( +export function schema[]>( ...args: - | [readonly TableSchema[]] - | readonly TableSchema[] -): Schema { - const handles: readonly TableSchema[] = - args.length === 1 && Array.isArray(args[0]) ? args[0] : args; + | [H] + | H +): Schema> { + const handles = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as H; const tableDefs = handles.map(h => h.tableDef); diff --git a/crates/bindings-typescript/src/server/table.ts b/crates/bindings-typescript/src/server/table.ts index 8c78488433b..96803cc1646 100644 --- a/crates/bindings-typescript/src/server/table.ts +++ b/crates/bindings-typescript/src/server/table.ts @@ -1,11 +1,11 @@ -import { AlgebraicType } from '../lib/algebraic_type'; +import { AlgebraicType, ProductType } from '../lib/algebraic_type'; import type RawConstraintDefV9 from '../lib/autogen/raw_constraint_def_v_9_type'; import RawIndexAlgorithm from '../lib/autogen/raw_index_algorithm_type'; import type RawIndexDefV9 from '../lib/autogen/raw_index_def_v_9_type'; import type RawSequenceDefV9 from '../lib/autogen/raw_sequence_def_v_9_type'; import type RawTableDefV9 from '../lib/autogen/raw_table_def_v_9_type'; import type { AllUnique } from './constraints'; -import type { ColumnIndex, IndexColumns, Indexes, IndexOpts } from './indexes'; +import type { ColumnIndex, IndexColumns, Indexes, IndexOpts, ReadonlyIndexes } from './indexes'; import { MODULE_DEF, splitName } from './schema'; import { RowBuilder, @@ -53,7 +53,9 @@ type CoerceArray[]> = X; */ export type UntypedTableDef = { name: string; + accessorName: string; columns: Record>>; + rowType: ProductType; indexes: IndexOpts[]; }; @@ -108,17 +110,30 @@ export type Table = Prettify< TableMethods & Indexes> >; -/** - * A type representing the methods available on a table. - */ -export type TableMethods = { +export type ReadonlyTable = Prettify< + ReadonlyTableMethods & + ReadonlyIndexes> +>; + +export type ReadonlyTableMethods = { /** Returns the number of rows in the TX state. */ count(): bigint; - /** Iterate over all rows in the TX state. Rust Iterator → TS IterableIterator. */ + /** + * Insert and return the inserted row (auto-increment fields filled). + * + * May throw on error: + * * If there are any unique or primary key columns in this table, may throw {@link UniqueAlreadyExists}. + * * If there are any auto-incrementing columns in this table, may throw {@link AutoIncOverflow}. + * */ iter(): IterableIterator>; [Symbol.iterator](): IterableIterator>; +}; +/** + * A type representing the methods available on a table. + */ +export type TableMethods = ReadonlyTableMethods & { /** * Insert and return the inserted row (auto-increment fields filled). * @@ -148,9 +163,9 @@ export type TableSchema< readonly tableName: TableName; /** - * The {@link AlgebraicType} representing the structure of a row in the table. + * The {@link ProductType} representing the structure of a row in the table. */ - readonly rowSpacetimeType: AlgebraicType; + readonly rowSpacetimeType: ProductType; /** * The {@link RawTableDefV9} of the configured table @@ -319,11 +334,11 @@ export function table>( }); } - const productType = AlgebraicType.Product({ + const productType = { elements: row.resolveType().value.elements.map(elem => { return { name: elem.name, algebraicType: elem.algebraicType }; }), - }); + }; return { rowType: row as RowBuilder>, diff --git a/crates/bindings-typescript/src/server/type_util.ts b/crates/bindings-typescript/src/server/type_util.ts index 4febb2c11c8..ae9e978c7e9 100644 --- a/crates/bindings-typescript/src/server/type_util.ts +++ b/crates/bindings-typescript/src/server/type_util.ts @@ -32,3 +32,15 @@ export type Values = T[keyof T]; * A helper type to collapse a tuple into a single type if it has only one element. */ export type CollapseTuple = A extends [infer T] ? T : A; + +type CamelCaseImpl = + S extends `${infer Head}_${infer Tail}` ? `${Head}${Capitalize>}` : + S extends `${infer Head}-${infer Tail}` ? `${Head}${Capitalize>}` : + S; + +/** + * Convert "Some_identifier-name" -> "someIdentifierName" + * - No spaces; allowed separators: "_" and "-" + * - Normalizes the *first* character to lowercase (e.g. "User_Name" -> "userName") + */ +export type CamelCase = Uncapitalize>; \ No newline at end of file diff --git a/crates/bindings-typescript/test-app/src/App.tsx b/crates/bindings-typescript/test-app/src/App.tsx index b8ee5f08b50..894e49b9239 100644 --- a/crates/bindings-typescript/test-app/src/App.tsx +++ b/crates/bindings-typescript/test-app/src/App.tsx @@ -1,9 +1,11 @@ import { DbConnection, Player } from './module_bindings'; import { useEffect } from 'react'; import './App.css'; -import { useSpacetimeDB, useTable } from '../../src/react'; +import { useReducer, useSpacetimeDB, useTable } from '../../src/react'; function App() { + const x = useReducer; + const createPlayer = useReducer('createPlayer'); const connection = useSpacetimeDB(); const players = useTable('player', { onInsert: player => { diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 104b8f6c14c..908c9243607 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -27,7 +27,10 @@ import { type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + type ClientTable as __ClientTable, + type RemoteModule as __RemoteModule, + type SetReducerFlags as __SetReducerFlags, + DbConnectionConfig, } from '../../../src/index'; // Import and reexport all reducer arg types @@ -50,196 +53,312 @@ export { Point }; import { UnindexedPlayer } from './unindexed_player_type.ts'; export { UnindexedPlayer }; import { User } from './user_type.ts'; +import { schema } from '../../../src/server/schema.ts'; +import t from '../../../src/server/type_builders.ts'; +import { table } from '../../../src/server/table.ts'; +import { reducerSchema, reducers } from '../../../src/server/reducers.ts'; +import { RemoteModule2 } from '../../../src/sdk/spacetime_module.ts'; export { User }; +const pointType = t.object('Point', { + x: t.number(), + y: t.number(), +}); + +const tablesSchema = schema( + table({ name: 'player', }, t.row({ + ownerId: t.string(), + name: t.string(), + location: pointType, + })), + table({ name: 'unindexed_player', }, t.row({ + ownerId: t.string(), + name: t.string(), + location: pointType, + })), + table({ name: 'user', primaryKey: 'identity', }, t.row({ + identity: t.string(), + name: t.string(), + })), +); + +const reducersSchema = reducers( + reducerSchema('create_player', { + name: t.string(), + location: pointType, + }), + reducerSchema('foo_bar', { + name: t.string(), + location: pointType, + }), +); + const REMOTE_MODULE = { - tables: { - player: { - tableName: 'player' as const, - rowType: Player.getTypeScriptAlgebraicType(), - primaryKey: 'ownerId', - primaryKeyInfo: { - colName: 'ownerId', - colType: ( - Player.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product - ).value.elements[0].algebraicType, - }, - }, - unindexed_player: { - tableName: 'unindexed_player' as const, - rowType: UnindexedPlayer.getTypeScriptAlgebraicType(), - }, - user: { - tableName: 'user' as const, - rowType: User.getTypeScriptAlgebraicType(), - primaryKey: 'identity', - primaryKeyInfo: { - colName: 'identity', - colType: ( - User.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product - ).value.elements[0].algebraicType, - }, - }, - }, - reducers: { - create_player: { - reducerName: 'create_player', - argsType: CreatePlayer.getTypeScriptAlgebraicType(), - }, - }, versionInfo: { - cliVersion: '1.5.0', - }, - // Constructors which are used by the DbConnectionImpl to - // extract type information from the generated RemoteModule. - // - // NOTE: This is not strictly necessary for `eventContextConstructor` because - // all we do is build a TypeScript object which we could have done inside the - // SDK, but if in the future we wanted to create a class this would be - // necessary because classes have methods, so we'll keep it. - eventContextConstructor: ( - imp: __DbConnectionImpl, - event: __Event - ) => { - return { - ...(imp as DbConnection), - event, - }; - }, - dbViewConstructor: (imp: __DbConnectionImpl) => { - return new RemoteTables(imp); + cliVersion: '1.6.0' as const, }, - reducersConstructor: ( - imp: __DbConnectionImpl, - setReducerFlags: SetReducerFlags - ) => { - return new RemoteReducers(imp, setReducerFlags); - }, - setReducerFlagsConstructor: () => { - return new SetReducerFlags(); - }, -}; - -// A type representing all the possible variants of a reducer. -export type Reducer = never | { name: 'CreatePlayer'; args: CreatePlayer }; - -export class RemoteReducers { - constructor( - private connection: __DbConnectionImpl, - private setCallReducerFlags: SetReducerFlags - ) {} - - createPlayer(name: string, location: Point) { - const __args = { name, location }; - let __writer = new __BinaryWriter(1024); - CreatePlayer.serialize(__writer, __args); - let __argsBuffer = __writer.getBuffer(); - this.connection.callReducer( - 'create_player', - __argsBuffer, - this.setCallReducerFlags.createPlayerFlags - ); - } - - onCreatePlayer( - callback: (ctx: ReducerEventContext, name: string, location: Point) => void - ) { - this.connection.onReducer('create_player', callback); - } - - removeOnCreatePlayer( - callback: (ctx: ReducerEventContext, name: string, location: Point) => void - ) { - this.connection.offReducer('create_player', callback); - } -} + tables: tablesSchema.schemaType, + reducers: reducersSchema.reducersType, +} satisfies RemoteModule2< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType +>; -export class SetReducerFlags { - createPlayerFlags: __CallReducerFlags = 'FullUpdate'; - createPlayer(flags: __CallReducerFlags) { - this.createPlayerFlags = flags; - } -} +export type EventContext = __EventContextInterface< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType +>; -export class RemoteTables { - constructor(private connection: __DbConnectionImpl) {} - - get player(): PlayerTableHandle<'player'> { - // clientCache is a private property - return new PlayerTableHandle( - ( - this.connection as unknown as { clientCache: __ClientCache } - ).clientCache.getOrCreateTable(REMOTE_MODULE.tables.player) - ); - } - - get unindexedPlayer(): UnindexedPlayerTableHandle<'unindexed_player'> { - // clientCache is a private property - return new UnindexedPlayerTableHandle( - ( - this.connection as unknown as { clientCache: __ClientCache } - ).clientCache.getOrCreateTable( - REMOTE_MODULE.tables.unindexed_player - ) - ); - } - - get user(): UserTableHandle<'user'> { - // clientCache is a private property - return new UserTableHandle( - ( - this.connection as unknown as { clientCache: __ClientCache } - ).clientCache.getOrCreateTable(REMOTE_MODULE.tables.user) - ); - } -} +export type ReducerEventContext = __ReducerEventContextInterface< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType +>; + +export type SubscriptionEventContext = __SubscriptionEventContextInterface< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType +>; + +export type ErrorContext = __ErrorContextInterface< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType +>; export class SubscriptionBuilder extends __SubscriptionBuilderImpl< - RemoteTables, - RemoteReducers, - SetReducerFlags + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType > {} -export class DbConnection extends __DbConnectionImpl< - RemoteTables, - RemoteReducers, - SetReducerFlags -> { - static builder = (): __DbConnectionBuilder< - DbConnection, - ErrorContext, - SubscriptionEventContext - > => { - return new __DbConnectionBuilder< - DbConnection, - ErrorContext, - SubscriptionEventContext - >(REMOTE_MODULE, (imp: __DbConnectionImpl) => imp as DbConnection); +export class DbConnectionBuilder extends __DbConnectionBuilder< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType, + DbConnection +> {}; + +export class DbConnection extends __DbConnectionImpl { + static builder = (): DbConnectionBuilder => { + return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); }; subscriptionBuilder = (): SubscriptionBuilder => { return new SubscriptionBuilder(this); }; } -export type EventContext = __EventContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags, - Reducer ->; -export type ReducerEventContext = __ReducerEventContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags, - Reducer ->; -export type SubscriptionEventContext = __SubscriptionEventContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags ->; -export type ErrorContext = __ErrorContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags ->; + +// // --- factory returning a well-typed object --- +// function makeReducersObj( +// r: { reducersType: R }, +// call: (name: string, spacetime: R['reducers'][number]['paramsSpacetimeType'], params: R['reducers'][number]['params']) => void +// ): Readonly> { +// const obj: Record = {}; +// for (const red of Object.values(r.reducersType.reducers)) { +// const key = toCamelCase(red.name as string); +// obj[key] = (params: any) => call(red.name as string, red.paramsSpacetimeType, params); +// } +// return Object.freeze(obj) as Readonly>; +// } + +// const reducersObj: UntypedReducers = {} +// for (const reducer of Object.values(reducersSchema.reducersType.reducers)) { +// reducersObj[toCamelCase(reducer.name)] = function(...args: Args) { +// this.#connection.callReducerWithParams( +// reducersSchema.reducersType.reducers[0].name, +// reducersSchema.reducersType.reducers[0].paramsSpacetimeType, +// args, +// "FullUpdate", +// ); +// }; +// } +// Object.freeze(reducersObj); + +// export class RemoteReducers implements UntypedReducersDef { +// [key: string]: (...args: any[]) => void; + +// #connection: __DbConnectionImpl; +// #setCallReducerFlags: SetReducerFlags; + +// constructor( +// connection: __DbConnectionImpl, +// setCallReducerFlags: SetReducerFlags +// ) { +// this.#connection = connection; +// this.#setCallReducerFlags = setCallReducerFlags; +// } + +// createPlayer(name: string, location: Point) { +// this.#connection.callReducerWithParams( +// r.reducersType.reducers[0].name, +// r.reducersType.reducers[0].paramsSpacetimeType, +// [name, location], +// this.#setCallReducerFlags.createPlayerFlags, +// ); +// } + +// // onCreatePlayer( +// // callback: (ctx: ReducerEventContext, name: string, location: Point) => void +// // ) { +// // this.#connection.onReducer('create_player', callback); +// // } + +// // removeOnCreatePlayer( +// // callback: (ctx: ReducerEventContext, name: string, location: Point) => void +// // ) { +// // this.#connection.offReducer('create_player', callback); +// // } +// } + +// export class SetReducerFlags implements __SetReducerFlags { +// createPlayerFlags: __CallReducerFlags = 'FullUpdate'; +// createPlayer(flags: __CallReducerFlags) { +// this.createPlayerFlags = flags; +// } +// } + +// export class RemoteTables implements ClientDbView { +// constructor(private connection: __DbConnectionImpl) {} + +// get player(): PlayerTableHandle { +// // clientCache is a private property +// return new PlayerTableHandle( +// ( +// this.connection as unknown as { clientCache: __ClientCache } +// ).clientCache.getOrCreateTable<"player">(spacetimedb.tablesDef.tables[0]) +// ); +// } + +// get unindexedPlayer(): UnindexedPlayerTableHandle<'unindexed_player'> { +// // clientCache is a private property +// return new UnindexedPlayerTableHandle( +// ( +// this.connection as unknown as { clientCache: __ClientCache } +// ).clientCache.getOrCreateTable<"unindexed_player">( +// REMOTE_MODULE.tables.unindexed_player +// ) +// ); +// } + +// get user(): UserTableHandle<'user'> { +// // clientCache is a private property +// return new UserTableHandle( +// ( +// this.connection as unknown as { clientCache: __ClientCache } +// ).clientCache.getOrCreateTable<"user">(REMOTE_MODULE.tables.user) +// ); +// } +// } + +// export class SubscriptionBuilder extends __SubscriptionBuilderImpl< +// SchemaDef, +// RemoteReducers +// > {} + +// export class DbConnection extends __DbConnectionImpl< +// SchemaDef, +// RemoteReducers, +// > { +// static builder = (): __DbConnectionBuilder< +// DbConnection, +// ErrorContext, +// SubscriptionEventContext +// > => { +// return new __DbConnectionBuilder< +// DbConnection, +// ErrorContext, +// SubscriptionEventContext +// >(REMOTE_MODULE, (imp: __DbConnectionImpl) => imp as DbConnection); +// }; +// subscriptionBuilder = (): SubscriptionBuilder => { +// return new SubscriptionBuilder(this); +// }; +// } + +// export type EventContext = __EventContextInterface< +// RemoteTables, +// RemoteReducers, +// SetReducerFlags, +// Reducer +// >; +// export type ReducerEventContext = __ReducerEventContextInterface< +// RemoteTables, +// RemoteReducers, +// SetReducerFlags, +// Reducer +// >; +// export type SubscriptionEventContext = __SubscriptionEventContextInterface< +// RemoteTables, +// RemoteReducers, +// SetReducerFlags +// >; +// export type ErrorContext = __ErrorContextInterface< +// RemoteTables, +// RemoteReducers, +// SetReducerFlags +// >; + +// const REMOTE_MODULE = { +// tables: { +// player: { +// name: 'player' as const, +// rowType: Player.getTypeScriptAlgebraicType(), +// primaryKey: 'ownerId', +// primaryKeyInfo: { +// colName: 'ownerId', +// colType: ( +// Player.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product +// ).value.elements[0].algebraicType, +// }, +// }, +// unindexed_player: { +// tableName: 'unindexed_player' as const, +// rowType: UnindexedPlayer.getTypeScriptAlgebraicType(), +// }, +// user: { +// tableName: 'user' as const, +// rowType: User.getTypeScriptAlgebraicType(), +// primaryKey: 'identity', +// primaryKeyInfo: { +// colName: 'identity', +// colType: ( +// User.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product +// ).value.elements[0].algebraicType, +// }, +// }, +// }, +// reducers: { +// create_player: { +// reducerName: 'create_player', +// argsType: CreatePlayer.getTypeScriptAlgebraicType(), +// }, +// }, +// versionInfo: { +// cliVersion: '1.5.0', +// }, +// // Constructors which are used by the DbConnectionImpl to +// // extract type information from the generated RemoteModule. +// // +// // NOTE: This is not strictly necessary for `eventContextConstructor` because +// // all we do is build a TypeScript object which we could have done inside the +// // SDK, but if in the future we wanted to create a class this would be +// // necessary because classes have methods, so we'll keep it. +// eventContextConstructor: ( +// imp: __DbConnectionImpl, +// event: __Event +// ) => { +// return { +// ...(imp as DbConnection), +// event, +// }; +// }, +// dbViewConstructor: (imp: __DbConnectionImpl) => { +// return new RemoteTables(imp); +// }, +// reducersConstructor: ( +// imp: __DbConnectionImpl, +// setReducerFlags: SetReducerFlags +// ) => { +// return new RemoteReducers(imp, setReducerFlags); +// }, +// setReducerFlagsConstructor: () => { +// return new SetReducerFlags(); +// }, +// } satisfies __RemoteModule; \ No newline at end of file diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts index 63a846fa7f6..53444fbc677 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts @@ -25,7 +25,7 @@ import { type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + type ClientTable as __ClientTable, } from '../../../src/index'; import { Player } from './player_type'; import { Point } from './point_type'; @@ -51,7 +51,7 @@ declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; * like `ctx.db.player.on_insert(...)`. */ export class PlayerTableHandle - implements __TableHandle + implements __ClientTable { // phantom type to track the table name readonly tableName!: TableName; @@ -80,11 +80,11 @@ export class PlayerTableHandle * Get a handle on the `ownerId` unique index on the table `player`. */ ownerId = { - // Find the subscribed row whose `ownerId` column value is equal to `col_val`, + // Find the subscribed row whose `ownerId` column value is equal to `colVal`, // if such a row is present in the client cache. - find: (col_val: string): Player | undefined => { + find: (colVal: string): Player | undefined => { for (let row of this.tableCache.iter()) { - if (__deepEqual(row.ownerId, col_val)) { + if (__deepEqual(row.ownerId, colVal)) { return row; } } diff --git a/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts index 21275ac19c1..e8364a92e8f 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts @@ -76,11 +76,11 @@ export class UserTableHandle * Get a handle on the `identity` unique index on the table `user`. */ identity = { - // Find the subscribed row whose `identity` column value is equal to `col_val`, + // Find the subscribed row whose `identity` column value is equal to `colVal`, // if such a row is present in the client cache. - find: (col_val: __Identity): User | undefined => { + find: (colVal: __Identity): User | undefined => { for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, col_val)) { + if (__deepEqual(row.identity, colVal)) { return row; } } diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts index 6d99cb4b7c6..9ffbf83d2c4 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts @@ -76,11 +76,11 @@ export class CounterTableHandle * Get a handle on the `id` unique index on the table `counter`. */ id = { - // Find the subscribed row whose `id` column value is equal to `col_val`, + // Find the subscribed row whose `id` column value is equal to `colVal`, // if such a row is present in the client cache. - find: (col_val: number): Counter | undefined => { + find: (colVal: number): Counter | undefined => { for (let row of this.tableCache.iter()) { - if (__deepEqual(row.id, col_val)) { + if (__deepEqual(row.id, colVal)) { return row; } } diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts index 47537ef2c6a..cb526e2dee8 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts @@ -76,11 +76,11 @@ export class OfflineUserTableHandle * Get a handle on the `identity` unique index on the table `offline_user`. */ identity = { - // Find the subscribed row whose `identity` column value is equal to `col_val`, + // Find the subscribed row whose `identity` column value is equal to `colVal`, // if such a row is present in the client cache. - find: (col_val: __Identity): User | undefined => { + find: (colVal: __Identity): User | undefined => { for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, col_val)) { + if (__deepEqual(row.identity, colVal)) { return row; } } diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts index 21275ac19c1..e8364a92e8f 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts @@ -76,11 +76,11 @@ export class UserTableHandle * Get a handle on the `identity` unique index on the table `user`. */ identity = { - // Find the subscribed row whose `identity` column value is equal to `col_val`, + // Find the subscribed row whose `identity` column value is equal to `colVal`, // if such a row is present in the client cache. - find: (col_val: __Identity): User | undefined => { + find: (colVal: __Identity): User | undefined => { for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, col_val)) { + if (__deepEqual(row.identity, colVal)) { return row; } } diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index e8c793be5b8..2770fc4d6ab 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -167,7 +167,7 @@ impl Lang for TypeScript { * but to directly chain method calls, * like `ctx.db.{accessor_method}.on_insert(...)`. */ -export class {table_handle} implements __TableHandle {{ +export class {table_handle} extends __TableCache {{ " ); out.indent(1); @@ -373,11 +373,11 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) // version of the SDK. // // Eventually we can remove this and only generate use the `primaryKeyInfo` field. - writeln!(out, "primaryKey: \"{}\",", pk.col_name.to_string().to_case(Case::Camel)); + writeln!(out, "primaryKey: \"{}\" as const,", pk.col_name.to_string().to_case(Case::Camel)); writeln!(out, "primaryKeyInfo: {{"); out.indent(1); - writeln!(out, "colName: \"{}\",", pk.col_name.to_string().to_case(Case::Camel)); + writeln!(out, "colName: \"{}\" as const,", pk.col_name.to_string().to_case(Case::Camel)); writeln!( out, "colType: ({row_type}.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product).value.elements[{}].algebraicType,", @@ -396,7 +396,7 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) for reducer in iter_reducers(module) { writeln!(out, "{}: {{", reducer.name); out.indent(1); - writeln!(out, "reducerName: \"{}\",", reducer.name); + writeln!(out, "reducerName: \"{}\" as const,", reducer.name); writeln!( out, "argsType: {args_type}.getTypeScriptAlgebraicType(),", @@ -409,7 +409,7 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) writeln!(out, "}},"); writeln!(out, "versionInfo: {{"); out.indent(1); - writeln!(out, "cliVersion: \"{}\",", spacetimedb_lib_version()); + writeln!(out, "cliVersion: \"{}\" as const,", spacetimedb_lib_version()); out.dedent(1); writeln!(out, "}},"); writeln!( @@ -438,7 +438,7 @@ setReducerFlagsConstructor: () => {{ }}" ); out.dedent(1); - writeln!(out, "}}"); + writeln!(out, "}} satisfies __RemoteModule;"); // Define `type Reducer` enum. writeln!(out); @@ -693,6 +693,7 @@ fn print_spacetimedb_imports(out: &mut Indenter) { "type CallReducerFlags as __CallReducerFlags", "type EventContextInterface as __EventContextInterface", "type ReducerEventContextInterface as __ReducerEventContextInterface", + "type RemoteModule as __RemoteModule", "type SubscriptionEventContextInterface as __SubscriptionEventContextInterface", "type ErrorContextInterface as __ErrorContextInterface", "SubscriptionBuilderImpl as __SubscriptionBuilderImpl", From e9c0a6fe35f20f5219852b5fdd3b51c605c1c9de Mon Sep 17 00:00:00 2001 From: = Date: Sun, 2 Nov 2025 21:34:37 -0500 Subject: [PATCH 02/23] RemoteModule-ify --- .../src/sdk/client_cache.ts | 39 ++++---- .../src/sdk/db_connection_builder.ts | 19 ++-- .../src/sdk/db_connection_impl.ts | 99 +++++++++---------- .../bindings-typescript/src/sdk/db_context.ts | 13 ++- .../src/sdk/event_context.ts | 29 +++--- .../src/sdk/spacetime_module.ts | 46 ++------- .../src/sdk/subscription_builder_impl.ts | 54 ++++------ .../src/sdk/table_cache.ts | 29 +++--- .../src/sdk/table_handle.ts | 29 +++--- .../bindings-typescript/src/server/db_view.ts | 6 +- .../test-app/src/module_bindings/index.ts | 30 +++--- .../tests/table_cache.test.ts | 1 - 12 files changed, 164 insertions(+), 230 deletions(-) diff --git a/crates/bindings-typescript/src/sdk/client_cache.ts b/crates/bindings-typescript/src/sdk/client_cache.ts index 40b8c98bc08..f410911c454 100644 --- a/crates/bindings-typescript/src/sdk/client_cache.ts +++ b/crates/bindings-typescript/src/sdk/client_cache.ts @@ -1,6 +1,6 @@ import type { UntypedSchemaDef } from '../server/schema.ts'; import type { UntypedTableDef } from '../server/table.ts'; -import type { UntypedReducersDef } from './reducers.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; import { TableCache } from './table_cache.ts'; type TableName = @@ -12,40 +12,39 @@ export type TableDefForTableName = : UntypedTableDef; type TableCacheForTableName< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, N -> = TableCache>; +> = TableCache>; /** * This is a helper class that provides a mapping from table names to their corresponding TableCache instances * while preserving the correspondence between the key and value type. */ -class TableMap { - private readonly map: Map>> = new Map(); +class TableMap { + private readonly map: Map>> = new Map(); - get>(key: K): TableCacheForTableName | undefined { + get>(key: K): TableCacheForTableName | undefined { // Cast required: a Map can't refine the union to the exact K-specific member on get(key: K). - return this.map.get(key) as TableCacheForTableName | undefined; + return this.map.get(key) as TableCacheForTableName | undefined; } - set>(key: K, value: TableCacheForTableName): this { + set>(key: K, value: TableCacheForTableName): this { this.map.set(key, value); return this; } - has(key: TableName): boolean { + has(key: TableName): boolean { return this.map.has(key); } - delete(key: TableName): boolean { + delete(key: TableName): boolean { return this.map.delete(key); } // optional: iteration stays broadly typed (cannot express per-key relation here) keys(): IterableIterator { return this.map.keys(); } - values(): IterableIterator>> { return this.map.values(); } - entries(): IterableIterator<[string, TableCacheForTableName>]> { return this.map.entries(); } + values(): IterableIterator>> { return this.map.values(); } + entries(): IterableIterator<[string, TableCacheForTableName>]> { return this.map.entries(); } [Symbol.iterator]() { return this.entries(); } } @@ -54,11 +53,11 @@ class TableMap { +export class ClientCache { /** * The tables in the database. */ - readonly tables = new TableMap(); + readonly tables = new TableMap(); /** * Returns the table with the given name. @@ -66,7 +65,7 @@ export class ClientCache>(name: N): TableCacheForTableName { + getTable>(name: N): TableCacheForTableName { const table = this.tables.get(name); if (!table) { console.error( @@ -83,9 +82,9 @@ export class ClientCache>( - tableDef: TableDefForTableName - ): TableCacheForTableName { + getOrCreateTable>( + tableDef: TableDefForTableName + ): TableCacheForTableName { const name = tableDef.name as N; let table = this.tables.get(name); @@ -93,7 +92,7 @@ export class ClientCache>(tableDef); + const newTable = new TableCache>(tableDef); this.tables.set(name, newTable); return newTable; } diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index b585cf35564..4eec420dd97 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -1,19 +1,16 @@ import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl'; import { EventEmitter } from './event_emitter'; import type { DbConnectionConfig, ErrorContextInterface, Identity, SubscriptionEventContextInterface } from '../'; -import { type RemoteModule, type RemoteModule2 } from './spacetime_module'; +import { type UntypedRemoteModule } from './spacetime_module'; import { ensureMinimumVersionOrThrow } from './version'; import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; -import type { UntypedSchemaDef } from '../server/schema'; -import type { UntypedReducersDef } from './reducers'; /** * The database client connection to a SpacetimeDB server. */ export class DbConnectionBuilder< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, - DbConnection extends DbConnectionImpl, + RemoteModule extends UntypedRemoteModule, + DbConnection extends DbConnectionImpl, > { #uri?: URL; #nameOrAddress?: string; @@ -34,8 +31,8 @@ export class DbConnectionBuilder< * @param dbConnectionConstructor The constructor to use to create a new `DbConnection`. */ constructor( - private remoteModule: RemoteModule2, - private dbConnectionCtor: (config: DbConnectionConfig) => DbConnection + private remoteModule: RemoteModule, + private dbConnectionCtor: (config: DbConnectionConfig) => DbConnection ) { this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn; } @@ -181,7 +178,7 @@ export class DbConnectionBuilder< * }); * ``` */ - onConnectError(callback: (ctx: ErrorContextInterface, error: Error) => void): this { + onConnectError(callback: (ctx: ErrorContextInterface, error: Error) => void): this { this.#emitter.on('connectError', callback); return this; } @@ -213,7 +210,7 @@ export class DbConnectionBuilder< * @throws {Error} Throws an error if called multiple times on the same `DbConnectionBuilder`. */ onDisconnect( - callback: (ctx: ErrorContextInterface, error?: Error | undefined) => void + callback: (ctx: ErrorContextInterface, error?: Error | undefined) => void ): this { this.#emitter.on('disconnect', callback); return this; @@ -233,7 +230,7 @@ export class DbConnectionBuilder< * DbConnection.builder().withUri(host).withModuleName(name_or_address).withToken(auth_token).build(); * ``` */ - build(): DbConnectionImpl { + build(): DbConnectionImpl { if (!this.#uri) { throw new Error('URI is required to connect to SpacetimeDB'); } diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 64c7b435554..fc270ac69ef 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -34,7 +34,7 @@ import type { UnsubscribeAppliedMessage, } from './message_types.ts'; import type { ReducerEvent } from './reducer_event.ts'; -import { type RemoteModule, type RemoteModule2 } from './spacetime_module.ts'; +import { type RemoteModule, type UntypedRemoteModule } from './spacetime_module.ts'; import { TableCache, type Operation, @@ -52,10 +52,8 @@ import { import { stdbLogger } from './logger.ts'; import { fromByteArray } from 'base64-js'; import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; -import type { UntypedSchemaDef } from '../server/schema.ts'; import type { DbView } from '../server/db_view.ts'; -import type { RowType, UntypedTableDef } from '../server/table.ts'; -import type { CamelCase } from '../server/type_util.ts'; +import type { UntypedTableDef } from '../server/table.ts'; import { toCamelCase } from '../server/runtime.ts'; export { DbConnectionBuilder, SubscriptionBuilderImpl, TableCache, type Event }; @@ -72,13 +70,13 @@ export type { export type ConnectionEvent = 'connect' | 'disconnect' | 'connectError'; export type CallReducerFlags = 'FullUpdate' | 'NoSuccessNotify'; -type ReducerEventCallback = ( - ctx: ReducerEventContextInterface, +type ReducerEventCallback = ( + ctx: ReducerEventContextInterface, ...args: ReducerArgs ) => void; -type SubscriptionEventCallback = ( - ctx: SubscriptionEventContextInterface, +type SubscriptionEventCallback = ( + ctx: SubscriptionEventContextInterface, ) => void; function callReducerFlagsToNumber(flags: CallReducerFlags): number { @@ -90,7 +88,7 @@ function callReducerFlagsToNumber(flags: CallReducerFlags): number { } } -export type DbConnectionConfig = { +export type DbConnectionConfig = { uri: URL; nameOrAddress: string; identity?: Identity; @@ -100,13 +98,12 @@ export type DbConnectionConfig; + remoteModule: RemoteModule; }; export class DbConnectionImpl< - SchemaDef extends UntypedSchemaDef, - ReducersDef extends UntypedReducersDef, -> implements DbContext { + RemoteModule extends UntypedRemoteModule, +> implements DbContext { /** * Whether or not the connection is active. */ @@ -126,20 +123,20 @@ export class DbConnectionImpl< * The accessor field to access the tables in the database and associated * callback functions. */ - db: DbView; + db: DbView; /** * The accessor field to access the reducers in the database and associated * callback functions. */ - reducers: ReducersView; + reducers: ReducersView; /** * The accessor field to access functions related to setting flags on * reducers regarding how the server should handle the reducer call and * the events that it sends back to the client. */ - setReducerFlags: SetReducerFlags; + setReducerFlags: SetReducerFlags; /** * The `ConnectionId` of the connection to to the database. @@ -149,19 +146,19 @@ export class DbConnectionImpl< // These fields are meant to be strictly private. #queryId = 0; #emitter: EventEmitter; - #reducerEmitter: EventEmitter> = + #reducerEmitter: EventEmitter> = new EventEmitter(); - #onApplied?: SubscriptionEventCallback; + #onApplied?: SubscriptionEventCallback; #messageQueue = Promise.resolve(); - #subscriptionManager = new SubscriptionManager(); - #remoteModule: RemoteModule2; + #subscriptionManager = new SubscriptionManager(); + #remoteModule: RemoteModule; #callReducerFlags = new Map(); // These fields are not part of the public API, but in a pinch you // could use JavaScript to access them by bypassing TypeScript's // private fields. // We use them in testing. - private clientCache: ClientCache; + private clientCache: ClientCache; private ws?: WebsocketDecompressAdapter | WebsocketTestAdapter; private wsPromise: Promise< WebsocketDecompressAdapter | WebsocketTestAdapter | undefined @@ -178,7 +175,7 @@ export class DbConnectionImpl< compression, lightMode, confirmedReads, - }: DbConnectionConfig) { + }: DbConnectionConfig) { stdbLogger('info', 'Connecting to SpacetimeDB WS...'); // We use .toString() here because some versions of React Native contain a bug where the URL constructor @@ -198,10 +195,10 @@ export class DbConnectionImpl< const connectionId = this.connectionId.toHexString(); url.searchParams.set('connection_id', connectionId); - this.clientCache = new ClientCache(); - this.db = this.#makeDbView(remoteModule.tables); - this.reducers = this.#makeReducers(remoteModule.reducers); - this.setReducerFlags = this.#makeSetReducerFlags(remoteModule.reducers); + this.clientCache = new ClientCache(); + this.db = this.#makeDbView(remoteModule); + this.reducers = this.#makeReducers(remoteModule); + this.setReducerFlags = this.#makeSetReducerFlags(remoteModule); this.wsPromise = createWSFn({ url, @@ -239,8 +236,8 @@ export class DbConnectionImpl< return queryId; }; - #makeDbView(def: SchemaDef): DbView { - const view = Object.create(null) as DbView; + #makeDbView(def: RemoteModule): DbView { + const view = Object.create(null) as DbView; for (const tbl of def.tables) { // DbView uses this name verbatim @@ -257,7 +254,7 @@ export class DbConnectionImpl< return view; } - #makeReducers(def: ReducersDef): ReducersView { + #makeReducers(def: RemoteModule): ReducersView { const out: Record = {}; for (const reducer of def.reducers) { @@ -274,11 +271,11 @@ export class DbConnectionImpl< }; } - return out as ReducersView; + return out as ReducersView; } - #makeSetReducerFlags(defs: ReducersDef): SetReducerFlags { - const out = Object.create(null) as SetReducerFlags; + #makeSetReducerFlags(defs: RemoteModule): SetReducerFlags { + const out = Object.create(null) as SetReducerFlags; for (const r of defs.reducers) { const key = toCamelCase(r.name); Object.defineProperty(out, key, { @@ -293,8 +290,8 @@ export class DbConnectionImpl< } #makeEventContext( - event: Event - ): EventContextInterface { + event: Event + ): EventContextInterface { // Bind methods to preserve `this` (#private fields safe) return { db: this.db, @@ -314,13 +311,13 @@ export class DbConnectionImpl< // Do not remove this function, or shoot yourself in the foot please. // It's not clear what would be a better way to do this at this exact // moment. - subscriptionBuilder = (): SubscriptionBuilderImpl => { + subscriptionBuilder = (): SubscriptionBuilderImpl => { return new SubscriptionBuilderImpl(this); }; registerSubscription( - handle: SubscriptionHandleImpl, - handleEmitter: EventEmitter>, + handle: SubscriptionHandleImpl, + handleEmitter: EventEmitter>, querySql: string[] ): number { const queryId = this.#getNextQueryId(); @@ -365,7 +362,7 @@ export class DbConnectionImpl< const rows: Operation[] = []; // TODO: performance - const table = this.#remoteModule.tables.tables.find(t => t.name === tableName); + const table = this.#remoteModule.tables.find(t => t.name === tableName); const rowType = table!.rowType; const columnsArray = Object.entries(table!.columns); const primaryKeyColumnEntry = columnsArray.find(col => col[1].columnMetadata.isPrimaryKey); @@ -593,14 +590,14 @@ export class DbConnectionImpl< #applyTableUpdates( tableUpdates: CacheTableUpdate[], - eventContext: EventContextInterface + eventContext: EventContextInterface ): PendingCallback[] { const pendingCallbacks: PendingCallback[] = []; for (const tableUpdate of tableUpdates) { // Get table information for the table being updated const tableName = tableUpdate.tableName; // TODO: performance - const tableDef = this.#remoteModule.tables.tables.find(t => t.name === tableName)!; + const tableDef = this.#remoteModule.tables.find(t => t.name === tableName)!; const table = this.clientCache.getOrCreateTable(tableDef); const newCallbacks = table.applyOperations( tableUpdate.operations, @@ -655,7 +652,7 @@ export class DbConnectionImpl< let reducerInfo = message.reducerInfo; let unknownTransaction = false; let reducerArgs: any | undefined; - const reducer = this.#remoteModule.reducers.reducers.find(t => t.name === reducerInfo!.reducerName)!; + const reducer = this.#remoteModule.reducers.find(t => t.name === reducerInfo!.reducerName)!; if (!reducerInfo) { unknownTransaction = true; } else { @@ -899,63 +896,63 @@ export class DbConnectionImpl< private on( eventName: ConnectionEvent, - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on(eventName, callback); } private off( eventName: ConnectionEvent, - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off(eventName, callback); } private onConnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on('connect', callback); } private onDisconnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on('disconnect', callback); } private onConnectError( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.on('connectError', callback); } removeOnConnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off('connect', callback); } removeOnDisconnect( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off('disconnect', callback); } removeOnConnectError( - callback: (ctx: DbConnectionImpl, ...args: any[]) => void + callback: (ctx: DbConnectionImpl, ...args: any[]) => void ): void { this.#emitter.off('connectError', callback); } // Note: This is required to be public because it needs to be // called from the `RemoteReducers` class. - onReducer(reducerName: string, callback: ReducerEventCallback): void { + onReducer(reducerName: string, callback: ReducerEventCallback): void { this.#reducerEmitter.on(reducerName, callback); } // Note: This is required to be public because it needs to be // called from the `RemoteReducers` class. - offReducer(reducerName: string, callback: ReducerEventCallback): void { + offReducer(reducerName: string, callback: ReducerEventCallback): void { this.#reducerEmitter.off(reducerName, callback); } } diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index 7d5d0da464c..cb7f76579a1 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,6 +1,6 @@ import type { DbView } from '../server/db_view'; -import type { UntypedSchemaDef } from '../server/schema'; import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers'; +import type { SchemaDef, UntypedRemoteModule } from './spacetime_module'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; /** @@ -10,10 +10,10 @@ import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; * @template ReducersDef - Type representing the reducers. * @template SetReducerFlags - Type representing the reducer flags collection. */ -export interface DbContext { - db: DbView; - reducers: ReducersView; - setReducerFlags: SetReducerFlags; +export interface DbContext { + db: DbView; + reducers: ReducersView; + setReducerFlags: SetReducerFlags; isActive: boolean; /** @@ -22,8 +22,7 @@ export interface DbContext; /** diff --git a/crates/bindings-typescript/src/sdk/event_context.ts b/crates/bindings-typescript/src/sdk/event_context.ts index 459cfbfeaf0..1a69b4c2a3c 100644 --- a/crates/bindings-typescript/src/sdk/event_context.ts +++ b/crates/bindings-typescript/src/sdk/event_context.ts @@ -1,39 +1,34 @@ -import type { UntypedSchemaDef } from '../server/schema.ts'; import type { DbContext } from './db_context'; import type { Event } from './event.ts'; import type { ReducerEvent } from './reducer_event.ts'; -import type { UntypedReducersDef } from './reducers.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; -export type UntypedEventContext = EventContextInterface; +export type UntypedEventContext = EventContextInterface; export interface EventContextInterface< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, -> extends DbContext { + RemoteModule extends UntypedRemoteModule, +> extends DbContext { /** Enum with variants for all possible events. */ - event: Event; + event: Event; } export interface ReducerEventContextInterface< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, -> extends DbContext { + RemoteModule extends UntypedRemoteModule, +> extends DbContext { /** Enum with variants for all possible events. */ - event: ReducerEvent; + event: ReducerEvent; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface SubscriptionEventContextInterface< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, -> extends DbContext { + RemoteModule extends UntypedRemoteModule, +> extends DbContext { /** No event is provided **/ } export interface ErrorContextInterface< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, -> extends DbContext { + RemoteModule extends UntypedRemoteModule, +> extends DbContext { /** Enum with variants for all possible events. */ event?: Error; } diff --git a/crates/bindings-typescript/src/sdk/spacetime_module.ts b/crates/bindings-typescript/src/sdk/spacetime_module.ts index 72ad623703e..f9326331198 100644 --- a/crates/bindings-typescript/src/sdk/spacetime_module.ts +++ b/crates/bindings-typescript/src/sdk/spacetime_module.ts @@ -1,44 +1,18 @@ -import type { AlgebraicType } from '../'; import type { UntypedSchemaDef } from '../server/schema'; -import type { DbConnectionImpl } from './db_connection_impl'; import type { UntypedReducersDef } from './reducers'; -export interface TableRuntimeTypeInfo { - tableName: string; - rowType: AlgebraicType; - primaryKey?: string; - primaryKeyInfo?: PrimaryKeyInfo; -} - -export interface PrimaryKeyInfo { - colName: string; - colType: AlgebraicType; -} - -export interface ReducerRuntimeTypeInfo { - reducerName: string; - argsType: AlgebraicType; -} - -export type RemoteModule2 = { +export type RemoteModule< + SchemaDef extends UntypedSchemaDef, + ReducersDef extends UntypedReducersDef, + CLI extends string = string +> = SchemaDef & ReducersDef & { versionInfo: { cliVersion: CLI; }; - tables: SchemaDef; - reducers: ReducersDef; } -export interface RemoteModule { - tables: { [name: string]: TableRuntimeTypeInfo }; - reducers: { [name: string]: ReducerRuntimeTypeInfo }; - eventContextConstructor: (imp: DbConnectionImpl, event: any) => any; - dbViewConstructor: (connection: DbConnectionImpl) => any; - reducersConstructor: ( - connection: DbConnectionImpl, - setReducerFlags: any - ) => any; - setReducerFlagsConstructor: () => any; - versionInfo?: { - cliVersion: string; - }; -} +export type UntypedRemoteModule = RemoteModule; + +export type SchemaDef = RemoteModule['tables']; + +export type ReducersDef = RemoteModule['reducers']; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts index a0856367b7b..842e055b791 100644 --- a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts +++ b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts @@ -1,24 +1,22 @@ -import type { UntypedSchemaDef } from '../server/schema'; import type { DbConnectionImpl } from './db_connection_impl'; import type { ErrorContextInterface, SubscriptionEventContextInterface, } from './event_context'; import { EventEmitter } from './event_emitter'; -import type { UntypedReducersDef } from './reducers'; +import type { UntypedRemoteModule } from './spacetime_module'; export class SubscriptionBuilderImpl< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, > { #onApplied?: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void = undefined; #onError?: ( - ctx: ErrorContextInterface + ctx: ErrorContextInterface ) => void = undefined; constructor( - private db: DbConnectionImpl + private db: DbConnectionImpl ) {} /** @@ -38,9 +36,9 @@ export class SubscriptionBuilderImpl< */ onApplied( cb: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void - ): SubscriptionBuilderImpl { + ): SubscriptionBuilderImpl { this.#onApplied = cb; return this; } @@ -66,8 +64,8 @@ export class SubscriptionBuilderImpl< * @returns The current `SubscriptionBuilder` instance. */ onError( - cb: (ctx: ErrorContextInterface) => void - ): SubscriptionBuilderImpl { + cb: (ctx: ErrorContextInterface) => void + ): SubscriptionBuilderImpl { this.#onError = cb; return this; } @@ -90,7 +88,7 @@ export class SubscriptionBuilderImpl< */ subscribe( query_sql: string | string[] - ): SubscriptionHandleImpl { + ): SubscriptionHandleImpl { const queries = Array.isArray(query_sql) ? query_sql : [query_sql]; if (queries.length === 0) { throw new Error('Subscriptions must have at least one query'); @@ -127,16 +125,15 @@ export class SubscriptionBuilderImpl< export type SubscribeEvent = 'applied' | 'error' | 'end'; -export class SubscriptionManager { +export class SubscriptionManager { subscriptions: Map< number, - { handle: SubscriptionHandleImpl; emitter: EventEmitter } + { handle: SubscriptionHandleImpl; emitter: EventEmitter } > = new Map(); } export class SubscriptionHandleImpl< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, > { #queryId: number; #unsubscribeCalled: boolean = false; @@ -146,23 +143,20 @@ export class SubscriptionHandleImpl< new EventEmitter(); constructor( - private db: DbConnectionImpl, + private db: DbConnectionImpl, querySql: string[], onApplied?: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void, onError?: ( - ctx: ErrorContextInterface, + ctx: ErrorContextInterface, error: Error ) => void ) { this.#emitter.on( 'applied', ( - ctx: SubscriptionEventContextInterface< - SchemaDef, - Reducers - > + ctx: SubscriptionEventContextInterface ) => { this.#activeState = true; if (onApplied) { @@ -173,7 +167,7 @@ export class SubscriptionHandleImpl< this.#emitter.on( 'error', ( - ctx: ErrorContextInterface, + ctx: ErrorContextInterface, error: Error ) => { this.#activeState = false; @@ -200,10 +194,7 @@ export class SubscriptionHandleImpl< this.#emitter.on( 'end', ( - _ctx: SubscriptionEventContextInterface< - SchemaDef, - Reducers - > + _ctx: SubscriptionEventContextInterface ) => { this.#endedState = true; this.#activeState = false; @@ -223,7 +214,7 @@ export class SubscriptionHandleImpl< */ unsubscribeThen( onEnd: ( - ctx: SubscriptionEventContextInterface + ctx: SubscriptionEventContextInterface ) => void ): void { if (this.#endedState) { @@ -237,10 +228,7 @@ export class SubscriptionHandleImpl< this.#emitter.on( 'end', ( - ctx: SubscriptionEventContextInterface< - SchemaDef, - Reducers - > + ctx: SubscriptionEventContextInterface ) => { this.#endedState = true; this.#activeState = false; diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index cd79debf72e..53521530beb 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -1,13 +1,11 @@ import { EventEmitter } from './event_emitter.ts'; -import type { TableRuntimeTypeInfo } from './spacetime_module.ts'; import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; import type { EventContextInterface, ClientTable } from './index.ts'; import type { RowType, Table, UntypedTableDef } from '../server/table.ts'; -import type { UntypedSchemaDef } from '../server/schema.ts'; -import type { UntypedReducersDef } from './reducers.ts'; import type { ClientTableCore } from './table_handle.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; export type Operation< RowType extends Record = Record, @@ -35,10 +33,9 @@ export type PendingCallback = { * Builder to generate calls to query a `table` in the database */ export class TableCache< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, TableDef extends UntypedTableDef, -> implements ClientTableCore { +> implements ClientTableCore { private rows: Map, number]>; private tableDef: TableDef; private emitter: EventEmitter<'insert' | 'delete' | 'update'>; @@ -84,7 +81,7 @@ export class TableCache< applyOperations = ( operations: Operation>[], - ctx: EventContextInterface + ctx: EventContextInterface ): PendingCallback[] => { const pendingCallbacks: PendingCallback[] = []; // TODO: performance @@ -157,7 +154,7 @@ export class TableCache< }; update = ( - ctx: EventContextInterface, + ctx: EventContextInterface, rowId: ComparablePrimitive, newRow: RowType, refCountDelta: number = 0 @@ -205,7 +202,7 @@ export class TableCache< }; insert = ( - ctx: EventContextInterface, + ctx: EventContextInterface, operation: Operation>, count: number = 1 ): PendingCallback | undefined => { @@ -228,7 +225,7 @@ export class TableCache< }; delete = ( - ctx: EventContextInterface, + ctx: EventContextInterface, operation: Operation>, count: number = 1 ): PendingCallback | undefined => { @@ -273,7 +270,7 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onInsert = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.on('insert', cb); }; @@ -294,7 +291,7 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onDelete = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.on('delete', cb); }; @@ -315,7 +312,7 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onUpdate = ( - cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void + cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void ): void => { this.emitter.on('update', cb); }; @@ -326,7 +323,7 @@ export class TableCache< * @param cb Callback to be removed */ removeOnInsert = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.off('insert', cb); }; @@ -337,7 +334,7 @@ export class TableCache< * @param cb Callback to be removed */ removeOnDelete = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType) => void ): void => { this.emitter.off('delete', cb); }; @@ -348,7 +345,7 @@ export class TableCache< * @param cb Callback to be removed */ removeOnUpdate = ( - cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void + cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void ): void => { this.emitter.off('update', cb); }; diff --git a/crates/bindings-typescript/src/sdk/table_handle.ts b/crates/bindings-typescript/src/sdk/table_handle.ts index 757caae4e80..9751d8edc96 100644 --- a/crates/bindings-typescript/src/sdk/table_handle.ts +++ b/crates/bindings-typescript/src/sdk/table_handle.ts @@ -1,36 +1,34 @@ import type { ReadonlyIndexes } from "../server/indexes"; -import type { UntypedSchemaDef } from "../server/schema"; import type { ReadonlyTableMethods, RowType, TableIndexes, UntypedTableDef } from "../server/table"; import type { Prettify } from "../server/type_util"; import type { EventContextInterface } from "./event_context"; -import type { UntypedReducersDef } from "./reducers"; +import type { UntypedRemoteModule } from "./spacetime_module"; export type ClientTableMethods< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, TableDef extends UntypedTableDef, > = { /** * Registers a callback to be invoked when a row is inserted into the table. */ - onInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; + onInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; /** * Removes a previously registered insert event listener. * @param cb The callback to remove from the insert event listeners. */ - removeOnInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; + removeOnInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; /** * Registers a callback to be invoked when a row is deleted from the table. */ - onDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; + onDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; /** * Removes a previously registered delete event listener. * @param cb The callback to remove from the delete event listeners. */ - removeOnDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; + removeOnDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; }; /** @@ -41,11 +39,10 @@ export type ClientTableMethods< * - AIO: auto-increment overflow error type (never if none) */ export type ClientTable< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, TableDef extends UntypedTableDef > = Prettify< - ClientTableCore & + ClientTableCore & ReadonlyIndexes> >; @@ -54,19 +51,17 @@ export type ClientTable< * Includes only staticly known methods. */ export type ClientTableCore< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, TableDef extends UntypedTableDef, > = ReadonlyTableMethods & - ClientTableMethods; + ClientTableMethods; /** * Client database view, mapping table names to their corresponding ClientTable handles. */ export type ClientDbView< - SchemaDef extends UntypedSchemaDef, - Reducers extends UntypedReducersDef, + RemoteModule extends UntypedRemoteModule, > = { - readonly [Tbl in SchemaDef['tables'][number] as Tbl['name']]: ClientTable; + readonly [Tbl in RemoteModule['tables'][number] as Tbl['name']]: ClientTable; }; \ No newline at end of file diff --git a/crates/bindings-typescript/src/server/db_view.ts b/crates/bindings-typescript/src/server/db_view.ts index 3ba53c0da55..ad8e540d192 100644 --- a/crates/bindings-typescript/src/server/db_view.ts +++ b/crates/bindings-typescript/src/server/db_view.ts @@ -1,5 +1,5 @@ import type { ClientTable } from "../sdk"; -import type { UntypedReducersDef } from "../sdk/reducers"; +import type { UntypedRemoteModule } from "../sdk/spacetime_module"; import type { UntypedSchemaDef } from "./schema"; import type { ReadonlyTable, Table } from "./table"; @@ -13,8 +13,8 @@ export type ReadonlyDbView = { /** * A type representing a client-side database view, mapping table names to their corresponding client Table handles. */ -export type ClientDbView = { - readonly [Tbl in SchemaDef['tables'][number] as Tbl['accessorName']]: ClientTable; +export type ClientDbView = { + readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable; }; /** diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 908c9243607..09683ccc7fa 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -57,7 +57,7 @@ import { schema } from '../../../src/server/schema.ts'; import t from '../../../src/server/type_builders.ts'; import { table } from '../../../src/server/table.ts'; import { reducerSchema, reducers } from '../../../src/server/reducers.ts'; -import { RemoteModule2 } from '../../../src/sdk/spacetime_module.ts'; +import { RemoteModule } from '../../../src/sdk/spacetime_module.ts'; export { User }; const pointType = t.object('Point', { @@ -97,47 +97,41 @@ const REMOTE_MODULE = { versionInfo: { cliVersion: '1.6.0' as const, }, - tables: tablesSchema.schemaType, - reducers: reducersSchema.reducersType, -} satisfies RemoteModule2< + tables: tablesSchema.schemaType.tables, + reducers: reducersSchema.reducersType.reducers, +} satisfies RemoteModule< typeof tablesSchema.schemaType, typeof reducersSchema.reducersType >; export type EventContext = __EventContextInterface< - typeof tablesSchema.schemaType, - typeof reducersSchema.reducersType + typeof REMOTE_MODULE >; export type ReducerEventContext = __ReducerEventContextInterface< - typeof tablesSchema.schemaType, - typeof reducersSchema.reducersType + typeof REMOTE_MODULE >; export type SubscriptionEventContext = __SubscriptionEventContextInterface< - typeof tablesSchema.schemaType, - typeof reducersSchema.reducersType + typeof REMOTE_MODULE >; export type ErrorContext = __ErrorContextInterface< - typeof tablesSchema.schemaType, - typeof reducersSchema.reducersType + typeof REMOTE_MODULE >; export class SubscriptionBuilder extends __SubscriptionBuilderImpl< - typeof tablesSchema.schemaType, - typeof reducersSchema.reducersType + typeof REMOTE_MODULE > {} export class DbConnectionBuilder extends __DbConnectionBuilder< - typeof tablesSchema.schemaType, - typeof reducersSchema.reducersType, + typeof REMOTE_MODULE, DbConnection > {}; -export class DbConnection extends __DbConnectionImpl { +export class DbConnection extends __DbConnectionImpl { static builder = (): DbConnectionBuilder => { - return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); + return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); }; subscriptionBuilder = (): SubscriptionBuilder => { return new SubscriptionBuilder(this); diff --git a/crates/bindings-typescript/tests/table_cache.test.ts b/crates/bindings-typescript/tests/table_cache.test.ts index eef948ba6be..f5961e9a4fd 100644 --- a/crates/bindings-typescript/tests/table_cache.test.ts +++ b/crates/bindings-typescript/tests/table_cache.test.ts @@ -1,5 +1,4 @@ import { type Operation, TableCache } from '../src/sdk/table_cache'; -import type { TableRuntimeTypeInfo } from '../src/sdk/spacetime_module'; import { describe, expect, test } from 'vitest'; import { Player } from '../test-app/src/module_bindings/player_type.ts'; From 541f493fb18da5c93c321b410785042f2cbfe463 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 2 Nov 2025 22:09:01 -0500 Subject: [PATCH 03/23] Smaller fixes --- .../src/react/SpacetimeDBProvider.ts | 25 ++++++++--------- .../src/react/connection_state.ts | 3 +- .../src/react/useReducer.ts | 28 ++++++++++--------- .../src/react/useSpacetimeDB.ts | 7 +++-- .../bindings-typescript/src/react/useTable.ts | 16 +++++------ .../src/sdk/db_connection_impl.ts | 10 +++---- .../bindings-typescript/src/sdk/db_context.ts | 6 ++-- 7 files changed, 49 insertions(+), 46 deletions(-) diff --git a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts index 6899bed3610..7ddd5b95981 100644 --- a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts @@ -8,26 +8,25 @@ import * as React from 'react'; import { SpacetimeDBContext } from './useSpacetimeDB'; import type { ConnectionState } from './connection_state'; import { ConnectionId } from '../lib/connection_id'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module'; export interface SpacetimeDBProviderProps< - DbConnection extends DbConnectionImpl, - ErrorContext extends ErrorContextInterface, - SubscriptionEventContext extends SubscriptionEventContextInterface, + RemoteModule extends UntypedRemoteModule, + DbConnection extends DbConnectionImpl, > { - connectionBuilder: DbConnectionBuilder; + connectionBuilder: DbConnectionBuilder; children?: React.ReactNode; } export function SpacetimeDBProvider< - DbConnection extends DbConnectionImpl, - ErrorContext extends ErrorContextInterface, - SubscriptionEventContext extends SubscriptionEventContextInterface, ->({ connectionBuilder, children }: SpacetimeDBProviderProps) { + RemoteModule extends UntypedRemoteModule, + DbConnection extends DbConnectionImpl, +>({ connectionBuilder, children }: SpacetimeDBProviderProps) { // Holds the imperative connection instance when (and only when) we’re on the client. const connRef = React.useRef(null); const getConnection = React.useCallback(() => connRef.current, []); - const [state, setState] = React.useState>({ + const [state, setState] = React.useState>({ isActive: false, identity: undefined, token: undefined, @@ -39,7 +38,7 @@ export function SpacetimeDBProvider< // Build on the client only; useEffect won't run during SSR. React.useEffect(() => { // Register callback for onConnect to update state - const onConnect = (conn: DbConnectionImpl) => { + const onConnect = (conn: DbConnectionImpl) => { setState(s => ({ ...s, isActive: conn.isActive, @@ -48,13 +47,13 @@ export function SpacetimeDBProvider< connectionId: conn.connectionId, })); }; - const onDisconnect = (ctx: ErrorContextInterface) => { + const onDisconnect = (ctx: ErrorContextInterface) => { setState(s => ({ ...s, isActive: ctx.isActive, })); }; - const onConnectError = (ctx: ErrorContextInterface, err: Error) => { + const onConnectError = (ctx: ErrorContextInterface, err: Error) => { setState(s => ({ ...s, isActive: ctx.isActive, @@ -76,7 +75,7 @@ export function SpacetimeDBProvider< // Lazily build once if (!connRef.current) { - connRef.current = connectionBuilder.build(); + connRef.current = connectionBuilder.build() as DbConnection; } return () => { diff --git a/crates/bindings-typescript/src/react/connection_state.ts b/crates/bindings-typescript/src/react/connection_state.ts index 4f45c6b902e..6d63e91d693 100644 --- a/crates/bindings-typescript/src/react/connection_state.ts +++ b/crates/bindings-typescript/src/react/connection_state.ts @@ -1,8 +1,9 @@ import type { ConnectionId } from "../lib/connection_id"; import type { Identity } from "../lib/identity"; import type { DbConnectionImpl } from "../sdk/db_connection_impl"; +import type { UntypedRemoteModule } from "../sdk/spacetime_module"; -export type ConnectionState = { +export type ConnectionState> = { isActive: boolean; identity?: Identity; token?: string; diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index dab84320ed2..b7684234b46 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -1,17 +1,19 @@ import type { DbConnectionImpl } from "../sdk/db_connection_impl"; import type { ReducerNamesFromReducers } from "../sdk/reducer_handle"; +import type { UntypedRemoteModule } from "../sdk/spacetime_module"; import { useSpacetimeDB } from "./useSpacetimeDB"; -// export function useReducer< -// DbConnection extends DbConnectionImpl, -// ReducerName extends ReducerNamesFromReducers = ReducerNamesFromReducers< -// DbConnection['reducers'] -// >, -// ReducerType = DbConnection['reducers'][ReducerName & keyof DbConnection['reducers']] -// >( -// reducerName: ReducerName, -// ): ReducerType { -// const connectionState = useSpacetimeDB(); -// const connection = connectionState.getConnection()!; -// return connection.reducers[reducerName as keyof typeof connection.reducers]; -// } \ No newline at end of file +export function useReducer< + RemoteModule extends UntypedRemoteModule, + DbConnection extends DbConnectionImpl, + ReducerName extends ReducerNamesFromReducers = ReducerNamesFromReducers< + DbConnection['reducers'] + >, + ReducerType = DbConnection['reducers'][ReducerName & keyof DbConnection['reducers']] +>( + reducerName: ReducerName, +): ReducerType { + const connectionState = useSpacetimeDB(); + const connection = connectionState.getConnection()!; + return connection.reducers[reducerName as keyof typeof connection.reducers]; +} \ No newline at end of file diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index 17f180b9878..c0421a2ffd2 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -1,13 +1,14 @@ import { createContext, useContext } from 'react'; import type { DbConnectionImpl } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -export const SpacetimeDBContext = createContext | undefined>(undefined); +export const SpacetimeDBContext = createContext> | undefined>(undefined); // Throws an error if used outside of a SpacetimeDBProvider // Error is caught by other hooks like useTable so they can provide better error messages -export function useSpacetimeDB(): ConnectionState { - const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; +export function useSpacetimeDB>(): ConnectionState { + const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; if (!context) { throw new Error( 'useSpacetimeDB must be used within a SpacetimeDBProvider component. Did you forget to add a `SpacetimeDBProvider` to your component tree?' diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index 5c81fdf2ed4..6a7f0f48782 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -9,6 +9,7 @@ import { useSpacetimeDB } from './useSpacetimeDB'; import { DbConnectionImpl, TableCache } from '../sdk/db_connection_impl'; import type { TableNamesFromDb } from '../sdk/table_handle'; import type { ConnectionState } from './connection_state'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module'; export interface UseQueryCallbacks { onInsert?: (row: RowType) => void; @@ -212,7 +213,7 @@ type ColumnsFromRow = { * ``` */ export function useTable< - DbConnection extends DbConnectionImpl, + DbConnection extends DbConnectionImpl, RowType extends Record, TableName extends TableNamesFromDb = TableNamesFromDb< DbConnection['db'] @@ -257,7 +258,7 @@ export function useTable< * ``` */ export function useTable< - DbConnection extends DbConnectionImpl, + DbConnection extends DbConnectionImpl, RowType extends Record, TableName extends TableNamesFromDb = TableNamesFromDb< DbConnection['db'] @@ -268,8 +269,7 @@ export function useTable< ): Snapshot; export function useTable< - DbConnection extends DbConnectionImpl, - RowType extends Record, + DbConnection extends DbConnectionImpl, TableName extends TableNamesFromDb = TableNamesFromDb< DbConnection['db'] >, @@ -320,10 +320,10 @@ export function useTable< } const table = connection.db[ tableName as keyof typeof connection.db - ] as unknown as TableCache; + ]; const result: readonly RowType[] = whereClause - ? table.iter().filter(row => evaluate(whereClause, row)) - : table.iter(); + ? Array.from(table.iter()).filter(row => evaluate(whereClause, row)) + : Array.from(table.iter()); return { rows: result, state: subscribeApplied ? 'ready' : 'loading', @@ -412,7 +412,7 @@ export function useTable< const table = connection.db[ tableName as keyof typeof connection.db - ] as unknown as TableCache; + ]; table.onInsert(onInsert); table.onDelete(onDelete); table.onUpdate?.(onUpdate); diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index fc270ac69ef..12bf41202f7 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -52,7 +52,7 @@ import { import { stdbLogger } from './logger.ts'; import { fromByteArray } from 'base64-js'; import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; -import type { DbView } from '../server/db_view.ts'; +import type { ClientDbView, DbView } from '../server/db_view.ts'; import type { UntypedTableDef } from '../server/table.ts'; import { toCamelCase } from '../server/runtime.ts'; @@ -123,7 +123,7 @@ export class DbConnectionImpl< * The accessor field to access the tables in the database and associated * callback functions. */ - db: DbView; + db: ClientDbView; /** * The accessor field to access the reducers in the database and associated @@ -236,11 +236,11 @@ export class DbConnectionImpl< return queryId; }; - #makeDbView(def: RemoteModule): DbView { - const view = Object.create(null) as DbView; + #makeDbView(def: RemoteModule): ClientDbView { + const view = Object.create(null) as ClientDbView; for (const tbl of def.tables) { - // DbView uses this name verbatim + // ClientDbView uses this name verbatim const key = tbl.accessorName; Object.defineProperty(view, key, { enumerable: true, diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index cb7f76579a1..2816b76b8d4 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,6 +1,6 @@ -import type { DbView } from '../server/db_view'; +import type { ClientDbView } from '../server/db_view'; import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers'; -import type { SchemaDef, UntypedRemoteModule } from './spacetime_module'; +import type { UntypedRemoteModule } from './spacetime_module'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; /** @@ -11,7 +11,7 @@ import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; * @template SetReducerFlags - Type representing the reducer flags collection. */ export interface DbContext { - db: DbView; + db: ClientDbView; reducers: ReducersView; setReducerFlags: SetReducerFlags; isActive: boolean; From 5da15d8dc4eb20998ce93b5fa25ba34bea9ce954 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 3 Nov 2025 09:33:56 -0500 Subject: [PATCH 04/23] Cleaning up where code lives --- .../src/{server => lib}/constraints.ts | 0 .../src/{server => lib}/indexes.ts | 2 +- .../src/{server => lib}/reducers.ts | 14 +- .../src/{server => lib}/schema.ts | 56 +++---- .../src/{server => lib}/table.ts | 12 +- .../{server => lib}/type_builders.test-d.ts | 0 .../src/{server => lib}/type_builders.ts | 4 +- .../src/{server => lib}/type_util.ts | 0 crates/bindings-typescript/src/lib/utils.ts | 10 ++ .../src/sdk/client_cache.ts | 4 +- .../sdk/{table_handle.ts => client_table.ts} | 6 +- .../src/sdk/db_connection_impl.ts | 7 +- .../bindings-typescript/src/sdk/db_context.ts | 2 +- crates/bindings-typescript/src/sdk/db_view.ts | 17 +-- .../bindings-typescript/src/sdk/reducers.ts | 4 +- .../src/sdk/spacetime_module.ts | 2 +- .../src/sdk/table_cache.ts | 5 +- .../bindings-typescript/src/server/db_view.ts | 13 +- .../bindings-typescript/src/server/index.ts | 10 +- .../bindings-typescript/src/server/runtime.ts | 19 +-- .../test-app/src/module_bindings/index.ts | 6 +- .../src/module_bindings/player_type.ts | 8 + .../bindings-typescript/tests/index.test.ts | 2 +- crates/codegen/src/typescript.rs | 141 ++++++++++++++++++ 24 files changed, 240 insertions(+), 104 deletions(-) rename crates/bindings-typescript/src/{server => lib}/constraints.ts (100%) rename crates/bindings-typescript/src/{server => lib}/indexes.ts (99%) rename crates/bindings-typescript/src/{server => lib}/reducers.ts (96%) rename crates/bindings-typescript/src/{server => lib}/schema.ts (97%) rename crates/bindings-typescript/src/{server => lib}/table.ts (95%) rename crates/bindings-typescript/src/{server => lib}/type_builders.test-d.ts (100%) rename crates/bindings-typescript/src/{server => lib}/type_builders.ts (99%) rename crates/bindings-typescript/src/{server => lib}/type_util.ts (100%) rename crates/bindings-typescript/src/sdk/{table_handle.ts => client_table.ts} (93%) diff --git a/crates/bindings-typescript/src/server/constraints.ts b/crates/bindings-typescript/src/lib/constraints.ts similarity index 100% rename from crates/bindings-typescript/src/server/constraints.ts rename to crates/bindings-typescript/src/lib/constraints.ts diff --git a/crates/bindings-typescript/src/server/indexes.ts b/crates/bindings-typescript/src/lib/indexes.ts similarity index 99% rename from crates/bindings-typescript/src/server/indexes.ts rename to crates/bindings-typescript/src/lib/indexes.ts index f85bd92cbe2..5c93c9aac9b 100644 --- a/crates/bindings-typescript/src/server/indexes.ts +++ b/crates/bindings-typescript/src/lib/indexes.ts @@ -1,7 +1,7 @@ import type { RowType, UntypedTableDef } from './table'; import type { ColumnMetadata, IndexTypes } from './type_builders'; import type { CollapseTuple, Prettify } from './type_util'; -import { Range } from './range'; +import { Range } from '../server/range'; import type { ColumnIsUnique } from './constraints'; /** diff --git a/crates/bindings-typescript/src/server/reducers.ts b/crates/bindings-typescript/src/lib/reducers.ts similarity index 96% rename from crates/bindings-typescript/src/server/reducers.ts rename to crates/bindings-typescript/src/lib/reducers.ts index 55233d5a5b1..e6c1b767a04 100644 --- a/crates/bindings-typescript/src/server/reducers.ts +++ b/crates/bindings-typescript/src/lib/reducers.ts @@ -1,11 +1,11 @@ -import type { ProductType } from '../lib/algebraic_type'; -import Lifecycle from '../lib/autogen/lifecycle_type'; -import type RawReducerDefV9 from '../lib/autogen/raw_reducer_def_v_9_type'; -import type { ConnectionId } from '../lib/connection_id'; -import type { Identity } from '../lib/identity'; -import type { Timestamp } from '../lib/timestamp'; +import type { ProductType } from './algebraic_type'; +import Lifecycle from './autogen/lifecycle_type'; +import type RawReducerDefV9 from './autogen/raw_reducer_def_v_9_type'; +import type { ConnectionId } from './connection_id'; +import type { Identity } from './identity'; +import type { Timestamp } from './timestamp'; import type { UntypedReducersDef } from '../sdk/reducers'; -import type { DbView } from './db_view'; +import type { DbView } from '../server/db_view'; import { MODULE_DEF, type UntypedSchemaDef } from './schema'; import type { InferTypeOfRow, diff --git a/crates/bindings-typescript/src/server/schema.ts b/crates/bindings-typescript/src/lib/schema.ts similarity index 97% rename from crates/bindings-typescript/src/server/schema.ts rename to crates/bindings-typescript/src/lib/schema.ts index a8b775121e7..1a33c11473d 100644 --- a/crates/bindings-typescript/src/server/schema.ts +++ b/crates/bindings-typescript/src/lib/schema.ts @@ -1,5 +1,5 @@ -import type RawTableDefV9 from '../lib/autogen/raw_table_def_v_9_type'; -import type Typespace from '../lib/autogen/typespace_type'; +import type RawTableDefV9 from './autogen/raw_table_def_v_9_type'; +import type Typespace from './autogen/typespace_type'; import { // eslint-disable-next-line @typescript-eslint/no-unused-vars type ColumnBuilder, @@ -16,14 +16,37 @@ import { type ParamsObj, type Reducer, } from './reducers'; -import type RawModuleDefV9 from '../lib/autogen/raw_module_def_v_9_type'; +import type RawModuleDefV9 from './autogen/raw_module_def_v_9_type'; import { AlgebraicType, type AlgebraicTypeVariants, -} from '../lib/algebraic_type'; -import type RawScopedTypeNameV9 from '../lib/autogen/raw_scoped_type_name_v_9_type'; +} from './algebraic_type'; +import type RawScopedTypeNameV9 from './autogen/raw_scoped_type_name_v_9_type'; import type { CamelCase } from './type_util'; +/** + * An untyped representation of the database schema. + */ +export type UntypedSchemaDef = { + tables: readonly UntypedTableDef[]; +}; + +/** + * Helper type to convert an array of TableSchema into a schema definition + */ +type TablesToSchema[]> = { + tables: { + /** @type {UntypedTableDef} */ + readonly [i in keyof T]: { + name: T[i]['tableName']; + accessorName: CamelCase; + columns: T[i]['rowType']['row']; + rowType: T[i]['rowSpacetimeType']; + indexes: T[i]['idxs']; + }; + }; +}; + /** * The global module definition that gets populated by calls to `reducer()` and lifecycle hooks. */ @@ -72,29 +95,6 @@ export function splitName(name: string): RawScopedTypeNameV9 { return { name: scope.pop()!, scope }; } -/** - * An untyped representation of the database schema. - */ -export type UntypedSchemaDef = { - tables: readonly UntypedTableDef[]; -}; - -/** - * Helper type to convert an array of TableSchema into a schema definition - */ -type TablesToSchema[]> = { - tables: { - /** @type {UntypedTableDef} */ - readonly [i in keyof T]: { - name: T[i]['tableName']; - accessorName: CamelCase; - columns: T[i]['rowType']['row']; - rowType: T[i]['rowSpacetimeType']; - indexes: T[i]['idxs']; - }; - }; -}; - /** * The Schema class represents the database schema for a SpacetimeDB application. * It encapsulates the table definitions and typespace, and provides methods to define diff --git a/crates/bindings-typescript/src/server/table.ts b/crates/bindings-typescript/src/lib/table.ts similarity index 95% rename from crates/bindings-typescript/src/server/table.ts rename to crates/bindings-typescript/src/lib/table.ts index 96803cc1646..a4d4d9b5254 100644 --- a/crates/bindings-typescript/src/server/table.ts +++ b/crates/bindings-typescript/src/lib/table.ts @@ -1,9 +1,9 @@ -import { AlgebraicType, ProductType } from '../lib/algebraic_type'; -import type RawConstraintDefV9 from '../lib/autogen/raw_constraint_def_v_9_type'; -import RawIndexAlgorithm from '../lib/autogen/raw_index_algorithm_type'; -import type RawIndexDefV9 from '../lib/autogen/raw_index_def_v_9_type'; -import type RawSequenceDefV9 from '../lib/autogen/raw_sequence_def_v_9_type'; -import type RawTableDefV9 from '../lib/autogen/raw_table_def_v_9_type'; +import { AlgebraicType, ProductType } from './algebraic_type'; +import type RawConstraintDefV9 from './autogen/raw_constraint_def_v_9_type'; +import RawIndexAlgorithm from './autogen/raw_index_algorithm_type'; +import type RawIndexDefV9 from './autogen/raw_index_def_v_9_type'; +import type RawSequenceDefV9 from './autogen/raw_sequence_def_v_9_type'; +import type RawTableDefV9 from './autogen/raw_table_def_v_9_type'; import type { AllUnique } from './constraints'; import type { ColumnIndex, IndexColumns, Indexes, IndexOpts, ReadonlyIndexes } from './indexes'; import { MODULE_DEF, splitName } from './schema'; diff --git a/crates/bindings-typescript/src/server/type_builders.test-d.ts b/crates/bindings-typescript/src/lib/type_builders.test-d.ts similarity index 100% rename from crates/bindings-typescript/src/server/type_builders.test-d.ts rename to crates/bindings-typescript/src/lib/type_builders.test-d.ts diff --git a/crates/bindings-typescript/src/server/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts similarity index 99% rename from crates/bindings-typescript/src/server/type_builders.ts rename to crates/bindings-typescript/src/lib/type_builders.ts index ab440838003..9ede6be0864 100644 --- a/crates/bindings-typescript/src/server/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -12,8 +12,8 @@ import { type TimeDurationAlgebraicType, type TimestampAlgebraicType, } from '..'; -import type { OptionAlgebraicType } from '../lib/option'; -import { addType, MODULE_DEF } from './schema'; +import type { OptionAlgebraicType } from './option'; +import { addType, MODULE_DEF } from '../server/schema'; import type { CoerceRow } from './table'; import { set, type Set } from './type_util'; diff --git a/crates/bindings-typescript/src/server/type_util.ts b/crates/bindings-typescript/src/lib/type_util.ts similarity index 100% rename from crates/bindings-typescript/src/server/type_util.ts rename to crates/bindings-typescript/src/lib/type_util.ts diff --git a/crates/bindings-typescript/src/lib/utils.ts b/crates/bindings-typescript/src/lib/utils.ts index 0b09319dc53..dfb42ff23e6 100644 --- a/crates/bindings-typescript/src/lib/utils.ts +++ b/crates/bindings-typescript/src/lib/utils.ts @@ -1,5 +1,6 @@ import BinaryReader from './binary_reader'; import BinaryWriter from './binary_writer'; +import type { CamelCase } from './type_util'; export function toPascalCase(s: string): string { const str = s.replace(/([-_][a-z])/gi, $1 => { @@ -98,3 +99,12 @@ export function u256ToUint8Array(data: bigint): Uint8Array { export function u256ToHexString(data: bigint): string { return uint8ArrayToHexString(u256ToUint8Array(data)); } + +/** + * Type safe conversion from a string like "some_identifier-name" to "someIdentifierName". + * @param str The string to convert + * @returns The converted string + */ +export function toCamelCase(str: T): CamelCase { + return str.replace(/[-_]+(\w)/g, (_, c) => c.toUpperCase()) as CamelCase; +} diff --git a/crates/bindings-typescript/src/sdk/client_cache.ts b/crates/bindings-typescript/src/sdk/client_cache.ts index f410911c454..125a43ee99c 100644 --- a/crates/bindings-typescript/src/sdk/client_cache.ts +++ b/crates/bindings-typescript/src/sdk/client_cache.ts @@ -1,5 +1,5 @@ -import type { UntypedSchemaDef } from '../server/schema.ts'; -import type { UntypedTableDef } from '../server/table.ts'; +import type { UntypedSchemaDef } from '../lib/schema.ts'; +import type { UntypedTableDef } from '../lib/table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; import { TableCache } from './table_cache.ts'; diff --git a/crates/bindings-typescript/src/sdk/table_handle.ts b/crates/bindings-typescript/src/sdk/client_table.ts similarity index 93% rename from crates/bindings-typescript/src/sdk/table_handle.ts rename to crates/bindings-typescript/src/sdk/client_table.ts index 9751d8edc96..ca19165f639 100644 --- a/crates/bindings-typescript/src/sdk/table_handle.ts +++ b/crates/bindings-typescript/src/sdk/client_table.ts @@ -1,6 +1,6 @@ -import type { ReadonlyIndexes } from "../server/indexes"; -import type { ReadonlyTableMethods, RowType, TableIndexes, UntypedTableDef } from "../server/table"; -import type { Prettify } from "../server/type_util"; +import type { ReadonlyIndexes } from "../lib/indexes"; +import type { ReadonlyTableMethods, RowType, TableIndexes, UntypedTableDef } from "../lib/table"; +import type { Prettify } from "../lib/type_util"; import type { EventContextInterface } from "./event_context"; import type { UntypedRemoteModule } from "./spacetime_module"; diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 12bf41202f7..d2854f4f92f 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -1,7 +1,6 @@ import { ConnectionId, ProductType } from '../'; import { AlgebraicType, - type AlgebraicTypeVariants, type ComparablePrimitive, } from '../'; import { parseValue } from '../'; @@ -52,9 +51,9 @@ import { import { stdbLogger } from './logger.ts'; import { fromByteArray } from 'base64-js'; import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; -import type { ClientDbView, DbView } from '../server/db_view.ts'; -import type { UntypedTableDef } from '../server/table.ts'; -import { toCamelCase } from '../server/runtime.ts'; +import type { ClientDbView } from './db_view.ts'; +import type { UntypedTableDef } from '../lib/table.ts'; +import { toCamelCase } from '../lib/utils.ts'; export { DbConnectionBuilder, SubscriptionBuilderImpl, TableCache, type Event }; diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index 2816b76b8d4..65c127d67d5 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,4 +1,4 @@ -import type { ClientDbView } from '../server/db_view'; +import type { ClientDbView } from './db_view'; import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers'; import type { UntypedRemoteModule } from './spacetime_module'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts index 664fcbe2483..2d12c64326e 100644 --- a/crates/bindings-typescript/src/sdk/db_view.ts +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -1,14 +1,9 @@ -import type { DbView } from "../server/db_view"; -import type { UntypedSchemaDef } from "../server/schema"; +import type { UntypedRemoteModule } from "./spacetime_module"; +import type { ClientTable } from "./table_handle"; /** - * An untyped database view, where the table names and row types are not known. - * Each key is a camelCased version of the table name, and each value is an untyped table handle. - * - * For example, a database with tables "user_profile" and "game_stats" would have the type: - * { - * userProfile: TableHandle<"user_profile", any>; - * gameStats: TableHandle<"game_stats", any>; - * } + * A type representing a client-side database view, mapping table names to their corresponding client Table handles. */ -export type UntypedDbView = DbView; \ No newline at end of file +export type ClientDbView = { + readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable; +}; diff --git a/crates/bindings-typescript/src/sdk/reducers.ts b/crates/bindings-typescript/src/sdk/reducers.ts index e25b805c97f..28a7b6804c8 100644 --- a/crates/bindings-typescript/src/sdk/reducers.ts +++ b/crates/bindings-typescript/src/sdk/reducers.ts @@ -1,6 +1,6 @@ import type { ProductType } from "../lib/algebraic_type"; -import type { ParamsObj } from "../server/reducers"; -import type { CamelCase } from "../server/type_util"; +import type { ParamsObj } from "../lib/reducers"; +import type { CamelCase } from "../lib/type_util"; import type { CallReducerFlags } from "./db_connection_impl"; export type ReducersView = { diff --git a/crates/bindings-typescript/src/sdk/spacetime_module.ts b/crates/bindings-typescript/src/sdk/spacetime_module.ts index f9326331198..73f7d5fdca9 100644 --- a/crates/bindings-typescript/src/sdk/spacetime_module.ts +++ b/crates/bindings-typescript/src/sdk/spacetime_module.ts @@ -1,4 +1,4 @@ -import type { UntypedSchemaDef } from '../server/schema'; +import type { UntypedSchemaDef } from '../lib/schema'; import type { UntypedReducersDef } from './reducers'; export type RemoteModule< diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index 53521530beb..c87afc5e895 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -3,8 +3,8 @@ import { EventEmitter } from './event_emitter.ts'; import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; import type { EventContextInterface, ClientTable } from './index.ts'; -import type { RowType, Table, UntypedTableDef } from '../server/table.ts'; -import type { ClientTableCore } from './table_handle.ts'; +import type { RowType, Table, UntypedTableDef } from '../lib/table.ts'; +import type { ClientTableCore } from './client_table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; export type Operation< @@ -29,6 +29,7 @@ export type PendingCallback = { table: string; cb: () => void; }; + /** * Builder to generate calls to query a `table` in the database */ diff --git a/crates/bindings-typescript/src/server/db_view.ts b/crates/bindings-typescript/src/server/db_view.ts index ad8e540d192..43a4a284ed3 100644 --- a/crates/bindings-typescript/src/server/db_view.ts +++ b/crates/bindings-typescript/src/server/db_view.ts @@ -1,7 +1,5 @@ -import type { ClientTable } from "../sdk"; -import type { UntypedRemoteModule } from "../sdk/spacetime_module"; -import type { UntypedSchemaDef } from "./schema"; -import type { ReadonlyTable, Table } from "./table"; +import type { UntypedSchemaDef } from "../lib/schema"; +import type { ReadonlyTable, Table } from "../lib/table"; /** * A type representing a read-only database view, mapping table names to their corresponding read-only Table handles. @@ -10,13 +8,6 @@ export type ReadonlyDbView = { readonly [Tbl in SchemaDef['tables'][number] as Tbl['accessorName']]: ReadonlyTable; }; -/** - * A type representing a client-side database view, mapping table names to their corresponding client Table handles. - */ -export type ClientDbView = { - readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable; -}; - /** * A type representing the database view, mapping table names to their corresponding Table handles. */ diff --git a/crates/bindings-typescript/src/server/index.ts b/crates/bindings-typescript/src/server/index.ts index 6db590d7d07..903bb80ec73 100644 --- a/crates/bindings-typescript/src/server/index.ts +++ b/crates/bindings-typescript/src/server/index.ts @@ -1,10 +1,10 @@ -export * from './type_builders'; -export { schema, type InferSchema } from './schema'; -export { table } from './table'; -export { reducers } from './reducers'; +export * from '../lib/type_builders'; +export { schema, type InferSchema } from '../lib/schema'; +export { table } from '../lib/table'; +export { reducers } from '../lib/reducers'; export * as errors from './errors'; export { SenderError } from './errors'; -export { type Reducer, type ReducerCtx } from './reducers'; +export { type Reducer, type ReducerCtx } from '../lib/reducers'; export { type DbView } from './db_view'; import './polyfills'; // Ensure polyfills are loaded diff --git a/crates/bindings-typescript/src/server/runtime.ts b/crates/bindings-typescript/src/server/runtime.ts index 74533661373..1366c763aa6 100644 --- a/crates/bindings-typescript/src/server/runtime.ts +++ b/crates/bindings-typescript/src/server/runtime.ts @@ -15,33 +15,24 @@ import { type IndexVal, type UniqueIndex, type RangedIndex, -} from './indexes'; -import { type RowType, type Table, type TableMethods } from './table'; +} from '../lib/indexes'; +import { type RowType, type Table, type TableMethods } from '../lib/table'; import { type ReducerCtx, REDUCERS, type JwtClaims, type AuthCtx, type JsonObject, -} from './reducers'; -import { MODULE_DEF } from './schema'; +} from '../lib/reducers'; +import { MODULE_DEF } from '../lib/schema'; import * as _syscalls from 'spacetime:sys@1.0'; import type { u16, u32, ModuleHooks } from 'spacetime:sys@1.0'; import type { DbView } from './db_view'; -import type { CamelCase } from './type_util'; +import { toCamelCase } from '../lib/utils'; const { freeze } = Object; -/** - * Type safe conversion from a string like "some_identifier-name" to "someIdentifierName". - * @param str The string to convert - * @returns The converted string - */ -export function toCamelCase(str: T): CamelCase { - return str.replace(/[-_]+(\w)/g, (_, c) => c.toUpperCase()) as CamelCase; -} - const sys: typeof _syscalls = freeze( Object.fromEntries( Object.entries(_syscalls).map(([name, syscall]) => [ diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 09683ccc7fa..5f44e5493cc 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -54,9 +54,9 @@ import { UnindexedPlayer } from './unindexed_player_type.ts'; export { UnindexedPlayer }; import { User } from './user_type.ts'; import { schema } from '../../../src/server/schema.ts'; -import t from '../../../src/server/type_builders.ts'; -import { table } from '../../../src/server/table.ts'; -import { reducerSchema, reducers } from '../../../src/server/reducers.ts'; +import t from '../../../src/lib/type_builders.ts'; +import { table } from '../../../src/lib/table.ts'; +import { reducerSchema, reducers } from '../../../src/lib/reducers.ts'; import { RemoteModule } from '../../../src/sdk/spacetime_module.ts'; export { User }; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts index 4bafe1fb1e4..72ace94abbd 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts @@ -27,10 +27,18 @@ import { type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, type TableHandle as __TableHandle, } from '../../../src/index'; +import { t } from '../../../src/server'; import { Point } from './point_type'; // Mark import as potentially unused declare type __keep_Point = Point; +t.object('Player', { + ownerId: t.string(), + name: t.string(), + location: Point.getTypeScriptAlgebraicType(), +}); + + export type Player = { ownerId: string; name: string; diff --git a/crates/bindings-typescript/tests/index.test.ts b/crates/bindings-typescript/tests/index.test.ts index 570728769fb..90be9efc2c0 100644 --- a/crates/bindings-typescript/tests/index.test.ts +++ b/crates/bindings-typescript/tests/index.test.ts @@ -6,7 +6,7 @@ import { type IdentityTokenMessage, } from '../src/index'; import type { ColumnBuilder } from '../src/server'; -import { t } from '../src/server/type_builders'; +import { t } from '../src/lib/type_builders'; describe('TypeBuilder', () => { it('builds the correct algebraic type for a point', () => { diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 2770fc4d6ab..b02e233f777 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -24,6 +24,147 @@ use spacetimedb_lib::version::spacetimedb_lib_version; type Imports = BTreeSet; +// Target output +// // 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 ../../../src/index cli version 1.5.0 (commit 5bfc84351742a6a8dc717b6c0011946f2d1b632d). + +// /* eslint-disable */ +// /* tslint:disable */ +// import { +// AlgebraicType as __AlgebraicTypeValue, +// BinaryReader as __BinaryReader, +// BinaryWriter as __BinaryWriter, +// ClientCache as __ClientCache, +// ConnectionId as __ConnectionId, +// DbConnectionBuilder as __DbConnectionBuilder, +// DbConnectionImpl as __DbConnectionImpl, +// Identity as __Identity, +// SubscriptionBuilderImpl as __SubscriptionBuilderImpl, +// TableCache as __TableCache, +// TimeDuration as __TimeDuration, +// Timestamp as __Timestamp, +// deepEqual as __deepEqual, +// type AlgebraicType as __AlgebraicTypeType, +// type AlgebraicTypeVariants as __AlgebraicTypeVariants, +// type CallReducerFlags as __CallReducerFlags, +// type ErrorContextInterface as __ErrorContextInterface, +// type Event as __Event, +// type EventContextInterface as __EventContextInterface, +// type ReducerEventContextInterface as __ReducerEventContextInterface, +// type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, +// type ClientTable as __ClientTable, +// type RemoteModule as __RemoteModule, +// type SetReducerFlags as __SetReducerFlags, +// DbConnectionConfig, +// } from '../../../src/index'; + +// // Import and reexport all reducer arg types +// import { CreatePlayer } from './create_player_reducer.ts'; +// export { CreatePlayer }; + +// // Import and reexport all table handle types +// import { PlayerTableHandle } from './player_table.ts'; +// export { PlayerTableHandle }; +// import { UnindexedPlayerTableHandle } from './unindexed_player_table.ts'; +// export { UnindexedPlayerTableHandle }; +// import { UserTableHandle } from './user_table.ts'; +// export { UserTableHandle }; + +// // Import and reexport all types +// import { Player } from './player_type.ts'; +// export { Player }; +// import { Point } from './point_type.ts'; +// export { Point }; +// import { UnindexedPlayer } from './unindexed_player_type.ts'; +// export { UnindexedPlayer }; +// import { User } from './user_type.ts'; +// import { schema } from '../../../src/server/schema.ts'; +// import t from '../../../src/server/type_builders.ts'; +// import { table } from '../../../src/server/table.ts'; +// import { reducerSchema, reducers } from '../../../src/server/reducers.ts'; +// import { RemoteModule } from '../../../src/sdk/spacetime_module.ts'; +// export { User }; + +// const pointType = t.object('Point', { +// x: t.number(), +// y: t.number(), +// }); + +// const tablesSchema = schema( +// table({ name: 'player', }, t.row({ +// ownerId: t.string(), +// name: t.string(), +// location: pointType, +// })), +// table({ name: 'unindexed_player', }, t.row({ +// ownerId: t.string(), +// name: t.string(), +// location: pointType, +// })), +// table({ name: 'user', primaryKey: 'identity', }, t.row({ +// identity: t.string(), +// name: t.string(), +// })), +// ); + +// const reducersSchema = reducers( +// reducerSchema('create_player', { +// name: t.string(), +// location: pointType, +// }), +// reducerSchema('foo_bar', { +// name: t.string(), +// location: pointType, +// }), +// ); + +// const REMOTE_MODULE = { +// versionInfo: { +// cliVersion: '1.6.0' as const, +// }, +// tables: tablesSchema.schemaType.tables, +// reducers: reducersSchema.reducersType.reducers, +// } satisfies RemoteModule< +// typeof tablesSchema.schemaType, +// typeof reducersSchema.reducersType +// >; + +// export type EventContext = __EventContextInterface< +// typeof REMOTE_MODULE +// >; + +// export type ReducerEventContext = __ReducerEventContextInterface< +// typeof REMOTE_MODULE +// >; + +// export type SubscriptionEventContext = __SubscriptionEventContextInterface< +// typeof REMOTE_MODULE +// >; + +// export type ErrorContext = __ErrorContextInterface< +// typeof REMOTE_MODULE +// >; + +// export class SubscriptionBuilder extends __SubscriptionBuilderImpl< +// typeof REMOTE_MODULE +// > {} + +// export class DbConnectionBuilder extends __DbConnectionBuilder< +// typeof REMOTE_MODULE, +// DbConnection +// > {}; + +// export class DbConnection extends __DbConnectionImpl { +// static builder = (): DbConnectionBuilder => { +// return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); +// }; +// subscriptionBuilder = (): SubscriptionBuilder => { +// return new SubscriptionBuilder(this); +// }; +// } + const INDENT: &str = " "; pub struct TypeScript; From 269ad2f69ebf577dee6bbfcb9e9a4ab4864ac121 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 3 Nov 2025 12:36:49 -0500 Subject: [PATCH 05/23] Small fixes --- .../bindings-typescript/src/react/useTable.ts | 2 +- .../src/sdk/db_connection_impl.ts | 8 ++--- crates/bindings-typescript/src/sdk/db_view.ts | 2 +- crates/bindings-typescript/src/sdk/index.ts | 2 +- .../src/sdk/message_types.ts | 34 +++++++++---------- .../test-app/src/module_bindings/index.ts | 2 +- 6 files changed, 23 insertions(+), 27 deletions(-) diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index 6a7f0f48782..311b3d68663 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -7,7 +7,7 @@ import { } from 'react'; import { useSpacetimeDB } from './useSpacetimeDB'; import { DbConnectionImpl, TableCache } from '../sdk/db_connection_impl'; -import type { TableNamesFromDb } from '../sdk/table_handle'; +import type { TableNamesFromDb } from '../sdk/client_table'; import type { ConnectionState } from './connection_state'; import type { UntypedRemoteModule } from '../sdk/spacetime_module'; diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index d2854f4f92f..14ea6d83606 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -535,9 +535,7 @@ export class DbConnectionImpl< const parsedTableUpdates = await parseDatabaseUpdate( message.value.update ); - const subscribeAppliedMessage: SubscribeAppliedMessage< - Record - > = { + const subscribeAppliedMessage: SubscribeAppliedMessage = { tag: 'SubscribeApplied', queryId: message.value.queryId.id, tableUpdates: parsedTableUpdates, @@ -549,9 +547,7 @@ export class DbConnectionImpl< const parsedTableUpdates = await parseDatabaseUpdate( message.value.update ); - const unsubscribeAppliedMessage: UnsubscribeAppliedMessage< - Record - > = { + const unsubscribeAppliedMessage: UnsubscribeAppliedMessage = { tag: 'UnsubscribeApplied', queryId: message.value.queryId.id, tableUpdates: parsedTableUpdates, diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts index 2d12c64326e..9d991765226 100644 --- a/crates/bindings-typescript/src/sdk/db_view.ts +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -1,5 +1,5 @@ import type { UntypedRemoteModule } from "./spacetime_module"; -import type { ClientTable } from "./table_handle"; +import type { ClientTable } from "./client_table"; /** * A type representing a client-side database view, mapping table names to their corresponding client Table handles. diff --git a/crates/bindings-typescript/src/sdk/index.ts b/crates/bindings-typescript/src/sdk/index.ts index 568e04bd5b5..0f35b9d06f9 100644 --- a/crates/bindings-typescript/src/sdk/index.ts +++ b/crates/bindings-typescript/src/sdk/index.ts @@ -2,6 +2,6 @@ export * from './db_connection_impl.ts'; export * from './client_cache.ts'; export * from './message_types.ts'; -export { type ClientTable } from './table_handle.ts'; +export { type ClientTable } from './client_table.ts'; export { type RemoteModule } from './spacetime_module.ts'; export { type SetReducerFlags } from './reducers.ts'; diff --git a/crates/bindings-typescript/src/sdk/message_types.ts b/crates/bindings-typescript/src/sdk/message_types.ts index ee7842019c0..5051dddeb82 100644 --- a/crates/bindings-typescript/src/sdk/message_types.ts +++ b/crates/bindings-typescript/src/sdk/message_types.ts @@ -3,15 +3,16 @@ import type { UpdateStatus } from './client_api/index.ts'; import { Identity } from '../'; import type { TableUpdate } from './table_cache.ts'; import { Timestamp } from '../'; +import type { RowType, Table, UntypedTableDef } from '../lib/table.ts'; -export type InitialSubscriptionMessage> = { +export type InitialSubscriptionMessage = { tag: 'InitialSubscription'; - tableUpdates: TableUpdate[]; + tableUpdates: TableUpdate[]; }; -export type TransactionUpdateMessage> = { +export type TransactionUpdateMessage = { tag: 'TransactionUpdate'; - tableUpdates: TableUpdate[]; + tableUpdates: TableUpdate[]; identity: Identity; connectionId: ConnectionId | null; reducerInfo?: { @@ -24,10 +25,10 @@ export type TransactionUpdateMessage> = { energyConsumed: bigint; }; -export type TransactionUpdateLightMessage> = +export type TransactionUpdateLightMessage = { tag: 'TransactionUpdateLight'; - tableUpdates: TableUpdate[]; + tableUpdates: TableUpdate[]; }; export type IdentityTokenMessage = { @@ -37,16 +38,16 @@ export type IdentityTokenMessage = { connectionId: ConnectionId; }; -export type SubscribeAppliedMessage> = { +export type SubscribeAppliedMessage = { tag: 'SubscribeApplied'; queryId: number; - tableUpdates: TableUpdate[]; + tableUpdates: TableUpdate[]; }; -export type UnsubscribeAppliedMessage> = { +export type UnsubscribeAppliedMessage = { tag: 'UnsubscribeApplied'; queryId: number; - tableUpdates: TableUpdate[]; + tableUpdates: TableUpdate[]; }; export type SubscriptionError = { @@ -55,12 +56,11 @@ export type SubscriptionError = { error: string; }; -export type Message = Record> = - - | InitialSubscriptionMessage - | TransactionUpdateMessage - | TransactionUpdateLightMessage +export type Message = + | InitialSubscriptionMessage + | TransactionUpdateMessage + | TransactionUpdateLightMessage | IdentityTokenMessage - | SubscribeAppliedMessage - | UnsubscribeAppliedMessage + | SubscribeAppliedMessage + | UnsubscribeAppliedMessage | SubscriptionError; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 5f44e5493cc..0768767c9d1 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -53,7 +53,7 @@ export { Point }; import { UnindexedPlayer } from './unindexed_player_type.ts'; export { UnindexedPlayer }; import { User } from './user_type.ts'; -import { schema } from '../../../src/server/schema.ts'; +import { schema } from '../../../src/lib/schema.ts'; import t from '../../../src/lib/type_builders.ts'; import { table } from '../../../src/lib/table.ts'; import { reducerSchema, reducers } from '../../../src/lib/reducers.ts'; From 051fb278b3d871c87536cb87c8905c21103728fe Mon Sep 17 00:00:00 2001 From: = Date: Mon, 3 Nov 2025 19:58:49 -0500 Subject: [PATCH 06/23] About to try new code gen --- .../src/lib/type_builders.ts | 2 +- crates/bindings-typescript/src/sdk/index.ts | 4 + .../test-app/src/module_bindings/index.ts | 13 + .../src/module_bindings/player_table.ts | 11 + .../src/module_bindings/player_type.ts | 5 + .../src/module_bindings/point_type.ts | 47 +- crates/codegen/src/typescript.rs | 1128 +++++------------ 7 files changed, 340 insertions(+), 870 deletions(-) diff --git a/crates/bindings-typescript/src/lib/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts index 9ede6be0864..771cce3b188 100644 --- a/crates/bindings-typescript/src/lib/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -13,7 +13,7 @@ import { type TimestampAlgebraicType, } from '..'; import type { OptionAlgebraicType } from './option'; -import { addType, MODULE_DEF } from '../server/schema'; +import { addType, MODULE_DEF } from '../lib/schema'; import type { CoerceRow } from './table'; import { set, type Set } from './type_util'; diff --git a/crates/bindings-typescript/src/sdk/index.ts b/crates/bindings-typescript/src/sdk/index.ts index 0f35b9d06f9..3a066053531 100644 --- a/crates/bindings-typescript/src/sdk/index.ts +++ b/crates/bindings-typescript/src/sdk/index.ts @@ -5,3 +5,7 @@ export * from './message_types.ts'; export { type ClientTable } from './client_table.ts'; export { type RemoteModule } from './spacetime_module.ts'; export { type SetReducerFlags } from './reducers.ts'; +export * from '../lib/type_builders.ts'; +export { schema } from '../lib/schema.ts'; +export { table } from '../lib/table.ts'; +export { reducerSchema, reducers } from '../lib/reducers.ts'; \ No newline at end of file diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 0768767c9d1..95c704eaad9 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -65,6 +65,19 @@ const pointType = t.object('Point', { y: t.number(), }); +table({ + name: 'player', + primaryKey: 'ownerId', + + indexes: [ + { name: 'this_is_an_index', algorithm: "btree", columns: [ "ownerId" ] } + ], +}, t.row({ + ownerId: t.string(), + name: t.string(), + location: pointType, +})) + const tablesSchema = schema( table({ name: 'player', }, t.row({ ownerId: t.string(), diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts index 53444fbc677..a966cde4283 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts @@ -26,6 +26,8 @@ import { type ReducerEventContextInterface as __ReducerEventContextInterface, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, type ClientTable as __ClientTable, + table, + t, } from '../../../src/index'; import { Player } from './player_type'; import { Point } from './point_type'; @@ -40,6 +42,15 @@ import { } from '.'; declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; + +export default table({ name: 'player' }, { + id: t.string().primaryKey().index(), + ownerId: t.string().unique(), + timestamp: t.timestamp(), + score: t.i32(), + location: Point, +}); + /** * Table handle for the table `player`. * diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts index 72ace94abbd..d6f04870fa6 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts @@ -38,6 +38,11 @@ t.object('Player', { location: Point.getTypeScriptAlgebraicType(), }); +const x = t.enum('PlayerEnum', { + foobar: t.f32(), + bazqux: t.string(), + quxfoo: t.bool(), +}); export type Player = { ownerId: string; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts index 9bd08a301a1..939de299c8b 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts @@ -25,47 +25,10 @@ import { type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + t as __t, } from '../../../src/index'; -export type Point = { - x: number; - y: number; -}; -let _cached_Point_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const Point = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_Point_type_value) return _cached_Point_type_value; - _cached_Point_type_value = __AlgebraicTypeValue.Product({ elements: [] }); - _cached_Point_type_value.value.elements.push( - { name: 'x', algebraicType: __AlgebraicTypeValue.U16 }, - { name: 'y', algebraicType: __AlgebraicTypeValue.U16 } - ); - return _cached_Point_type_value; - }, - - serialize(writer: __BinaryWriter, value: Point): void { - __AlgebraicTypeValue.serializeValue( - writer, - Point.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): Point { - return __AlgebraicTypeValue.deserializeValue( - reader, - Point.getTypeScriptAlgebraicType() - ); - }, -}; - -export default Point; +export default __t.object('Point', { + x: __t.number(), + y: __t.number(), +}); \ No newline at end of file diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index b02e233f777..dd3f0c8b871 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -1,8 +1,7 @@ use crate::util::{ - is_reducer_invokable, iter_reducers, iter_tables, iter_types, iter_unique_cols, - print_auto_generated_version_comment, + is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, iter_types, iter_unique_cols, print_auto_generated_version_comment }; -use crate::{indent_scope, OutputFile}; +use crate::{OutputFile}; use super::util::{collect_case, print_auto_generated_file_comment, type_ref_name}; @@ -13,7 +12,8 @@ use std::ops::Deref; use convert_case::{Case, Casing}; use spacetimedb_lib::sats::layout::PrimitiveType; use spacetimedb_lib::sats::AlgebraicTypeRef; -use spacetimedb_schema::def::{ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; +use spacetimedb_primitives::ColId; +use spacetimedb_schema::def::{BTreeAlgorithm, IndexAlgorithm, ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; use spacetimedb_schema::identifier::Identifier; use spacetimedb_schema::schema::{Schema, TableSchema}; use spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse, ProductTypeDef}; @@ -24,147 +24,6 @@ use spacetimedb_lib::version::spacetimedb_lib_version; type Imports = BTreeSet; -// Target output -// // 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 ../../../src/index cli version 1.5.0 (commit 5bfc84351742a6a8dc717b6c0011946f2d1b632d). - -// /* eslint-disable */ -// /* tslint:disable */ -// import { -// AlgebraicType as __AlgebraicTypeValue, -// BinaryReader as __BinaryReader, -// BinaryWriter as __BinaryWriter, -// ClientCache as __ClientCache, -// ConnectionId as __ConnectionId, -// DbConnectionBuilder as __DbConnectionBuilder, -// DbConnectionImpl as __DbConnectionImpl, -// Identity as __Identity, -// SubscriptionBuilderImpl as __SubscriptionBuilderImpl, -// TableCache as __TableCache, -// TimeDuration as __TimeDuration, -// Timestamp as __Timestamp, -// deepEqual as __deepEqual, -// type AlgebraicType as __AlgebraicTypeType, -// type AlgebraicTypeVariants as __AlgebraicTypeVariants, -// type CallReducerFlags as __CallReducerFlags, -// type ErrorContextInterface as __ErrorContextInterface, -// type Event as __Event, -// type EventContextInterface as __EventContextInterface, -// type ReducerEventContextInterface as __ReducerEventContextInterface, -// type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, -// type ClientTable as __ClientTable, -// type RemoteModule as __RemoteModule, -// type SetReducerFlags as __SetReducerFlags, -// DbConnectionConfig, -// } from '../../../src/index'; - -// // Import and reexport all reducer arg types -// import { CreatePlayer } from './create_player_reducer.ts'; -// export { CreatePlayer }; - -// // Import and reexport all table handle types -// import { PlayerTableHandle } from './player_table.ts'; -// export { PlayerTableHandle }; -// import { UnindexedPlayerTableHandle } from './unindexed_player_table.ts'; -// export { UnindexedPlayerTableHandle }; -// import { UserTableHandle } from './user_table.ts'; -// export { UserTableHandle }; - -// // Import and reexport all types -// import { Player } from './player_type.ts'; -// export { Player }; -// import { Point } from './point_type.ts'; -// export { Point }; -// import { UnindexedPlayer } from './unindexed_player_type.ts'; -// export { UnindexedPlayer }; -// import { User } from './user_type.ts'; -// import { schema } from '../../../src/server/schema.ts'; -// import t from '../../../src/server/type_builders.ts'; -// import { table } from '../../../src/server/table.ts'; -// import { reducerSchema, reducers } from '../../../src/server/reducers.ts'; -// import { RemoteModule } from '../../../src/sdk/spacetime_module.ts'; -// export { User }; - -// const pointType = t.object('Point', { -// x: t.number(), -// y: t.number(), -// }); - -// const tablesSchema = schema( -// table({ name: 'player', }, t.row({ -// ownerId: t.string(), -// name: t.string(), -// location: pointType, -// })), -// table({ name: 'unindexed_player', }, t.row({ -// ownerId: t.string(), -// name: t.string(), -// location: pointType, -// })), -// table({ name: 'user', primaryKey: 'identity', }, t.row({ -// identity: t.string(), -// name: t.string(), -// })), -// ); - -// const reducersSchema = reducers( -// reducerSchema('create_player', { -// name: t.string(), -// location: pointType, -// }), -// reducerSchema('foo_bar', { -// name: t.string(), -// location: pointType, -// }), -// ); - -// const REMOTE_MODULE = { -// versionInfo: { -// cliVersion: '1.6.0' as const, -// }, -// tables: tablesSchema.schemaType.tables, -// reducers: reducersSchema.reducersType.reducers, -// } satisfies RemoteModule< -// typeof tablesSchema.schemaType, -// typeof reducersSchema.reducersType -// >; - -// export type EventContext = __EventContextInterface< -// typeof REMOTE_MODULE -// >; - -// export type ReducerEventContext = __ReducerEventContextInterface< -// typeof REMOTE_MODULE -// >; - -// export type SubscriptionEventContext = __SubscriptionEventContextInterface< -// typeof REMOTE_MODULE -// >; - -// export type ErrorContext = __ErrorContextInterface< -// typeof REMOTE_MODULE -// >; - -// export class SubscriptionBuilder extends __SubscriptionBuilderImpl< -// typeof REMOTE_MODULE -// > {} - -// export class DbConnectionBuilder extends __DbConnectionBuilder< -// typeof REMOTE_MODULE, -// DbConnection -// > {}; - -// export class DbConnection extends __DbConnectionImpl { -// static builder = (): DbConnectionBuilder => { -// return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); -// }; -// subscriptionBuilder = (): SubscriptionBuilder => { -// return new SubscriptionBuilder(this); -// }; -// } - const INDENT: &str = " "; pub struct TypeScript; @@ -249,6 +108,21 @@ impl Lang for TypeScript { } } + + /// e.g. + /// ```ts + /// table({ + /// name: 'player', + /// indexes: [ + /// { name: 'this_is_an_index', algorithm: "btree", columns: [ "ownerId" ] } + /// ], + /// }, t.row({ + /// id: t.u32().primaryKey(), + /// ownerId: t.string(), + /// name: t.string().unique(), + /// location: pointType, + /// })) + /// ``` fn generate_table_file(&self, module: &ModuleDef, table: &TableDef) -> OutputFile { let schema = TableSchema::from_module_def(module, table, (), 0.into()) .validated() @@ -278,150 +152,67 @@ impl Lang for TypeScript { None, ); - writeln!( - out, - "import {{ type EventContext, type Reducer, RemoteReducers, RemoteTables }} from \".\";" - ); - - // Mark potentially unused types - writeln!( - out, - "declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables];" - ); - - let table_name = table.name.deref(); - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - let table_handle = table_name_pascalcase.clone() + "TableHandle"; - let accessor_method = table_method_name(&table.name); - writeln!(out); - write!( - out, - "/** - * Table handle for the table `{table_name}`. - * - * Obtain a handle from the [`{accessor_method}`] property on [`RemoteTables`], - * like `ctx.db.{accessor_method}`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.{accessor_method}.on_insert(...)`. - */ -export class {table_handle} extends __TableCache {{ -" - ); + writeln!(out, "export default table({{"); out.indent(1); - writeln!(out, "// phantom type to track the table name"); - writeln!(out, "readonly tableName!: TableName;"); - writeln!(out, "tableCache: __TableCache<{row_type}>;"); - writeln!(out); - writeln!(out, "constructor(tableCache: __TableCache<{row_type}>) {{"); - out.with_indent(|out| writeln!(out, "this.tableCache = tableCache;")); - writeln!(out, "}}"); - writeln!(out); - writeln!(out, "count(): number {{"); - out.with_indent(|out| { - writeln!(out, "return this.tableCache.count();"); - }); - writeln!(out, "}}"); - writeln!(out); - writeln!(out, "iter(): Iterable<{row_type}> {{"); - out.with_indent(|out| { - writeln!(out, "return this.tableCache.iter();"); - }); - writeln!(out, "}}"); - - for (unique_field_ident, unique_field_type_use) in - iter_unique_cols(module.typespace_for_generate(), &schema, product_def) - { - let unique_field_name = unique_field_ident.deref().to_case(Case::Camel); - let unique_field_name_pascalcase = unique_field_name.to_case(Case::Pascal); - - let unique_constraint = table_name_pascalcase.clone() + &unique_field_name_pascalcase + "Unique"; - let unique_field_type = type_name(module, unique_field_type_use); - - writeln!( - out, - "/** - * Access to the `{unique_field_name}` unique index on the table `{table_name}`, - * which allows point queries on the field of the same name - * via the [`{unique_constraint}.find`] method. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.{accessor_method}.{unique_field_name}().find(...)`. - * - * Get a handle on the `{unique_field_name}` unique index on the table `{table_name}`. - */" - ); - writeln!(out, "{unique_field_name} = {{"); - out.with_indent(|out| { - writeln!( - out, - "// Find the subscribed row whose `{unique_field_name}` column value is equal to `col_val`," - ); - writeln!(out, "// if such a row is present in the client cache."); - writeln!( - out, - "find: (col_val: {unique_field_type}): {row_type} | undefined => {{" - ); - out.with_indent(|out| { - writeln!(out, "for (let row of this.tableCache.iter()) {{"); - out.with_indent(|out| { - writeln!(out, "if (__deepEqual(row.{unique_field_name}, col_val)) {{"); - out.with_indent(|out| { - writeln!(out, "return row;"); - }); - writeln!(out, "}}"); - }); - writeln!(out, "}}"); - }); - writeln!(out, "}},"); - }); - writeln!(out, "}};"); - } - - writeln!(out); - - // TODO: expose non-unique indices. - writeln!( out, - "onInsert = (cb: (ctx: EventContext, row: {row_type}) => void) => {{ -{INDENT}return this.tableCache.onInsert(cb); -}} - -removeOnInsert = (cb: (ctx: EventContext, row: {row_type}) => void) => {{ -{INDENT}return this.tableCache.removeOnInsert(cb); -}} - -onDelete = (cb: (ctx: EventContext, row: {row_type}) => void) => {{ -{INDENT}return this.tableCache.onDelete(cb); -}} - -removeOnDelete = (cb: (ctx: EventContext, row: {row_type}) => void) => {{ -{INDENT}return this.tableCache.removeOnDelete(cb); -}}" + "name: '{}',", + table.name.deref() ); - - if schema.pk().is_some() { - write!( - out, - " -// Updates are only defined for tables with primary keys. -onUpdate = (cb: (ctx: EventContext, oldRow: {row_type}, newRow: {row_type}) => void) => {{ -{INDENT}return this.tableCache.onUpdate(cb); -}} - -removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) => void) => {{ -{INDENT}return this.tableCache.removeOnUpdate(cb); -}}" - ); + writeln!(out, "indexes: ["); + out.indent(1); + for index_def in iter_indexes(table) { + if !index_def.generated() { + // Skip system-defined indexes + continue; + } + match &index_def.algorithm { + IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => { + let get_name_and_type = |col_pos: ColId| { + let (field_name, field_type) = &product_def.elements[col_pos.idx()]; + let name_camel = field_name.deref().to_case(Case::Camel); + (name_camel, field_type) + }; + writeln!(out, "{{ name: '{}', algorithm: 'btree', columns: [", index_def.name); + out.indent(1); + for col_id in columns.iter() { + writeln!(out, "'{}',", get_name_and_type(col_id).0); + } + out.dedent(1); + writeln!(out, "] }},"); + } + IndexAlgorithm::Direct(_) => { + // Direct indexes are not implemented yet. + continue; + } + _ => todo!(), + }; } out.dedent(1); - - writeln!(out, "}}"); + writeln!(out, "}}, {{"); + out.indent(1); + for (field_ident, field_ty) in &product_def.elements { + let field_name = field_ident.deref().to_case(Case::Camel); + write!(out, "{field_name}: "); + write_type(module, out, field_ty, None, None).unwrap(); + + let mut annotations = Vec::new(); + if schema.pk().map(|pk| *field_ident == Identifier::new(pk.col_name.clone()).unwrap()).unwrap_or(false) { + annotations.push("primaryKey()"); + } else { + for (unique_field_ident, _) in + iter_unique_cols(module.typespace_for_generate(), &schema, product_def) + { + if field_ident == unique_field_ident { + annotations.push("unique()"); + } + } + } + } + writeln!(out, "}});"); + out.dedent(1); OutputFile { filename: table_module_name(&table.name) + ".ts", code: output.into_inner(), @@ -463,12 +254,84 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) out.newline(); + // const tablesSchema = schema( +// table({ name: 'player', }, t.row({ +// ownerId: t.string(), +// name: t.string(), +// location: pointType, +// })), +// table({ name: 'unindexed_player', }, t.row({ +// ownerId: t.string(), +// name: t.string(), +// location: pointType, +// })), +// table({ name: 'user', primaryKey: 'identity', }, t.row({ +// identity: t.string(), +// name: t.string(), +// })), +// ); + +// const reducersSchema = reducers( +// reducerSchema('create_player', { +// name: t.string(), +// location: pointType, +// }), +// reducerSchema('foo_bar', { +// name: t.string(), +// location: pointType, +// }), +// ); + +// const REMOTE_MODULE = { +// versionInfo: { +// cliVersion: '1.6.0' as const, +// }, +// tables: tablesSchema.schemaType.tables, +// reducers: reducersSchema.reducersType.reducers, +// } satisfies RemoteModule< +// typeof tablesSchema.schemaType, +// typeof reducersSchema.reducersType +// >; + +// export type EventContext = __EventContextInterface< +// typeof REMOTE_MODULE +// >; + +// export type ReducerEventContext = __ReducerEventContextInterface< +// typeof REMOTE_MODULE +// >; + +// export type SubscriptionEventContext = __SubscriptionEventContextInterface< +// typeof REMOTE_MODULE +// >; + +// export type ErrorContext = __ErrorContextInterface< +// typeof REMOTE_MODULE +// >; + +// export class SubscriptionBuilder extends __SubscriptionBuilderImpl< +// typeof REMOTE_MODULE +// > {} + +// export class DbConnectionBuilder extends __DbConnectionBuilder< +// typeof REMOTE_MODULE, +// DbConnection +// > {}; + +// export class DbConnection extends __DbConnectionImpl { +// static builder = (): DbConnectionBuilder => { +// return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); +// }; +// subscriptionBuilder = (): SubscriptionBuilder => { +// return new SubscriptionBuilder(this); +// }; + writeln!(out, "// Import and reexport all reducer arg types"); for reducer in iter_reducers(module) { let reducer_name = &reducer.name; let reducer_module_name = reducer_module_name(reducer_name) + ".ts"; let args_type = reducer_args_type_name(&reducer.name); - writeln!(out, "import {{ {args_type} }} from \"./{reducer_module_name}\";"); + writeln!(out, "import {args_type} from \"./{reducer_module_name}\";"); writeln!(out, "export {{ {args_type} }};"); } @@ -478,9 +341,8 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) let table_name = &table.name; let table_module_name = table_module_name(table_name) + ".ts"; let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - let table_handle = table_name_pascalcase.clone() + "TableHandle"; - writeln!(out, "import {{ {table_handle} }} from \"./{table_module_name}\";"); - writeln!(out, "export {{ {table_handle} }};"); + writeln!(out, "import {table_name_pascalcase} from \"./{table_module_name}\";"); + writeln!(out, "export {{ {table_name_pascalcase} }};"); } writeln!(out); @@ -488,331 +350,128 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) for ty in iter_types(module) { let type_name = collect_case(Case::Pascal, ty.name.name_segments()); let type_module_name = type_module_name(&ty.name) + ".ts"; - writeln!(out, "import {{ {type_name} }} from \"./{type_module_name}\";"); + writeln!(out, "import {type_name} from \"./{type_module_name}\";"); writeln!(out, "export {{ {type_name} }};"); } out.newline(); - - // Define SpacetimeModule - writeln!(out, "const REMOTE_MODULE = {{"); - out.indent(1); - writeln!(out, "tables: {{"); + + writeln!(out); + writeln!(out, "const tablesSchema = schema("); out.indent(1); for table in iter_tables(module) { - let type_ref = table.product_type_ref; - let row_type = type_ref_name(module, type_ref); - let schema = TableSchema::from_module_def(module, table, (), 0.into()) - .validated() - .expect("Failed to generate table due to validation errors"); - writeln!(out, "{}: {{", table.name); - out.indent(1); - writeln!(out, "tableName: \"{}\" as const,", table.name); - writeln!(out, "rowType: {row_type}.getTypeScriptAlgebraicType(),"); - if let Some(pk) = schema.pk() { - // This is left here so we can release the codegen change before releasing a new - // version of the SDK. - // - // Eventually we can remove this and only generate use the `primaryKeyInfo` field. - writeln!(out, "primaryKey: \"{}\" as const,", pk.col_name.to_string().to_case(Case::Camel)); - - writeln!(out, "primaryKeyInfo: {{"); - out.indent(1); - writeln!(out, "colName: \"{}\" as const,", pk.col_name.to_string().to_case(Case::Camel)); - writeln!( - out, - "colType: ({row_type}.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product).value.elements[{}].algebraicType,", - pk.col_pos.0 - ); - out.dedent(1); - writeln!(out, "}},"); - } - out.dedent(1); - writeln!(out, "}},"); + let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); + writeln!(out, "{},", table_name_pascalcase); } out.dedent(1); - writeln!(out, "}},"); - writeln!(out, "reducers: {{"); + writeln!(out, ");"); + + writeln!(out); + + writeln!(out, "const reducersSchema = reducers("); out.indent(1); for reducer in iter_reducers(module) { - writeln!(out, "{}: {{", reducer.name); - out.indent(1); - writeln!(out, "reducerName: \"{}\" as const,", reducer.name); - writeln!( - out, - "argsType: {args_type}.getTypeScriptAlgebraicType(),", - args_type = reducer_args_type_name(&reducer.name) - ); - out.dedent(1); - writeln!(out, "}},"); + if !is_reducer_invokable(reducer) { + // Skip system-defined reducers + continue; + } + let reducer_name = &reducer.name; + let args_type = reducer_args_type_name(&reducer.name); + writeln!(out, "reducerSchema(\"{}\", {}),", reducer_name, args_type); } out.dedent(1); - writeln!(out, "}},"); + writeln!(out, ");"); + + writeln!(out); + + writeln!(out, "const REMOTE_MODULE = {{"); + out.indent(1); writeln!(out, "versionInfo: {{"); out.indent(1); writeln!(out, "cliVersion: \"{}\" as const,", spacetimedb_lib_version()); out.dedent(1); writeln!(out, "}},"); - writeln!( - out, - "// Constructors which are used by the DbConnectionImpl to -// extract type information from the generated RemoteModule. -// -// NOTE: This is not strictly necessary for `eventContextConstructor` because -// all we do is build a TypeScript object which we could have done inside the -// SDK, but if in the future we wanted to create a class this would be -// necessary because classes have methods, so we'll keep it. -eventContextConstructor: (imp: __DbConnectionImpl, event: __Event) => {{ - return {{ - ...(imp as DbConnection), - event - }} -}}, -dbViewConstructor: (imp: __DbConnectionImpl) => {{ - return new RemoteTables(imp); -}}, -reducersConstructor: (imp: __DbConnectionImpl, setReducerFlags: SetReducerFlags) => {{ - return new RemoteReducers(imp, setReducerFlags); -}}, -setReducerFlagsConstructor: () => {{ - return new SetReducerFlags(); -}}" - ); + writeln!(out, "tables: tablesSchema.schemaType.tables,"); + writeln!(out, "reducers: reducersSchema.reducersType.reducers,"); + out.dedent(1); + writeln!(out, "}} satisfies __RemoteModule<"); + out.indent(1); + writeln!(out, "typeof tablesSchema.schemaType,"); + writeln!(out, "typeof reducersSchema.reducersType"); + out.dedent(1); + writeln!(out, ">;"); out.dedent(1); - writeln!(out, "}} satisfies __RemoteModule;"); - - // Define `type Reducer` enum. - writeln!(out); - print_reducer_enum_defn(module, out); - - out.newline(); - - print_remote_reducers(module, out); - - out.newline(); - - print_set_reducer_flags(module, out); - - out.newline(); - - print_remote_tables(module, out); - - out.newline(); - - print_subscription_builder(module, out); - - out.newline(); - - print_db_connection(module, out); out.newline(); + // Write type aliases for EventContext, ReducerEventContext, SubscriptionEventContext, ErrorContext writeln!( out, - "export type EventContext = __EventContextInterface;" + "export type EventContext = __EventContextInterface;" ); - writeln!( out, - "export type ReducerEventContext = __ReducerEventContextInterface;" + "export type ReducerEventContext = __ReducerEventContextInterface;" ); - writeln!( out, - "export type SubscriptionEventContext = __SubscriptionEventContextInterface;" + "export type SubscriptionEventContext = __SubscriptionEventContextInterface;" ); - writeln!( out, - "export type ErrorContext = __ErrorContextInterface;" + "export type ErrorContext = __ErrorContextInterface;" ); - vec![OutputFile { - filename: "index.ts".to_string(), - code: output.into_inner(), - }] - } -} + writeln!(out); -fn print_remote_reducers(module: &ModuleDef, out: &mut Indenter) { - writeln!(out, "export class RemoteReducers {{"); - out.indent(1); - writeln!( - out, - "constructor(private connection: __DbConnectionImpl, private setCallReducerFlags: SetReducerFlags) {{}}" - ); - out.newline(); + writeln!(out, "export class SubscriptionBuilder extends __SubscriptionBuilderImpl<"); + out.indent(1); + writeln!(out, "typeof REMOTE_MODULE"); + out.dedent(1); + writeln!(out, "> {{}}"); - for reducer in iter_reducers(module) { - // The reducer argument names and types as `ident: ty, ident: ty, ident: ty`, - // and the argument names as `ident, ident, ident` - // for passing to function call and struct literal expressions. - let mut arg_list = "".to_string(); - let mut arg_name_list = "".to_string(); - for (arg_ident, arg_ty) in &reducer.params_for_generate.elements[..] { - let arg_name = arg_ident.deref().to_case(Case::Camel); - arg_name_list += &arg_name; - arg_list += &arg_name; - arg_list += ": "; - write_type(module, &mut arg_list, arg_ty, None, None).unwrap(); - arg_list += ", "; - arg_name_list += ", "; - } - let arg_list = arg_list.trim_end_matches(", "); - let arg_name_list = arg_name_list.trim_end_matches(", "); - - let reducer_name = &reducer.name; - - if is_reducer_invokable(reducer) { - let reducer_function_name = reducer_function_name(reducer); - let reducer_variant = reducer_variant_name(&reducer.name); - if reducer.params_for_generate.elements.is_empty() { - writeln!(out, "{reducer_function_name}() {{"); - out.with_indent(|out| { - writeln!( - out, - "this.connection.callReducer(\"{reducer_name}\", new Uint8Array(0), this.setCallReducerFlags.{reducer_function_name}Flags);" - ); - }); - } else { - writeln!(out, "{reducer_function_name}({arg_list}) {{"); - out.with_indent(|out| { - writeln!(out, "const __args = {{ {arg_name_list} }};"); - writeln!(out, "let __writer = new __BinaryWriter(1024);"); - writeln!( - out, - "{reducer_variant}.serialize(__writer, __args);" - ); - writeln!(out, "let __argsBuffer = __writer.getBuffer();"); - writeln!(out, "this.connection.callReducer(\"{reducer_name}\", __argsBuffer, this.setCallReducerFlags.{reducer_function_name}Flags);"); - }); - } - writeln!(out, "}}"); - out.newline(); - } + writeln!(out); + writeln!(out, "export class DbConnectionBuilder extends __DbConnectionBuilder<"); + out.indent(1); + writeln!(out, "typeof REMOTE_MODULE,"); + writeln!(out, "DbConnection"); + out.dedent(1); + writeln!(out, "> {{}}"); - let arg_list_padded = if arg_list.is_empty() { - String::new() - } else { - format!(", {arg_list}") - }; - let reducer_name_pascal = reducer_name.deref().to_case(Case::Pascal); + writeln!(out); writeln!( out, - "on{reducer_name_pascal}(callback: (ctx: ReducerEventContext{arg_list_padded}) => void) {{" + "export class DbConnection extends __DbConnectionImpl {{" ); out.indent(1); - writeln!(out, "this.connection.onReducer(\"{reducer_name}\", callback);"); + writeln!( + out, + "static builder = (): DbConnectionBuilder => {{" + ); + out.indent(1); + writeln!( + out, + "return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config));" + ); out.dedent(1); - writeln!(out, "}}"); - out.newline(); + writeln!(out, "}};"); writeln!( out, - "removeOn{reducer_name_pascal}(callback: (ctx: ReducerEventContext{arg_list_padded}) => void) {{" + "subscriptionBuilder = (): SubscriptionBuilder => {{" ); out.indent(1); - writeln!(out, "this.connection.offReducer(\"{reducer_name}\", callback);"); + writeln!(out, "return new SubscriptionBuilder(this);"); + out.dedent(1); + writeln!(out, "}};"); out.dedent(1); writeln!(out, "}}"); out.newline(); - } - - out.dedent(1); - writeln!(out, "}}"); -} - -fn print_set_reducer_flags(module: &ModuleDef, out: &mut Indenter) { - writeln!(out, "export class SetReducerFlags {{"); - out.indent(1); - - for reducer in iter_reducers(module).filter(|r| is_reducer_invokable(r)) { - let reducer_function_name = reducer_function_name(reducer); - writeln!(out, "{reducer_function_name}Flags: __CallReducerFlags = 'FullUpdate';"); - writeln!(out, "{reducer_function_name}(flags: __CallReducerFlags) {{"); - out.with_indent(|out| { - writeln!(out, "this.{reducer_function_name}Flags = flags;"); - }); - writeln!(out, "}}"); - out.newline(); - } - - out.dedent(1); - writeln!(out, "}}"); -} - -fn print_remote_tables(module: &ModuleDef, out: &mut Indenter) { - writeln!(out, "export class RemoteTables {{"); - out.indent(1); - writeln!(out, "constructor(private connection: __DbConnectionImpl) {{}}"); - - for table in iter_tables(module) { - writeln!(out); - let table_name = table.name.deref(); - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - let table_name_camelcase = table.name.deref().to_case(Case::Camel); - let table_handle = table_name_pascalcase.clone() + "TableHandle"; - let type_ref = table.product_type_ref; - let row_type = type_ref_name(module, type_ref); - writeln!(out, "get {table_name_camelcase}(): {table_handle}<'{table_name}'> {{"); - out.with_indent(|out| { - writeln!(out, "// clientCache is a private property"); - writeln!( - out, - "return new {table_handle}((this.connection as unknown as {{ clientCache: __ClientCache }}).clientCache.getOrCreateTable<{row_type}>(REMOTE_MODULE.tables.{table_name}));" - ); - }); - writeln!(out, "}}"); - } - - out.dedent(1); - writeln!(out, "}}"); -} - -fn print_subscription_builder(_module: &ModuleDef, out: &mut Indenter) { - writeln!( - out, - "export class SubscriptionBuilder extends __SubscriptionBuilderImpl {{ }}" - ); -} - -fn print_db_connection(_module: &ModuleDef, out: &mut Indenter) { - writeln!( - out, - "export class DbConnection extends __DbConnectionImpl {{" - ); - out.indent(1); - writeln!( - out, - "static builder = (): __DbConnectionBuilder => {{" - ); - out.indent(1); - writeln!( - out, - "return new __DbConnectionBuilder(REMOTE_MODULE, (imp: __DbConnectionImpl) => imp as DbConnection);" - ); - out.dedent(1); - writeln!(out, "}}"); - writeln!(out, "subscriptionBuilder = (): SubscriptionBuilder => {{"); - out.indent(1); - writeln!(out, "return new SubscriptionBuilder(this);"); - out.dedent(1); - writeln!(out, "}}"); - out.dedent(1); - writeln!(out, "}}"); -} -fn print_reducer_enum_defn(module: &ModuleDef, out: &mut Indenter) { - writeln!(out, "// A type representing all the possible variants of a reducer."); - writeln!(out, "export type Reducer = never"); - for reducer in iter_reducers(module) { - writeln!( - out, - "| {{ name: \"{}\", args: {} }}", - reducer_variant_name(&reducer.name), - reducer_args_type_name(&reducer.name) - ); + vec![OutputFile { + filename: "index.ts".to_string(), + code: output.into_inner(), + }] } - writeln!(out, ";"); } fn print_spacetimedb_imports(out: &mut Indenter) { @@ -824,13 +483,12 @@ fn print_spacetimedb_imports(out: &mut Indenter) { "type AlgebraicTypeVariants as __AlgebraicTypeVariants", "Identity as __Identity", "ClientCache as __ClientCache", + "ClientTable as __ClientTable", "ConnectionId as __ConnectionId", "Timestamp as __Timestamp", "TimeDuration as __TimeDuration", "DbConnectionBuilder as __DbConnectionBuilder", - "TableCache as __TableCache", "BinaryWriter as __BinaryWriter", - "type TableHandle as __TableHandle", "type CallReducerFlags as __CallReducerFlags", "type EventContextInterface as __EventContextInterface", "type ReducerEventContextInterface as __ReducerEventContextInterface", @@ -842,6 +500,13 @@ fn print_spacetimedb_imports(out: &mut Indenter) { "DbConnectionImpl as __DbConnectionImpl", "type Event as __Event", "deepEqual as __deepEqual", + "schema as __schema", + "table as __table", + "reducers as __reducers", + "reducerSchema as __reducerSchema", + "DbConnectionConfig as __DbConnectionConfig", + "RemoteModule as __RemoteModule", + "t as __t", ]; types.sort(); writeln!(out, "import {{"); @@ -867,111 +532,45 @@ fn print_lint_suppression(output: &mut Indenter) { writeln!(output, "/* tslint:disable */"); } -fn write_get_algebraic_type_for_product( - module: &ModuleDef, - out: &mut Indenter, - type_cache_name: &str, - elements: &[(Identifier, AlgebraicTypeUse)], -) { - writeln!( - out, - "/** -* A function which returns this type represented as an AlgebraicType. -* This function is derived from the AlgebraicType used to generate this type. -*/" - ); - writeln!(out, "getTypeScriptAlgebraicType(): __AlgebraicTypeType {{"); - { - out.indent(1); - writeln!(out, "if ({type_cache_name}) return {type_cache_name};"); - // initialization is split in two because of recursive types - writeln!( - out, - "{type_cache_name} = __AlgebraicTypeValue.Product({{ elements: [] }});" - ); - writeln!(out, "{type_cache_name}.value.elements.push("); - out.indent(1); - convert_product_type_elements(module, out, elements, ""); - out.dedent(1); - writeln!(out, ");"); - writeln!(out, "return {type_cache_name};"); - out.dedent(1); - } - writeln!(out, "}},"); -} - +/// e.g. +/// ```ts +/// export default __t.object('Point', { +/// x: __t.f32(), +/// y: __t.f32(), +/// fooBar: __t.string(), +/// }); +/// ``` fn define_body_for_product( module: &ModuleDef, out: &mut Indenter, name: &str, elements: &[(Identifier, AlgebraicTypeUse)], ) { - write!(out, "export type {name} = {{"); + + write!(out, "export default __t.object(\"{name}\", {{"); if elements.is_empty() { writeln!(out, "}};"); } else { writeln!(out); - out.with_indent(|out| write_arglist_no_delimiters(module, out, elements, None, true).unwrap()); + out.with_indent(|out| write_object_type_builder_fields(module, out, elements, true).unwrap()); writeln!(out, "}};"); } - - let type_cache_name = &*format!("_cached_{name}_type_value"); - writeln!(out, "let {type_cache_name}: __AlgebraicTypeType | null = null;"); - out.newline(); - - writeln!( - out, - "/** - * An object for generated helper functions. - */" - ); - writeln!(out, "export const {name} = {{"); - out.indent(1); - write_get_algebraic_type_for_product(module, out, type_cache_name, elements); - writeln!(out); - - writeln!(out, "serialize(writer: __BinaryWriter, value: {name}): void {{"); - out.indent(1); - writeln!( - out, - "__AlgebraicTypeValue.serializeValue(writer, {name}.getTypeScriptAlgebraicType(), value);" - ); - out.dedent(1); - writeln!(out, "}},"); - writeln!(out); - - writeln!(out, "deserialize(reader: __BinaryReader): {name} {{"); - out.indent(1); - writeln!( - out, - "return __AlgebraicTypeValue.deserializeValue(reader, {name}.getTypeScriptAlgebraicType());" - ); - out.dedent(1); - writeln!(out, "}},"); - writeln!(out); - - out.dedent(1); - writeln!(out, "}}"); - - out.newline(); - - writeln!(out, "export default {name};"); - out.newline(); } -fn write_arglist_no_delimiters( +/// e.g. +/// ```ts +/// x: __t.f32(), +/// y: __t.f32(), +/// fooBar: __t.string(), +/// ``` +fn write_object_type_builder_fields( module: &ModuleDef, out: &mut impl Write, elements: &[(Identifier, AlgebraicTypeUse)], - prefix: Option<&str>, convert_case: bool, ) -> anyhow::Result<()> { for (ident, ty) in elements { - if let Some(prefix) = prefix { - write!(out, "{prefix} ")?; - } - let name = if convert_case { ident.deref().to_case(Case::Camel) } else { @@ -979,13 +578,58 @@ fn write_arglist_no_delimiters( }; write!(out, "{name}: ")?; - write_type(module, out, ty, None, None)?; + write_type_builder(module, out, ty)?; writeln!(out, ",")?; } Ok(()) } +/// e.g. `__t.option(__t.i32())`, `__t.string()` +fn write_type_builder(module: &ModuleDef, out: &mut W, ty: &AlgebraicTypeUse) -> fmt::Result { + match ty { + AlgebraicTypeUse::Unit => write!(out, "__t.unit()")?, + AlgebraicTypeUse::Never => write!(out, "__t.never()")?, + AlgebraicTypeUse::Identity => write!(out, "__t.identity()")?, + AlgebraicTypeUse::ConnectionId => write!(out, "__t.connectionId()")?, + AlgebraicTypeUse::Timestamp => write!(out, "__t.timestamp()")?, + AlgebraicTypeUse::TimeDuration => write!(out, "__t.timeDuration()")?, + AlgebraicTypeUse::ScheduleAt => write!(out, "__t.scheduleAt()")?, + AlgebraicTypeUse::Option(inner_ty) => { + write!(out, "__t.option(")?; + write_type_builder(module, out, inner_ty)?; + write!(out, ")")?; + }, + AlgebraicTypeUse::Primitive(prim) => match prim { + PrimitiveType::Bool => write!(out, "__t.bool()")?, + PrimitiveType::I8 => write!(out, "__t.i8()")?, + PrimitiveType::U8 => write!(out, "__t.u8()")?, + PrimitiveType::I16 => write!(out, "__t.i16()")?, + PrimitiveType::U16 => write!(out, "__t.u16()")?, + PrimitiveType::I32 => write!(out, "__t.i32()")?, + PrimitiveType::U32 => write!(out, "__t.u32()")?, + PrimitiveType::I64 => write!(out, "__t.i64()")?, + PrimitiveType::U64 => write!(out, "__t.u64()")?, + PrimitiveType::I128 => write!(out, "__t.i128()")?, + PrimitiveType::U128 => write!(out, "__t.u128()")?, + PrimitiveType::I256 => write!(out, "__t.i256()")?, + PrimitiveType::U256 => write!(out, "__t.u256()")?, + PrimitiveType::F32 => write!(out, "__t.f32()")?, + PrimitiveType::F64 => write!(out, "__t.f64()")?, + }, + AlgebraicTypeUse::String => write!(out, "__t.string()")?, + AlgebraicTypeUse::Array(elem_ty) => { + write!(out, "__t.array(")?; + write_type_builder(module, out, elem_ty)?; + write!(out, ")")?; + }, + AlgebraicTypeUse::Ref(r) => { + write!(out, "{}", type_ref_name(module, *r))?; + }, + } + Ok(()) +} + fn write_sum_variant_type(module: &ModuleDef, out: &mut Indenter, ident: &Identifier, ty: &AlgebraicTypeUse) { let name = ident.deref().to_case(Case::Pascal); write!(out, "export type {name} = "); @@ -1031,56 +675,14 @@ fn write_variant_types(module: &ModuleDef, out: &mut Indenter, variants: &[(Iden } } -fn write_variant_constructors( - module: &ModuleDef, - out: &mut Indenter, - name: &str, - variants: &[(Identifier, AlgebraicTypeUse)], -) { - // Write all the variant constructors. - // Write all of the variant constructors. - for (ident, ty) in variants { - if matches!(ty, AlgebraicTypeUse::Unit) { - // If the variant has no members, we can export a simple object. - // ``` - // Foo: { tag: "Foo" } = { tag: "Foo" } as const, - // ``` - write!(out, "{ident}: {{ tag: \"{ident}\" }} as const,"); - writeln!(out); - continue; - } - let variant_name = ident.deref().to_case(Case::Pascal); - write!(out, "{variant_name}: (value: "); - write_type(module, out, ty, None, None).unwrap(); - writeln!( - out, - "): {name}Variants.{variant_name} => ({{ tag: \"{variant_name}\", value }})," - ); - } -} - -fn write_get_algebraic_type_for_sum( - module: &ModuleDef, - out: &mut Indenter, - type_cache_name: &str, - variants: &[(Identifier, AlgebraicTypeUse)], -) { - writeln!(out, "getTypeScriptAlgebraicType(): __AlgebraicTypeType {{"); - { - indent_scope!(out); - writeln!(out, "if ({type_cache_name}) return {type_cache_name};"); - // initialization is split in two because of recursive types - writeln!(out, "{type_cache_name} = __AlgebraicTypeValue.Sum({{ variants: [] }});"); - writeln!(out, "{type_cache_name}.value.variants.push("); - out.indent(1); - convert_sum_type_variants(module, &mut out, variants, ""); - out.dedent(1); - writeln!(out, ");"); - writeln!(out, "return {type_cache_name};"); - } - writeln!(out, "}},"); -} - +/// e.g. +/// ```ts +/// // The tagged union or sum type for the algebraic type `Option`. +/// export default __t.enum("Option", { +/// none: __t.unit(), +/// some: { value: __t.i32() }, +/// }); +/// ``` fn define_body_for_sum( module: &ModuleDef, out: &mut Indenter, @@ -1088,71 +690,9 @@ fn define_body_for_sum( variants: &[(Identifier, AlgebraicTypeUse)], ) { writeln!(out, "// The tagged union or sum type for the algebraic type `{name}`."); - write!(out, "export type {name} = "); - - let names = variants - .iter() - .map(|(ident, _)| format!("{name}Variants.{}", ident.deref().to_case(Case::Pascal))) - .collect::>() - .join(" |\n "); - - if variants.is_empty() { - writeln!(out, "never;"); - } else { - writeln!(out, "{names};"); - } - - out.newline(); - - let type_cache_name = &*format!("_cached_{name}_type_value"); - writeln!(out, "let {type_cache_name}: __AlgebraicTypeType | null = null;"); - out.newline(); - - // Write the runtime value with helper functions - writeln!(out, "// A value with helper functions to construct the type."); - writeln!(out, "export const {name} = {{"); - out.indent(1); - - // Write all of the variant constructors. - writeln!( - out, - "// Helper functions for constructing each variant of the tagged union. -// ``` -// const foo = Foo.A(42); -// assert!(foo.tag === \"A\"); -// assert!(foo.value === 42); -// ```" - ); - write_variant_constructors(module, out, name, variants); - writeln!(out); - - // Write the function that generates the algebraic type. - write_get_algebraic_type_for_sum(module, out, type_cache_name, variants); - writeln!(out); - - writeln!( - out, - "serialize(writer: __BinaryWriter, value: {name}): void {{ - __AlgebraicTypeValue.serializeValue(writer, {name}.getTypeScriptAlgebraicType(), value); -}}," - ); - writeln!(out); - - writeln!( - out, - "deserialize(reader: __BinaryReader): {name} {{ - return __AlgebraicTypeValue.deserializeValue(reader, {name}.getTypeScriptAlgebraicType()); -}}," - ); - writeln!(out); - - out.dedent(1); - - writeln!(out, "}}"); - out.newline(); - - writeln!(out, "export default {name};"); - + write!(out, "export default __t.enum(\"{name}\", {{"); + out.with_indent(|out| write_object_type_builder_fields(module, out, variants, true).unwrap()); + writeln!(out, "}});"); out.newline(); } @@ -1290,72 +830,6 @@ pub fn write_type( Ok(()) } -fn convert_algebraic_type<'a>( - module: &'a ModuleDef, - out: &mut Indenter, - ty: &'a AlgebraicTypeUse, - ref_prefix: &'a str, -) { - match ty { - AlgebraicTypeUse::ScheduleAt => write!(out, "__AlgebraicTypeValue.createScheduleAtType()"), - AlgebraicTypeUse::Identity => write!(out, "__AlgebraicTypeValue.createIdentityType()"), - AlgebraicTypeUse::ConnectionId => write!(out, "__AlgebraicTypeValue.createConnectionIdType()"), - AlgebraicTypeUse::Timestamp => write!(out, "__AlgebraicTypeValue.createTimestampType()"), - AlgebraicTypeUse::TimeDuration => write!(out, "__AlgebraicTypeValue.createTimeDurationType()"), - AlgebraicTypeUse::Option(inner_ty) => { - write!(out, "__AlgebraicTypeValue.createOptionType("); - convert_algebraic_type(module, out, inner_ty, ref_prefix); - write!(out, ")"); - } - AlgebraicTypeUse::Array(ty) => { - write!(out, "__AlgebraicTypeValue.Array("); - convert_algebraic_type(module, out, ty, ref_prefix); - write!(out, ")"); - } - AlgebraicTypeUse::Ref(r) => write!( - out, - "{ref_prefix}{}.getTypeScriptAlgebraicType()", - type_ref_name(module, *r) - ), - AlgebraicTypeUse::Primitive(prim) => { - write!(out, "__AlgebraicTypeValue.{prim:?}"); - } - AlgebraicTypeUse::Unit => write!(out, "__AlgebraicTypeValue.Product({{ elements: [] }})"), - AlgebraicTypeUse::Never => unimplemented!(), - AlgebraicTypeUse::String => write!(out, "__AlgebraicTypeValue.String"), - } -} - -fn convert_sum_type_variants<'a>( - module: &'a ModuleDef, - out: &mut Indenter, - variants: &'a [(Identifier, AlgebraicTypeUse)], - ref_prefix: &'a str, -) { - for (ident, ty) in variants { - write!(out, "{{ name: \"{ident}\", algebraicType: ",); - convert_algebraic_type(module, out, ty, ref_prefix); - writeln!(out, " }},"); - } -} - -fn convert_product_type_elements<'a>( - module: &'a ModuleDef, - out: &mut Indenter, - elements: &'a [(Identifier, AlgebraicTypeUse)], - ref_prefix: &'a str, -) { - for (ident, ty) in elements { - write!( - out, - "{{ name: \"{}\", algebraicType: ", - ident.deref().to_case(Case::Camel) - ); - convert_algebraic_type(module, out, ty, ref_prefix); - writeln!(out, " }},"); - } -} - /// Print imports for each of the `imports`. fn print_imports(module: &ModuleDef, out: &mut Indenter, imports: Imports, suffix: Option<&str>) { for typeref in imports { From 83a23f9d1ffeac322d519a70a2f431a84befe8fe Mon Sep 17 00:00:00 2001 From: = Date: Mon, 3 Nov 2025 23:38:34 -0500 Subject: [PATCH 07/23] Almost there, just in a bit of a pickle --- .../src/lib/constraints.ts | 11 + .../bindings-typescript/src/lib/reducers.ts | 5 +- crates/bindings-typescript/src/lib/table.ts | 20 +- .../src/react/SpacetimeDBProvider.ts | 24 +- .../src/react/useReducer.ts | 23 +- .../src/react/useSpacetimeDB.ts | 6 +- .../bindings-typescript/src/react/useTable.ts | 83 +++-- .../src/sdk/client_table.ts | 11 +- .../src/sdk/db_connection_builder.ts | 15 +- .../src/sdk/db_connection_impl.ts | 2 + crates/bindings-typescript/src/sdk/db_view.ts | 4 +- .../bindings-typescript/test-app/src/App.tsx | 2 +- .../module_bindings/create_player_reducer.ts | 59 +-- .../test-app/src/module_bindings/index.ts | 348 +++--------------- .../src/module_bindings/player_table.ts | 115 +----- .../src/module_bindings/player_type.ts | 73 +--- .../src/module_bindings/point_type.ts | 16 +- .../module_bindings/unindexed_player_table.ts | 73 +--- .../module_bindings/unindexed_player_type.ts | 65 +--- .../src/module_bindings/user_table.ts | 100 +---- .../test-app/src/module_bindings/user_type.ts | 58 +-- crates/codegen/src/typescript.rs | 248 +++++-------- 22 files changed, 356 insertions(+), 1005 deletions(-) diff --git a/crates/bindings-typescript/src/lib/constraints.ts b/crates/bindings-typescript/src/lib/constraints.ts index 4301137281f..4b206a8c858 100644 --- a/crates/bindings-typescript/src/lib/constraints.ts +++ b/crates/bindings-typescript/src/lib/constraints.ts @@ -37,3 +37,14 @@ export type ColumnIsUnique> = M extends | { isPrimaryKey: true } ? true : false; + +/** + * Constraint helper type used *inside* {@link table} to enforce the type + * of constraint definitions. + */ +export type ConstraintOpts = { + name?: string; +} & ( + | { constraint: 'unique'; columns: [AllowedCol] } + | { constraint: 'primaryKey'; columns: [AllowedCol] } +); \ No newline at end of file diff --git a/crates/bindings-typescript/src/lib/reducers.ts b/crates/bindings-typescript/src/lib/reducers.ts index e6c1b767a04..64f54c3d961 100644 --- a/crates/bindings-typescript/src/lib/reducers.ts +++ b/crates/bindings-typescript/src/lib/reducers.ts @@ -11,7 +11,6 @@ import type { InferTypeOfRow, RowBuilder, RowObj, - StringBuilder, TypeBuilder, } from './type_builders'; @@ -265,7 +264,7 @@ export function clientDisconnected< */ export type ReducerSchema< ReducerName extends string, - Params extends ParamsObj, + Params extends ParamsObj | RowObj, > = { /** * The name of the reducer. @@ -347,7 +346,7 @@ export function reducerSchema< Params extends ParamsObj, >( name: ReducerName, - params: Params + params: Params ): ReducerSchema { const paramType: ProductType = { elements: Object.entries(params).map(([n, c]) => ({ diff --git a/crates/bindings-typescript/src/lib/table.ts b/crates/bindings-typescript/src/lib/table.ts index a4d4d9b5254..9bbc3e9e99c 100644 --- a/crates/bindings-typescript/src/lib/table.ts +++ b/crates/bindings-typescript/src/lib/table.ts @@ -4,10 +4,11 @@ import RawIndexAlgorithm from './autogen/raw_index_algorithm_type'; import type RawIndexDefV9 from './autogen/raw_index_def_v_9_type'; import type RawSequenceDefV9 from './autogen/raw_sequence_def_v_9_type'; import type RawTableDefV9 from './autogen/raw_table_def_v_9_type'; -import type { AllUnique } from './constraints'; +import type { AllUnique, ConstraintOpts } from './constraints'; import type { ColumnIndex, IndexColumns, Indexes, IndexOpts, ReadonlyIndexes } from './indexes'; import { MODULE_DEF, splitName } from './schema'; import { + ProductBuilder, RowBuilder, type ColumnBuilder, type ColumnMetadata, @@ -87,6 +88,7 @@ export type TableOpts = { name: string; public?: boolean; indexes?: IndexOpts[]; // declarative multi‑column indexes + constraints?: ConstraintOpts[]; scheduled?: string; }; @@ -295,6 +297,22 @@ export function table>( indexes.push({ name: undefined, accessorName: indexOpts.name, algorithm }); } + // add explicit constraints from options.constraints + for (const constraintOpts of opts.constraints ?? []) { + if (constraintOpts.constraint === 'unique') { + let data: RawConstraintDefV9['data']; + data = { + tag: 'Unique', + value: { columns: constraintOpts.columns.map(c => colIds.get(c)!) }, + }; + constraints.push({ name: constraintOpts.name, data }); + continue; + } + if (constraintOpts.constraint === 'primaryKey') { + pk.push(...constraintOpts.columns.map(c => colIds.get(c)!)); + } + } + for (const index of indexes) { const cols = index.algorithm.tag === 'Direct' diff --git a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts index 7ddd5b95981..0ca560b86ec 100644 --- a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts @@ -2,6 +2,7 @@ import { DbConnectionBuilder, type DbConnectionImpl, type ErrorContextInterface, + type RemoteModuleOf, type SubscriptionEventContextInterface, } from '../sdk/db_connection_impl'; import * as React from 'react'; @@ -12,21 +13,20 @@ import type { UntypedRemoteModule } from '../sdk/spacetime_module'; export interface SpacetimeDBProviderProps< RemoteModule extends UntypedRemoteModule, - DbConnection extends DbConnectionImpl, + DbConnection extends DbConnectionImpl = DbConnectionImpl, > { connectionBuilder: DbConnectionBuilder; children?: React.ReactNode; } export function SpacetimeDBProvider< - RemoteModule extends UntypedRemoteModule, - DbConnection extends DbConnectionImpl, ->({ connectionBuilder, children }: SpacetimeDBProviderProps) { + DbConnection extends DbConnectionImpl, +>({ connectionBuilder, children }: SpacetimeDBProviderProps>) { // Holds the imperative connection instance when (and only when) we’re on the client. const connRef = React.useRef(null); const getConnection = React.useCallback(() => connRef.current, []); - const [state, setState] = React.useState>({ + const [state, setState] = React.useState>({ isActive: false, identity: undefined, token: undefined, @@ -38,7 +38,7 @@ export function SpacetimeDBProvider< // Build on the client only; useEffect won't run during SSR. React.useEffect(() => { // Register callback for onConnect to update state - const onConnect = (conn: DbConnectionImpl) => { + const onConnect = (conn: DbConnectionImpl>) => { setState(s => ({ ...s, isActive: conn.isActive, @@ -47,13 +47,13 @@ export function SpacetimeDBProvider< connectionId: conn.connectionId, })); }; - const onDisconnect = (ctx: ErrorContextInterface) => { + const onDisconnect = (ctx: ErrorContextInterface>) => { setState(s => ({ ...s, isActive: ctx.isActive, })); }; - const onConnectError = (ctx: ErrorContextInterface, err: Error) => { + const onConnectError = (ctx: ErrorContextInterface>, err: Error) => { setState(s => ({ ...s, isActive: ctx.isActive, @@ -75,13 +75,13 @@ export function SpacetimeDBProvider< // Lazily build once if (!connRef.current) { - connRef.current = connectionBuilder.build() as DbConnection; + connRef.current = connectionBuilder.build() as unknown as DbConnection; } return () => { - connRef.current?.removeOnConnect(onConnect); - connRef.current?.removeOnDisconnect(onDisconnect); - connRef.current?.removeOnConnectError(onConnectError); + connRef.current?.removeOnConnect(onConnect as any); + connRef.current?.removeOnDisconnect(onDisconnect as any); + connRef.current?.removeOnConnectError(onConnectError as any); connRef.current?.disconnect(); connRef.current = null; }; diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index b7684234b46..06eb76da311 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -1,19 +1,16 @@ -import type { DbConnectionImpl } from "../sdk/db_connection_impl"; -import type { ReducerNamesFromReducers } from "../sdk/reducer_handle"; +import type { DbConnectionImpl, RemoteModuleOf } from "../sdk/db_connection_impl"; +import type { ReducersView } from "../sdk/reducers"; import type { UntypedRemoteModule } from "../sdk/spacetime_module"; -import { useSpacetimeDB } from "./useSpacetimeDB"; +import { useSpacetimeDB, } from "./useSpacetimeDB"; + export function useReducer< - RemoteModule extends UntypedRemoteModule, - DbConnection extends DbConnectionImpl, - ReducerName extends ReducerNamesFromReducers = ReducerNamesFromReducers< - DbConnection['reducers'] - >, - ReducerType = DbConnection['reducers'][ReducerName & keyof DbConnection['reducers']] + DbConnection extends DbConnectionImpl, + ReducerName extends keyof ReducersView> = keyof ReducersView> >( - reducerName: ReducerName, -): ReducerType { - const connectionState = useSpacetimeDB(); + reducerName: ReducerName +): ReducersView>[ReducerName] { + const connectionState = useSpacetimeDB(); const connection = connectionState.getConnection()!; - return connection.reducers[reducerName as keyof typeof connection.reducers]; + return connection.reducers[reducerName]; } \ No newline at end of file diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index c0421a2ffd2..8eb1a1e01d1 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -3,12 +3,12 @@ import type { DbConnectionImpl } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -export const SpacetimeDBContext = createContext> | undefined>(undefined); +export const SpacetimeDBContext = createContext> | undefined>(undefined); // Throws an error if used outside of a SpacetimeDBProvider // Error is caught by other hooks like useTable so they can provide better error messages -export function useSpacetimeDB>(): ConnectionState { - const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; +export function useSpacetimeDB>(): ConnectionState { + const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; if (!context) { throw new Error( 'useSpacetimeDB must be used within a SpacetimeDBProvider component. Did you forget to add a `SpacetimeDBProvider` to your component tree?' diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index 311b3d68663..0dc69d54f6a 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -6,10 +6,13 @@ import { useSyncExternalStore, } from 'react'; import { useSpacetimeDB } from './useSpacetimeDB'; -import { DbConnectionImpl, TableCache } from '../sdk/db_connection_impl'; -import type { TableNamesFromDb } from '../sdk/client_table'; +import { DbConnectionImpl, TableCache, type EventContextInterface, type RemoteModuleOf } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; -import type { UntypedRemoteModule } from '../sdk/spacetime_module'; +import type { RemoteModule, UntypedRemoteModule } from '../sdk/spacetime_module'; +import type { ClientDbView } from '../sdk/db_view'; +import type { RowType, UntypedTableDef } from '../lib/table'; +import type { ClientTable } from '../sdk/client_table'; +import type { Infer, InferTypeOfRow } from '../lib/type_builders'; export interface UseQueryCallbacks { onInsert?: (row: RowType) => void; @@ -69,7 +72,7 @@ export const isOr = ( e: Expr ): e is Extract, { type: 'or' }> => e.type === 'or'; -type RecordLike = Record; +type RecordLike = Record; export function evaluate( expr: Expr, @@ -179,6 +182,15 @@ type ColumnsFromRow = { }[keyof R] & string; +// From a ClientTable, get Tbl +type TableDefOfClient = T extends ClientTable ? Tbl : never; + +// Row type for a given connection + table key +type RowTypeOfTable< + C extends DbConnectionImpl, + K extends keyof ClientDbView> +> = InferTypeOfRow>[K]>>>; + /** * React hook to subscribe to a table in SpacetimeDB and receive live updates as rows are inserted, updated, or deleted. * @@ -214,15 +226,12 @@ type ColumnsFromRow = { */ export function useTable< DbConnection extends DbConnectionImpl, - RowType extends Record, - TableName extends TableNamesFromDb = TableNamesFromDb< - DbConnection['db'] - >, + TableName extends keyof ClientDbView> >( tableName: TableName, - where: Expr>, - callbacks?: UseQueryCallbacks -): Snapshot; + where: Expr>> , + callbacks?: UseQueryCallbacks> +): Snapshot>; /** * React hook to subscribe to a table in SpacetimeDB and receive live updates as rows are inserted, updated, or deleted. @@ -259,37 +268,33 @@ export function useTable< */ export function useTable< DbConnection extends DbConnectionImpl, - RowType extends Record, - TableName extends TableNamesFromDb = TableNamesFromDb< - DbConnection['db'] - >, + TableName extends keyof ClientDbView> >( tableName: TableName, - callbacks?: UseQueryCallbacks -): Snapshot; + callbacks?: UseQueryCallbacks> +): Snapshot>; export function useTable< DbConnection extends DbConnectionImpl, - TableName extends TableNamesFromDb = TableNamesFromDb< - DbConnection['db'] - >, + TableName extends keyof ClientDbView>, >( tableName: TableName, whereClauseOrCallbacks?: - | Expr> - | UseQueryCallbacks, - callbacks?: UseQueryCallbacks -): Snapshot { - let whereClause: Expr> | undefined; + | Expr>> + | UseQueryCallbacks>, + callbacks?: UseQueryCallbacks> +): Snapshot> { + type UseTableRowType = RowTypeOfTable; + let whereClause: Expr> | undefined; if ( whereClauseOrCallbacks && typeof whereClauseOrCallbacks === 'object' && 'type' in whereClauseOrCallbacks ) { - whereClause = whereClauseOrCallbacks as Expr>; + whereClause = whereClauseOrCallbacks as Expr>; } else { callbacks = whereClauseOrCallbacks as - | UseQueryCallbacks + | UseQueryCallbacks | undefined; } const [subscribeApplied, setSubscribeApplied] = useState(false); @@ -305,25 +310,23 @@ export function useTable< } const query = - `SELECT * FROM ${tableName}` + + `SELECT * FROM ${String(tableName)}` + (whereClause ? ` WHERE ${toString(whereClause)}` : ''); const latestTransactionEvent = useRef(null); - const lastSnapshotRef = useRef | null>(null); + const lastSnapshotRef = useRef | null>(null); const whereKey = whereClause ? toString(whereClause) : ''; - const computeSnapshot = useCallback((): Snapshot => { + const computeSnapshot = useCallback((): Snapshot => { const connection = connectionState.getConnection(); if (!connection) { return { rows: [], state: 'loading' }; } - const table = connection.db[ - tableName as keyof typeof connection.db - ]; - const result: readonly RowType[] = whereClause - ? Array.from(table.iter()).filter(row => evaluate(whereClause, row)) - : Array.from(table.iter()); + const table = connection.db[tableName]; + const result: readonly UseTableRowType[] = whereClause + ? Array.from(table.iter()).filter(row => evaluate(whereClause, row as any)) as unknown as readonly UseTableRowType[] + : Array.from(table.iter()) as unknown as readonly UseTableRowType[]; return { rows: result, state: subscribeApplied ? 'ready' : 'loading', @@ -348,7 +351,7 @@ export function useTable< const subscribe = useCallback( (onStoreChange: () => void) => { - const onInsert = (ctx: any, row: RowType) => { + const onInsert = (ctx: EventContextInterface, row: any) => { if (whereClause && !evaluate(whereClause, row)) { return; } @@ -363,7 +366,7 @@ export function useTable< } }; - const onDelete = (ctx: any, row: RowType) => { + const onDelete = (ctx: EventContextInterface, row: any) => { if (whereClause && !evaluate(whereClause, row)) { return; } @@ -378,7 +381,7 @@ export function useTable< } }; - const onUpdate = (ctx: any, oldRow: RowType, newRow: RowType) => { + const onUpdate = (ctx: EventContextInterface, oldRow: any, newRow: any) => { const change = classifyMembership(whereClause, oldRow, newRow); switch (change) { @@ -434,7 +437,7 @@ export function useTable< ] ); - const getSnapshot = useCallback((): Snapshot => { + const getSnapshot = useCallback((): Snapshot => { if (!lastSnapshotRef.current) { lastSnapshotRef.current = computeSnapshot(); } diff --git a/crates/bindings-typescript/src/sdk/client_table.ts b/crates/bindings-typescript/src/sdk/client_table.ts index ca19165f639..6d0e006e9b2 100644 --- a/crates/bindings-typescript/src/sdk/client_table.ts +++ b/crates/bindings-typescript/src/sdk/client_table.ts @@ -55,13 +55,4 @@ export type ClientTableCore< TableDef extends UntypedTableDef, > = ReadonlyTableMethods & - ClientTableMethods; - -/** - * Client database view, mapping table names to their corresponding ClientTable handles. - */ -export type ClientDbView< - RemoteModule extends UntypedRemoteModule, -> = { - readonly [Tbl in RemoteModule['tables'][number] as Tbl['name']]: ClientTable; -}; \ No newline at end of file + ClientTableMethods; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index 4eec420dd97..cf33668d4c7 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -1,16 +1,17 @@ import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl'; import { EventEmitter } from './event_emitter'; -import type { DbConnectionConfig, ErrorContextInterface, Identity, SubscriptionEventContextInterface } from '../'; +import type { DbConnectionConfig, ErrorContextInterface, Identity, RemoteModuleOf, SubscriptionEventContextInterface } from '../'; import { type UntypedRemoteModule } from './spacetime_module'; import { ensureMinimumVersionOrThrow } from './version'; import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; /** * The database client connection to a SpacetimeDB server. + * NOTE: DbConnectionImpl is used here */ export class DbConnectionBuilder< RemoteModule extends UntypedRemoteModule, - DbConnection extends DbConnectionImpl, + DbConnection extends DbConnectionImpl > { #uri?: URL; #nameOrAddress?: string; @@ -31,8 +32,8 @@ export class DbConnectionBuilder< * @param dbConnectionConstructor The constructor to use to create a new `DbConnection`. */ constructor( - private remoteModule: RemoteModule, - private dbConnectionCtor: (config: DbConnectionConfig) => DbConnection + private remoteModule: RemoteModuleOf, + private dbConnectionCtor: (config: DbConnectionConfig>) => DbConnection ) { this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn; } @@ -178,7 +179,7 @@ export class DbConnectionBuilder< * }); * ``` */ - onConnectError(callback: (ctx: ErrorContextInterface, error: Error) => void): this { + onConnectError(callback: (ctx: ErrorContextInterface>, error: Error) => void): this { this.#emitter.on('connectError', callback); return this; } @@ -210,7 +211,7 @@ export class DbConnectionBuilder< * @throws {Error} Throws an error if called multiple times on the same `DbConnectionBuilder`. */ onDisconnect( - callback: (ctx: ErrorContextInterface, error?: Error | undefined) => void + callback: (ctx: ErrorContextInterface>, error?: Error | undefined) => void ): this { this.#emitter.on('disconnect', callback); return this; @@ -230,7 +231,7 @@ export class DbConnectionBuilder< * DbConnection.builder().withUri(host).withModuleName(name_or_address).withToken(auth_token).build(); * ``` */ - build(): DbConnectionImpl { + build(): DbConnection { if (!this.#uri) { throw new Error('URI is required to connect to SpacetimeDB'); } diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 14ea6d83606..04b1a7967bd 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -57,6 +57,8 @@ import { toCamelCase } from '../lib/utils.ts'; export { DbConnectionBuilder, SubscriptionBuilderImpl, TableCache, type Event }; +export type RemoteModuleOf = C extends DbConnectionImpl ? RM : never; + export type { DbContext, EventContextInterface, diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts index 9d991765226..afcdbd4d4d0 100644 --- a/crates/bindings-typescript/src/sdk/db_view.ts +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -5,5 +5,5 @@ import type { ClientTable } from "./client_table"; * A type representing a client-side database view, mapping table names to their corresponding client Table handles. */ export type ClientDbView = { - readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable; -}; + readonly [Tbl in RemoteModule['tables'][number] as Extract]: ClientTable; +}; \ No newline at end of file diff --git a/crates/bindings-typescript/test-app/src/App.tsx b/crates/bindings-typescript/test-app/src/App.tsx index 894e49b9239..bb2e7b1b7bf 100644 --- a/crates/bindings-typescript/test-app/src/App.tsx +++ b/crates/bindings-typescript/test-app/src/App.tsx @@ -7,7 +7,7 @@ function App() { const x = useReducer; const createPlayer = useReducer('createPlayer'); const connection = useSpacetimeDB(); - const players = useTable('player', { + const players = useTable('player', { onInsert: player => { console.log(player); }, diff --git a/crates/bindings-typescript/test-app/src/module_bindings/create_player_reducer.ts b/crates/bindings-typescript/test-app/src/module_bindings/create_player_reducer.ts index 494f9eb5d1e..fdec0043c97 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/create_player_reducer.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/create_player_reducer.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,54 +30,13 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, } from '../../../src/index'; -import { Point } from './point_type'; -// Mark import as potentially unused -declare type __keep_Point = Point; +import Point from './point_type'; -export type CreatePlayer = { - name: string; - location: Point; +export default { + name: __t.string(), + location: Point, }; -let _cached_CreatePlayer_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const CreatePlayer = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_CreatePlayer_type_value) return _cached_CreatePlayer_type_value; - _cached_CreatePlayer_type_value = __AlgebraicTypeValue.Product({ - elements: [], - }); - _cached_CreatePlayer_type_value.value.elements.push( - { name: 'name', algebraicType: __AlgebraicTypeValue.String }, - { name: 'location', algebraicType: Point.getTypeScriptAlgebraicType() } - ); - return _cached_CreatePlayer_type_value; - }, - - serialize(writer: __BinaryWriter, value: CreatePlayer): void { - __AlgebraicTypeValue.serializeValue( - writer, - CreatePlayer.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): CreatePlayer { - return __AlgebraicTypeValue.deserializeValue( - reader, - CreatePlayer.getTypeScriptAlgebraicType() - ); - }, -}; - -export default CreatePlayer; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index 95c704eaad9..004d8995f86 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -1,7 +1,7 @@ // 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 ../../../src/index cli version 1.5.0 (commit 5bfc84351742a6a8dc717b6c0011946f2d1b632d). +// This was generated using ../../../src/index cli version 1.6.0 (commit 051fb278b3d871c87536cb87c8905c21103728fe). /* eslint-disable */ /* tslint:disable */ @@ -10,15 +10,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -26,84 +32,58 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type ClientTable as __ClientTable, type RemoteModule as __RemoteModule, - type SetReducerFlags as __SetReducerFlags, - DbConnectionConfig, + type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, } from '../../../src/index'; // Import and reexport all reducer arg types -import { CreatePlayer } from './create_player_reducer.ts'; +import CreatePlayer from './create_player_reducer.ts'; export { CreatePlayer }; // Import and reexport all table handle types -import { PlayerTableHandle } from './player_table.ts'; -export { PlayerTableHandle }; -import { UnindexedPlayerTableHandle } from './unindexed_player_table.ts'; -export { UnindexedPlayerTableHandle }; -import { UserTableHandle } from './user_table.ts'; -export { UserTableHandle }; +import PlayerRow from './player_table.ts'; +export { PlayerRow }; +import UnindexedPlayerRow from './unindexed_player_table.ts'; +export { UnindexedPlayerRow }; +import UserRow from './user_table.ts'; +export { UserRow }; // Import and reexport all types -import { Player } from './player_type.ts'; +import Player from './player_type.ts'; export { Player }; -import { Point } from './point_type.ts'; +import Point from './point_type.ts'; export { Point }; -import { UnindexedPlayer } from './unindexed_player_type.ts'; +import UnindexedPlayer from './unindexed_player_type.ts'; export { UnindexedPlayer }; -import { User } from './user_type.ts'; -import { schema } from '../../../src/lib/schema.ts'; -import t from '../../../src/lib/type_builders.ts'; -import { table } from '../../../src/lib/table.ts'; -import { reducerSchema, reducers } from '../../../src/lib/reducers.ts'; -import { RemoteModule } from '../../../src/sdk/spacetime_module.ts'; +import User from './user_type.ts'; export { User }; -const pointType = t.object('Point', { - x: t.number(), - y: t.number(), -}); - -table({ - name: 'player', - primaryKey: 'ownerId', - - indexes: [ - { name: 'this_is_an_index', algorithm: "btree", columns: [ "ownerId" ] } - ], -}, t.row({ - ownerId: t.string(), - name: t.string(), - location: pointType, -})) - -const tablesSchema = schema( - table({ name: 'player', }, t.row({ - ownerId: t.string(), - name: t.string(), - location: pointType, - })), - table({ name: 'unindexed_player', }, t.row({ - ownerId: t.string(), - name: t.string(), - location: pointType, - })), - table({ name: 'user', primaryKey: 'identity', }, t.row({ - identity: t.string(), - name: t.string(), - })), +const tablesSchema = __schema( + __table( + { + name: 'player', + indexes: [], + }, + PlayerRow + ), + __table( + { + name: 'unindexed_player', + indexes: [], + }, + UnindexedPlayerRow + ), + __table( + { + name: 'user', + indexes: [], + }, + UserRow + ) ); -const reducersSchema = reducers( - reducerSchema('create_player', { - name: t.string(), - location: pointType, - }), - reducerSchema('foo_bar', { - name: t.string(), - location: pointType, - }), +const reducersSchema = __reducers( + __reducerSchema('create_player', CreatePlayer) ); const REMOTE_MODULE = { @@ -112,26 +92,21 @@ const REMOTE_MODULE = { }, tables: tablesSchema.schemaType.tables, reducers: reducersSchema.reducersType.reducers, -} satisfies RemoteModule< +} satisfies __RemoteModule< typeof tablesSchema.schemaType, typeof reducersSchema.reducersType >; -export type EventContext = __EventContextInterface< - typeof REMOTE_MODULE ->; +export type Module = typeof REMOTE_MODULE; +export type EventContext = __EventContextInterface; export type ReducerEventContext = __ReducerEventContextInterface< typeof REMOTE_MODULE >; - export type SubscriptionEventContext = __SubscriptionEventContextInterface< typeof REMOTE_MODULE >; - -export type ErrorContext = __ErrorContextInterface< - typeof REMOTE_MODULE ->; +export type ErrorContext = __ErrorContextInterface; export class SubscriptionBuilder extends __SubscriptionBuilderImpl< typeof REMOTE_MODULE @@ -140,232 +115,17 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< export class DbConnectionBuilder extends __DbConnectionBuilder< typeof REMOTE_MODULE, DbConnection -> {}; +> {} export class DbConnection extends __DbConnectionImpl { static builder = (): DbConnectionBuilder => { - return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); + return new DbConnectionBuilder( + REMOTE_MODULE, + (config: __DbConnectionConfig) => + new DbConnection(config) + ); }; subscriptionBuilder = (): SubscriptionBuilder => { return new SubscriptionBuilder(this); }; } - - -// // --- factory returning a well-typed object --- -// function makeReducersObj( -// r: { reducersType: R }, -// call: (name: string, spacetime: R['reducers'][number]['paramsSpacetimeType'], params: R['reducers'][number]['params']) => void -// ): Readonly> { -// const obj: Record = {}; -// for (const red of Object.values(r.reducersType.reducers)) { -// const key = toCamelCase(red.name as string); -// obj[key] = (params: any) => call(red.name as string, red.paramsSpacetimeType, params); -// } -// return Object.freeze(obj) as Readonly>; -// } - -// const reducersObj: UntypedReducers = {} -// for (const reducer of Object.values(reducersSchema.reducersType.reducers)) { -// reducersObj[toCamelCase(reducer.name)] = function(...args: Args) { -// this.#connection.callReducerWithParams( -// reducersSchema.reducersType.reducers[0].name, -// reducersSchema.reducersType.reducers[0].paramsSpacetimeType, -// args, -// "FullUpdate", -// ); -// }; -// } -// Object.freeze(reducersObj); - -// export class RemoteReducers implements UntypedReducersDef { -// [key: string]: (...args: any[]) => void; - -// #connection: __DbConnectionImpl; -// #setCallReducerFlags: SetReducerFlags; - -// constructor( -// connection: __DbConnectionImpl, -// setCallReducerFlags: SetReducerFlags -// ) { -// this.#connection = connection; -// this.#setCallReducerFlags = setCallReducerFlags; -// } - -// createPlayer(name: string, location: Point) { -// this.#connection.callReducerWithParams( -// r.reducersType.reducers[0].name, -// r.reducersType.reducers[0].paramsSpacetimeType, -// [name, location], -// this.#setCallReducerFlags.createPlayerFlags, -// ); -// } - -// // onCreatePlayer( -// // callback: (ctx: ReducerEventContext, name: string, location: Point) => void -// // ) { -// // this.#connection.onReducer('create_player', callback); -// // } - -// // removeOnCreatePlayer( -// // callback: (ctx: ReducerEventContext, name: string, location: Point) => void -// // ) { -// // this.#connection.offReducer('create_player', callback); -// // } -// } - -// export class SetReducerFlags implements __SetReducerFlags { -// createPlayerFlags: __CallReducerFlags = 'FullUpdate'; -// createPlayer(flags: __CallReducerFlags) { -// this.createPlayerFlags = flags; -// } -// } - -// export class RemoteTables implements ClientDbView { -// constructor(private connection: __DbConnectionImpl) {} - -// get player(): PlayerTableHandle { -// // clientCache is a private property -// return new PlayerTableHandle( -// ( -// this.connection as unknown as { clientCache: __ClientCache } -// ).clientCache.getOrCreateTable<"player">(spacetimedb.tablesDef.tables[0]) -// ); -// } - -// get unindexedPlayer(): UnindexedPlayerTableHandle<'unindexed_player'> { -// // clientCache is a private property -// return new UnindexedPlayerTableHandle( -// ( -// this.connection as unknown as { clientCache: __ClientCache } -// ).clientCache.getOrCreateTable<"unindexed_player">( -// REMOTE_MODULE.tables.unindexed_player -// ) -// ); -// } - -// get user(): UserTableHandle<'user'> { -// // clientCache is a private property -// return new UserTableHandle( -// ( -// this.connection as unknown as { clientCache: __ClientCache } -// ).clientCache.getOrCreateTable<"user">(REMOTE_MODULE.tables.user) -// ); -// } -// } - -// export class SubscriptionBuilder extends __SubscriptionBuilderImpl< -// SchemaDef, -// RemoteReducers -// > {} - -// export class DbConnection extends __DbConnectionImpl< -// SchemaDef, -// RemoteReducers, -// > { -// static builder = (): __DbConnectionBuilder< -// DbConnection, -// ErrorContext, -// SubscriptionEventContext -// > => { -// return new __DbConnectionBuilder< -// DbConnection, -// ErrorContext, -// SubscriptionEventContext -// >(REMOTE_MODULE, (imp: __DbConnectionImpl) => imp as DbConnection); -// }; -// subscriptionBuilder = (): SubscriptionBuilder => { -// return new SubscriptionBuilder(this); -// }; -// } - -// export type EventContext = __EventContextInterface< -// RemoteTables, -// RemoteReducers, -// SetReducerFlags, -// Reducer -// >; -// export type ReducerEventContext = __ReducerEventContextInterface< -// RemoteTables, -// RemoteReducers, -// SetReducerFlags, -// Reducer -// >; -// export type SubscriptionEventContext = __SubscriptionEventContextInterface< -// RemoteTables, -// RemoteReducers, -// SetReducerFlags -// >; -// export type ErrorContext = __ErrorContextInterface< -// RemoteTables, -// RemoteReducers, -// SetReducerFlags -// >; - -// const REMOTE_MODULE = { -// tables: { -// player: { -// name: 'player' as const, -// rowType: Player.getTypeScriptAlgebraicType(), -// primaryKey: 'ownerId', -// primaryKeyInfo: { -// colName: 'ownerId', -// colType: ( -// Player.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product -// ).value.elements[0].algebraicType, -// }, -// }, -// unindexed_player: { -// tableName: 'unindexed_player' as const, -// rowType: UnindexedPlayer.getTypeScriptAlgebraicType(), -// }, -// user: { -// tableName: 'user' as const, -// rowType: User.getTypeScriptAlgebraicType(), -// primaryKey: 'identity', -// primaryKeyInfo: { -// colName: 'identity', -// colType: ( -// User.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product -// ).value.elements[0].algebraicType, -// }, -// }, -// }, -// reducers: { -// create_player: { -// reducerName: 'create_player', -// argsType: CreatePlayer.getTypeScriptAlgebraicType(), -// }, -// }, -// versionInfo: { -// cliVersion: '1.5.0', -// }, -// // Constructors which are used by the DbConnectionImpl to -// // extract type information from the generated RemoteModule. -// // -// // NOTE: This is not strictly necessary for `eventContextConstructor` because -// // all we do is build a TypeScript object which we could have done inside the -// // SDK, but if in the future we wanted to create a class this would be -// // necessary because classes have methods, so we'll keep it. -// eventContextConstructor: ( -// imp: __DbConnectionImpl, -// event: __Event -// ) => { -// return { -// ...(imp as DbConnection), -// event, -// }; -// }, -// dbViewConstructor: (imp: __DbConnectionImpl) => { -// return new RemoteTables(imp); -// }, -// reducersConstructor: ( -// imp: __DbConnectionImpl, -// setReducerFlags: SetReducerFlags -// ) => { -// return new RemoteReducers(imp, setReducerFlags); -// }, -// setReducerFlagsConstructor: () => { -// return new SetReducerFlags(); -// }, -// } satisfies __RemoteModule; \ No newline at end of file diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts index a966cde4283..239885c6eb7 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,110 +30,13 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type ClientTable as __ClientTable, - table, - t, } from '../../../src/index'; -import { Player } from './player_type'; -import { Point } from './point_type'; -// Mark import as potentially unused -declare type __keep_Point = Point; +import Point from './point_type'; -import { - type EventContext, - type Reducer, - RemoteReducers, - RemoteTables, -} from '.'; -declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; - - -export default table({ name: 'player' }, { - id: t.string().primaryKey().index(), - ownerId: t.string().unique(), - timestamp: t.timestamp(), - score: t.i32(), +export default __t.row({ + ownerId: __t.string(), + name: __t.string(), location: Point, }); - -/** - * Table handle for the table `player`. - * - * Obtain a handle from the [`player`] property on [`RemoteTables`], - * like `ctx.db.player`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.player.on_insert(...)`. - */ -export class PlayerTableHandle - implements __ClientTable -{ - // phantom type to track the table name - readonly tableName!: TableName; - tableCache: __TableCache; - - constructor(tableCache: __TableCache) { - this.tableCache = tableCache; - } - - count(): number { - return this.tableCache.count(); - } - - iter(): Iterable { - return this.tableCache.iter(); - } - /** - * Access to the `ownerId` unique index on the table `player`, - * which allows point queries on the field of the same name - * via the [`PlayerOwnerIdUnique.find`] method. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.player.ownerId().find(...)`. - * - * Get a handle on the `ownerId` unique index on the table `player`. - */ - ownerId = { - // Find the subscribed row whose `ownerId` column value is equal to `colVal`, - // if such a row is present in the client cache. - find: (colVal: string): Player | undefined => { - for (let row of this.tableCache.iter()) { - if (__deepEqual(row.ownerId, colVal)) { - return row; - } - } - }, - }; - - onInsert = (cb: (ctx: EventContext, row: Player) => void) => { - return this.tableCache.onInsert(cb); - }; - - removeOnInsert = (cb: (ctx: EventContext, row: Player) => void) => { - return this.tableCache.removeOnInsert(cb); - }; - - onDelete = (cb: (ctx: EventContext, row: Player) => void) => { - return this.tableCache.onDelete(cb); - }; - - removeOnDelete = (cb: (ctx: EventContext, row: Player) => void) => { - return this.tableCache.removeOnDelete(cb); - }; - - // Updates are only defined for tables with primary keys. - onUpdate = ( - cb: (ctx: EventContext, oldRow: Player, newRow: Player) => void - ) => { - return this.tableCache.onUpdate(cb); - }; - - removeOnUpdate = ( - cb: (ctx: EventContext, onRow: Player, newRow: Player) => void - ) => { - return this.tableCache.removeOnUpdate(cb); - }; -} diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts index d6f04870fa6..5249c4a6be3 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,66 +30,13 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, } from '../../../src/index'; -import { t } from '../../../src/server'; -import { Point } from './point_type'; -// Mark import as potentially unused -declare type __keep_Point = Point; +import Point from './point_type'; -t.object('Player', { - ownerId: t.string(), - name: t.string(), - location: Point.getTypeScriptAlgebraicType(), +export default __t.object('Player', { + ownerId: __t.string(), + name: __t.string(), + location: Point, }); - -const x = t.enum('PlayerEnum', { - foobar: t.f32(), - bazqux: t.string(), - quxfoo: t.bool(), -}); - -export type Player = { - ownerId: string; - name: string; - location: Point; -}; -let _cached_Player_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const Player = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_Player_type_value) return _cached_Player_type_value; - _cached_Player_type_value = __AlgebraicTypeValue.Product({ elements: [] }); - _cached_Player_type_value.value.elements.push( - { name: 'ownerId', algebraicType: __AlgebraicTypeValue.String }, - { name: 'name', algebraicType: __AlgebraicTypeValue.String }, - { name: 'location', algebraicType: Point.getTypeScriptAlgebraicType() } - ); - return _cached_Player_type_value; - }, - - serialize(writer: __BinaryWriter, value: Player): void { - __AlgebraicTypeValue.serializeValue( - writer, - Player.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): Player { - return __AlgebraicTypeValue.deserializeValue( - reader, - Player.getTypeScriptAlgebraicType() - ); - }, -}; - -export default Player; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts index 939de299c8b..3c0f0fd79d3 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/point_type.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,11 +30,11 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - t as __t, } from '../../../src/index'; export default __t.object('Point', { - x: __t.number(), - y: __t.number(), -}); \ No newline at end of file + x: __t.u16(), + y: __t.u16(), +}); diff --git a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts index eaff291a344..239885c6eb7 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,64 +30,13 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, } from '../../../src/index'; -import { UnindexedPlayer } from './unindexed_player_type'; -import { Point } from './point_type'; -// Mark import as potentially unused -declare type __keep_Point = Point; +import Point from './point_type'; -import { - type EventContext, - type Reducer, - RemoteReducers, - RemoteTables, -} from '.'; -declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; - -/** - * Table handle for the table `unindexed_player`. - * - * Obtain a handle from the [`unindexedPlayer`] property on [`RemoteTables`], - * like `ctx.db.unindexedPlayer`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.unindexedPlayer.on_insert(...)`. - */ -export class UnindexedPlayerTableHandle - implements __TableHandle -{ - // phantom type to track the table name - readonly tableName!: TableName; - tableCache: __TableCache; - - constructor(tableCache: __TableCache) { - this.tableCache = tableCache; - } - - count(): number { - return this.tableCache.count(); - } - - iter(): Iterable { - return this.tableCache.iter(); - } - - onInsert = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => { - return this.tableCache.onInsert(cb); - }; - - removeOnInsert = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => { - return this.tableCache.removeOnInsert(cb); - }; - - onDelete = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => { - return this.tableCache.onDelete(cb); - }; - - removeOnDelete = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => { - return this.tableCache.removeOnDelete(cb); - }; -} +export default __t.row({ + ownerId: __t.string(), + name: __t.string(), + location: Point, +}); diff --git a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts index 8f2545fa8be..c27daf013e7 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,56 +30,13 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, } from '../../../src/index'; -import { Point } from './point_type'; -// Mark import as potentially unused -declare type __keep_Point = Point; +import Point from './point_type'; -export type UnindexedPlayer = { - ownerId: string; - name: string; - location: Point; -}; -let _cached_UnindexedPlayer_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const UnindexedPlayer = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_UnindexedPlayer_type_value) - return _cached_UnindexedPlayer_type_value; - _cached_UnindexedPlayer_type_value = __AlgebraicTypeValue.Product({ - elements: [], - }); - _cached_UnindexedPlayer_type_value.value.elements.push( - { name: 'ownerId', algebraicType: __AlgebraicTypeValue.String }, - { name: 'name', algebraicType: __AlgebraicTypeValue.String }, - { name: 'location', algebraicType: Point.getTypeScriptAlgebraicType() } - ); - return _cached_UnindexedPlayer_type_value; - }, - - serialize(writer: __BinaryWriter, value: UnindexedPlayer): void { - __AlgebraicTypeValue.serializeValue( - writer, - UnindexedPlayer.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): UnindexedPlayer { - return __AlgebraicTypeValue.deserializeValue( - reader, - UnindexedPlayer.getTypeScriptAlgebraicType() - ); - }, -}; - -export default UnindexedPlayer; +export default __t.object('UnindexedPlayer', { + ownerId: __t.string(), + name: __t.string(), + location: Point, +}); diff --git a/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts index e8364a92e8f..29fa7652d66 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/user_table.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,93 +30,11 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, } from '../../../src/index'; -import { User } from './user_type'; -import { - type EventContext, - type Reducer, - RemoteReducers, - RemoteTables, -} from '.'; -declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; - -/** - * Table handle for the table `user`. - * - * Obtain a handle from the [`user`] property on [`RemoteTables`], - * like `ctx.db.user`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.user.on_insert(...)`. - */ -export class UserTableHandle - implements __TableHandle -{ - // phantom type to track the table name - readonly tableName!: TableName; - tableCache: __TableCache; - - constructor(tableCache: __TableCache) { - this.tableCache = tableCache; - } - - count(): number { - return this.tableCache.count(); - } - - iter(): Iterable { - return this.tableCache.iter(); - } - /** - * Access to the `identity` unique index on the table `user`, - * which allows point queries on the field of the same name - * via the [`UserIdentityUnique.find`] method. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.user.identity().find(...)`. - * - * Get a handle on the `identity` unique index on the table `user`. - */ - identity = { - // Find the subscribed row whose `identity` column value is equal to `colVal`, - // if such a row is present in the client cache. - find: (colVal: __Identity): User | undefined => { - for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, colVal)) { - return row; - } - } - }, - }; - - onInsert = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.onInsert(cb); - }; - - removeOnInsert = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.removeOnInsert(cb); - }; - - onDelete = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.onDelete(cb); - }; - - removeOnDelete = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.removeOnDelete(cb); - }; - - // Updates are only defined for tables with primary keys. - onUpdate = (cb: (ctx: EventContext, oldRow: User, newRow: User) => void) => { - return this.tableCache.onUpdate(cb); - }; - removeOnUpdate = ( - cb: (ctx: EventContext, onRow: User, newRow: User) => void - ) => { - return this.tableCache.removeOnUpdate(cb); - }; -} +export default __t.row({ + identity: __t.identity(), + username: __t.string(), +}); diff --git a/crates/bindings-typescript/test-app/src/module_bindings/user_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/user_type.ts index 6fa4066b314..3a4ccf1be08 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/user_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/user_type.ts @@ -8,15 +8,21 @@ import { BinaryReader as __BinaryReader, BinaryWriter as __BinaryWriter, ClientCache as __ClientCache, + ClientTable as __ClientTable, ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, + DbConnectionConfig as __DbConnectionConfig, DbConnectionImpl as __DbConnectionImpl, Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, TimeDuration as __TimeDuration, Timestamp as __Timestamp, deepEqual as __deepEqual, + reducerSchema as __reducerSchema, + reducers as __reducers, + schema as __schema, + t as __t, + table as __table, type AlgebraicType as __AlgebraicTypeType, type AlgebraicTypeVariants as __AlgebraicTypeVariants, type CallReducerFlags as __CallReducerFlags, @@ -24,51 +30,11 @@ import { type Event as __Event, type EventContextInterface as __EventContextInterface, type ReducerEventContextInterface as __ReducerEventContextInterface, + type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, } from '../../../src/index'; -export type User = { - identity: __Identity; - username: string; -}; -let _cached_User_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const User = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_User_type_value) return _cached_User_type_value; - _cached_User_type_value = __AlgebraicTypeValue.Product({ elements: [] }); - _cached_User_type_value.value.elements.push( - { - name: 'identity', - algebraicType: __AlgebraicTypeValue.createIdentityType(), - }, - { name: 'username', algebraicType: __AlgebraicTypeValue.String } - ); - return _cached_User_type_value; - }, - - serialize(writer: __BinaryWriter, value: User): void { - __AlgebraicTypeValue.serializeValue( - writer, - User.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): User { - return __AlgebraicTypeValue.deserializeValue( - reader, - User.getTypeScriptAlgebraicType() - ); - }, -}; - -export default User; +export default __t.object('User', { + identity: __t.identity(), + username: __t.string(), +}); diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index dd3f0c8b871..4a398e7234e 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -1,5 +1,5 @@ use crate::util::{ - is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, iter_types, iter_unique_cols, print_auto_generated_version_comment + is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, iter_types, print_auto_generated_version_comment }; use crate::{OutputFile}; @@ -15,7 +15,6 @@ use spacetimedb_lib::sats::AlgebraicTypeRef; use spacetimedb_primitives::ColId; use spacetimedb_schema::def::{BTreeAlgorithm, IndexAlgorithm, ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; use spacetimedb_schema::identifier::Identifier; -use spacetimedb_schema::schema::{Schema, TableSchema}; use spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse, ProductTypeDef}; use super::code_indenter::{CodeIndenter, Indenter}; @@ -124,21 +123,12 @@ impl Lang for TypeScript { /// })) /// ``` fn generate_table_file(&self, module: &ModuleDef, table: &TableDef) -> OutputFile { - let schema = TableSchema::from_module_def(module, table, (), 0.into()) - .validated() - .expect("Failed to generate table due to validation errors"); - let mut output = CodeIndenter::new(String::new(), INDENT); let out = &mut output; print_file_header(out, false); let type_ref = table.product_type_ref; - let row_type = type_ref_name(module, type_ref); - let row_type_module = type_ref_module_name(module, type_ref); - - writeln!(out, "import {{ {row_type} }} from \"./{row_type_module}\";"); - let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap(); // Import the types of all fields. @@ -154,65 +144,11 @@ impl Lang for TypeScript { writeln!(out); - writeln!(out, "export default table({{"); - out.indent(1); - writeln!( - out, - "name: '{}',", - table.name.deref() - ); - writeln!(out, "indexes: ["); + writeln!(out, "export default __t.row({{"); out.indent(1); - for index_def in iter_indexes(table) { - if !index_def.generated() { - // Skip system-defined indexes - continue; - } - match &index_def.algorithm { - IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => { - let get_name_and_type = |col_pos: ColId| { - let (field_name, field_type) = &product_def.elements[col_pos.idx()]; - let name_camel = field_name.deref().to_case(Case::Camel); - (name_camel, field_type) - }; - writeln!(out, "{{ name: '{}', algorithm: 'btree', columns: [", index_def.name); - out.indent(1); - for col_id in columns.iter() { - writeln!(out, "'{}',", get_name_and_type(col_id).0); - } - out.dedent(1); - writeln!(out, "] }},"); - } - IndexAlgorithm::Direct(_) => { - // Direct indexes are not implemented yet. - continue; - } - _ => todo!(), - }; - } + write_object_type_builder_fields(module, out, &product_def.elements, true).unwrap(); out.dedent(1); - writeln!(out, "}}, {{"); - out.indent(1); - for (field_ident, field_ty) in &product_def.elements { - let field_name = field_ident.deref().to_case(Case::Camel); - write!(out, "{field_name}: "); - write_type(module, out, field_ty, None, None).unwrap(); - - let mut annotations = Vec::new(); - if schema.pk().map(|pk| *field_ident == Identifier::new(pk.col_name.clone()).unwrap()).unwrap_or(false) { - annotations.push("primaryKey()"); - } else { - for (unique_field_ident, _) in - iter_unique_cols(module.typespace_for_generate(), &schema, product_def) - { - if field_ident == unique_field_ident { - annotations.push("unique()"); - } - } - } - } writeln!(out, "}});"); - out.dedent(1); OutputFile { filename: table_module_name(&table.name) + ".ts", code: output.into_inner(), @@ -236,9 +172,7 @@ impl Lang for TypeScript { None, ); - let args_type = reducer_args_type_name(&reducer.name); - - define_body_for_product(module, out, &args_type, &reducer.params_for_generate.elements); + define_body_for_reducer(module, out, &reducer.params_for_generate.elements); OutputFile { filename: reducer_module_name(&reducer.name) + ".ts", @@ -254,78 +188,6 @@ impl Lang for TypeScript { out.newline(); - // const tablesSchema = schema( -// table({ name: 'player', }, t.row({ -// ownerId: t.string(), -// name: t.string(), -// location: pointType, -// })), -// table({ name: 'unindexed_player', }, t.row({ -// ownerId: t.string(), -// name: t.string(), -// location: pointType, -// })), -// table({ name: 'user', primaryKey: 'identity', }, t.row({ -// identity: t.string(), -// name: t.string(), -// })), -// ); - -// const reducersSchema = reducers( -// reducerSchema('create_player', { -// name: t.string(), -// location: pointType, -// }), -// reducerSchema('foo_bar', { -// name: t.string(), -// location: pointType, -// }), -// ); - -// const REMOTE_MODULE = { -// versionInfo: { -// cliVersion: '1.6.0' as const, -// }, -// tables: tablesSchema.schemaType.tables, -// reducers: reducersSchema.reducersType.reducers, -// } satisfies RemoteModule< -// typeof tablesSchema.schemaType, -// typeof reducersSchema.reducersType -// >; - -// export type EventContext = __EventContextInterface< -// typeof REMOTE_MODULE -// >; - -// export type ReducerEventContext = __ReducerEventContextInterface< -// typeof REMOTE_MODULE -// >; - -// export type SubscriptionEventContext = __SubscriptionEventContextInterface< -// typeof REMOTE_MODULE -// >; - -// export type ErrorContext = __ErrorContextInterface< -// typeof REMOTE_MODULE -// >; - -// export class SubscriptionBuilder extends __SubscriptionBuilderImpl< -// typeof REMOTE_MODULE -// > {} - -// export class DbConnectionBuilder extends __DbConnectionBuilder< -// typeof REMOTE_MODULE, -// DbConnection -// > {}; - -// export class DbConnection extends __DbConnectionImpl { -// static builder = (): DbConnectionBuilder => { -// return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config)); -// }; -// subscriptionBuilder = (): SubscriptionBuilder => { -// return new SubscriptionBuilder(this); -// }; - writeln!(out, "// Import and reexport all reducer arg types"); for reducer in iter_reducers(module) { let reducer_name = &reducer.name; @@ -341,8 +203,10 @@ impl Lang for TypeScript { let table_name = &table.name; let table_module_name = table_module_name(table_name) + ".ts"; let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - writeln!(out, "import {table_name_pascalcase} from \"./{table_module_name}\";"); - writeln!(out, "export {{ {table_name_pascalcase} }};"); + // TODO: This really shouldn't be necessary. We could also have `table()` accept + // `__t.object(...)`s. + writeln!(out, "import {table_name_pascalcase}Row from \"./{table_module_name}\";"); + writeln!(out, "export {{ {table_name_pascalcase}Row }};"); } writeln!(out); @@ -357,18 +221,23 @@ impl Lang for TypeScript { out.newline(); writeln!(out); - writeln!(out, "const tablesSchema = schema("); + writeln!(out, "const tablesSchema = __schema("); out.indent(1); for table in iter_tables(module) { - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - writeln!(out, "{},", table_name_pascalcase); + let type_ref = table.product_type_ref; + let row_type_name = type_ref_name(module, type_ref); + writeln!(out, "__table({{"); + out.indent(1); + write_table_opts(module, out, table); + out.dedent(1); + writeln!(out, "}}, {}Row),", row_type_name); } out.dedent(1); writeln!(out, ");"); writeln!(out); - writeln!(out, "const reducersSchema = reducers("); + writeln!(out, "const reducersSchema = __reducers("); out.indent(1); for reducer in iter_reducers(module) { if !is_reducer_invokable(reducer) { @@ -377,7 +246,7 @@ impl Lang for TypeScript { } let reducer_name = &reducer.name; let args_type = reducer_args_type_name(&reducer.name); - writeln!(out, "reducerSchema(\"{}\", {}),", reducer_name, args_type); + writeln!(out, "__reducerSchema(\"{}\", {}),", reducer_name, args_type); } out.dedent(1); writeln!(out, ");"); @@ -451,7 +320,7 @@ impl Lang for TypeScript { out.indent(1); writeln!( out, - "return new DbConnectionBuilder(REMOTE_MODULE, (config: DbConnectionConfig) => new DbConnection(config));" + "return new DbConnectionBuilder(REMOTE_MODULE, (config: __DbConnectionConfig) => new DbConnection(config));" ); out.dedent(1); writeln!(out, "}};"); @@ -492,9 +361,9 @@ fn print_spacetimedb_imports(out: &mut Indenter) { "type CallReducerFlags as __CallReducerFlags", "type EventContextInterface as __EventContextInterface", "type ReducerEventContextInterface as __ReducerEventContextInterface", - "type RemoteModule as __RemoteModule", "type SubscriptionEventContextInterface as __SubscriptionEventContextInterface", "type ErrorContextInterface as __ErrorContextInterface", + "type RemoteModule as __RemoteModule", "SubscriptionBuilderImpl as __SubscriptionBuilderImpl", "BinaryReader as __BinaryReader", "DbConnectionImpl as __DbConnectionImpl", @@ -505,7 +374,6 @@ fn print_spacetimedb_imports(out: &mut Indenter) { "reducers as __reducers", "reducerSchema as __reducerSchema", "DbConnectionConfig as __DbConnectionConfig", - "RemoteModule as __RemoteModule", "t as __t", ]; types.sort(); @@ -532,6 +400,29 @@ fn print_lint_suppression(output: &mut Indenter) { writeln!(output, "/* tslint:disable */"); } +/// e.g. +/// ```ts +/// export default { +/// x: __t.f32(), +/// y: __t.f32(), +/// fooBar: __t.string(), +/// }; +/// ``` +fn define_body_for_reducer( + module: &ModuleDef, + out: &mut Indenter, + params: &[(Identifier, AlgebraicTypeUse)], +) { + write!(out, "export default {{"); + if params.is_empty() { + writeln!(out, "}};"); + } else { + writeln!(out); + out.with_indent(|out| write_object_type_builder_fields(module, out, params, true).unwrap()); + writeln!(out, "}};"); + } +} + /// e.g. /// ```ts /// export default __t.object('Point', { @@ -549,15 +440,56 @@ fn define_body_for_product( write!(out, "export default __t.object(\"{name}\", {{"); if elements.is_empty() { - writeln!(out, "}};"); + writeln!(out, "}});"); } else { writeln!(out); out.with_indent(|out| write_object_type_builder_fields(module, out, elements, true).unwrap()); - writeln!(out, "}};"); + writeln!(out, "}});"); } out.newline(); } +fn write_table_opts(module: &ModuleDef, out: &mut Indenter, table: &TableDef) { + let type_ref = table.product_type_ref; + let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap(); + writeln!( + out, + "name: '{}',", + table.name.deref() + ); + writeln!(out, "indexes: ["); + out.indent(1); + for index_def in iter_indexes(table) { + if !index_def.generated() { + // Skip system-defined indexes + continue; + } + match &index_def.algorithm { + IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => { + let get_name_and_type = |col_pos: ColId| { + let (field_name, field_type) = &product_def.elements[col_pos.idx()]; + let name_camel = field_name.deref().to_case(Case::Camel); + (name_camel, field_type) + }; + writeln!(out, "{{ name: '{}', algorithm: 'btree', columns: [", index_def.name); + out.indent(1); + for col_id in columns.iter() { + writeln!(out, "'{}',", get_name_and_type(col_id).0); + } + out.dedent(1); + writeln!(out, "] }},"); + } + IndexAlgorithm::Direct(_) => { + // Direct indexes are not implemented yet. + continue; + } + _ => todo!(), + }; + } + out.dedent(1); + writeln!(out, "],"); +} + /// e.g. /// ```ts /// x: __t.f32(), @@ -838,14 +770,10 @@ fn print_imports(module: &ModuleDef, out: &mut Indenter, imports: Imports, suffi if let Some(suffix) = suffix { writeln!( out, - "import {{ {type_name} as {type_name}{suffix} }} from \"./{module_name}\";" + "import {type_name}{suffix} from \"./{module_name}\";" ); - writeln!(out, "// Mark import as potentially unused"); - writeln!(out, "declare type __keep_{type_name}{suffix} = {type_name}{suffix};"); } else { - writeln!(out, "import {{ {type_name} }} from \"./{module_name}\";"); - writeln!(out, "// Mark import as potentially unused"); - writeln!(out, "declare type __keep_{type_name} = {type_name};"); + writeln!(out, "import {type_name} from \"./{module_name}\";"); } } } From ae3556ed584ab41f29187826c0fac8dc54479dc3 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 4 Nov 2025 20:41:46 -0500 Subject: [PATCH 08/23] Fixed many small issues, now begin the AlgebraicType stuff --- .../src/lib/reducer_schema.ts | 38 ++ .../bindings-typescript/src/lib/reducers.ts | 49 +- crates/bindings-typescript/src/lib/schema.ts | 15 +- crates/bindings-typescript/src/lib/table.ts | 38 +- .../src/lib/table_schema.ts | 38 ++ .../src/lib/type_builders.ts | 544 +++++++++--------- .../bindings-typescript/src/lib/type_util.ts | 18 +- .../src/react/SpacetimeDBProvider.ts | 15 +- .../src/react/connection_state.ts | 5 +- .../src/react/useReducer.ts | 19 +- .../src/react/useSpacetimeDB.ts | 6 +- .../bindings-typescript/src/react/useTable.ts | 103 ++-- .../src/sdk/client_cache.ts | 14 +- .../src/sdk/client_table.ts | 79 ++- .../src/sdk/db_connection_builder.ts | 9 +- .../src/sdk/db_connection_impl.ts | 21 +- crates/bindings-typescript/src/sdk/db_view.ts | 2 +- crates/bindings-typescript/src/sdk/event.ts | 4 +- .../src/sdk/event_context.ts | 6 +- crates/bindings-typescript/src/sdk/index.ts | 2 +- .../src/sdk/reducer_event.ts | 4 +- .../bindings-typescript/src/sdk/reducers.ts | 18 +- .../src/sdk/table_cache.ts | 45 +- .../bindings-typescript/test-app/src/App.tsx | 23 +- .../module_bindings/create_player_reducer.ts | 14 +- .../test-app/src/module_bindings/index.ts | 24 +- .../src/module_bindings/player_table.ts | 14 +- .../src/module_bindings/player_type.ts | 14 +- .../src/module_bindings/point_type.ts | 14 +- .../module_bindings/unindexed_player_table.ts | 14 +- .../module_bindings/unindexed_player_type.ts | 14 +- .../src/module_bindings/user_table.ts | 14 +- .../test-app/src/module_bindings/user_type.ts | 14 +- crates/codegen/src/typescript.rs | 21 +- 34 files changed, 631 insertions(+), 641 deletions(-) create mode 100644 crates/bindings-typescript/src/lib/reducer_schema.ts create mode 100644 crates/bindings-typescript/src/lib/table_schema.ts diff --git a/crates/bindings-typescript/src/lib/reducer_schema.ts b/crates/bindings-typescript/src/lib/reducer_schema.ts new file mode 100644 index 00000000000..1d0b2f71221 --- /dev/null +++ b/crates/bindings-typescript/src/lib/reducer_schema.ts @@ -0,0 +1,38 @@ +import type { ProductType } from "./algebraic_type"; +import type RawReducerDefV9 from "./autogen/raw_reducer_def_v_9_type"; +import type { ParamsObj } from "./reducers"; +import type { Infer, RowBuilder, RowObj } from "./type_builders"; +import type { CamelCase } from "./type_util"; + +/** + * Represents a handle to a database reducer, including its name and argument type. + */ +export type ReducerSchema< + ReducerName extends string, + Params extends ParamsObj | RowObj, +> = { + /** + * The name of the reducer. + */ + readonly reducerName: ReducerName; + + /** + * The accessor name for the reducer. + */ + readonly accessorName: CamelCase; + + /** + * The TypeBuilder representation of the reducer's parameter type. + */ + readonly params: RowBuilder; + + /** + * The {@link ProductType} representing the structure of the reducer's parameters. + */ + readonly paramsSpacetimeType: ProductType; + + /** + * The {@link RawReducerDefV9} of the configured reducer. + */ + readonly reducerDef: RawReducerDefV9; +}; \ No newline at end of file diff --git a/crates/bindings-typescript/src/lib/reducers.ts b/crates/bindings-typescript/src/lib/reducers.ts index 64f54c3d961..7e2835df33c 100644 --- a/crates/bindings-typescript/src/lib/reducers.ts +++ b/crates/bindings-typescript/src/lib/reducers.ts @@ -7,12 +7,17 @@ import type { Timestamp } from './timestamp'; import type { UntypedReducersDef } from '../sdk/reducers'; import type { DbView } from '../server/db_view'; import { MODULE_DEF, type UntypedSchemaDef } from './schema'; -import type { - InferTypeOfRow, +import { RowBuilder, - RowObj, - TypeBuilder, + type Infer, + type InferTypeOfRow, + type ProductBuilder, + type RowObj, + type TypeBuilder, } from './type_builders'; +import type { CamelCase } from './type_util'; +import type { ReducerSchema } from './reducer_schema'; +import { toCamelCase } from './utils'; /** * Helper to extract the parameter types from an object type @@ -259,34 +264,6 @@ export function clientDisconnected< pushReducer(name, params, fn, Lifecycle.OnDisconnect); } -/** - * Represents a handle to a database reducer, including its name and argument type. - */ -export type ReducerSchema< - ReducerName extends string, - Params extends ParamsObj | RowObj, -> = { - /** - * The name of the reducer. - */ - readonly reducerName: ReducerName; - - /** - * The type of the parameters object expected by the reducer. - */ - readonly paramsType: Params; - - /** - * - */ - readonly paramsSpacetimeType: ProductType; - - /** - * The {@link RawReducerDefV9} of the configured reducer. - */ - readonly reducerDef: RawReducerDefV9; -}; - class Reducers { /** * Phantom type to track the reducers definition @@ -302,8 +279,9 @@ type ReducersToSchema[]> = { /** @type {UntypedReducerDef} */ readonly [i in keyof T]: { name: T[i]['reducerName']; - params: T[i]['paramsType']; - paramsSpacetimeType: T[i]['paramsSpacetimeType']; + accessorName: T[i]['accessorName']; + params: T[i]['params']['row']; + paramsType: T[i]['paramsSpacetimeType']; }; }; }; @@ -356,7 +334,8 @@ export function reducerSchema< }; return { reducerName: name, - paramsType: params, + accessorName: toCamelCase(name), + params: new RowBuilder(params), paramsSpacetimeType: paramType, reducerDef: { name, diff --git a/crates/bindings-typescript/src/lib/schema.ts b/crates/bindings-typescript/src/lib/schema.ts index 1a33c11473d..16fe41680d7 100644 --- a/crates/bindings-typescript/src/lib/schema.ts +++ b/crates/bindings-typescript/src/lib/schema.ts @@ -7,7 +7,7 @@ import { // eslint-disable-next-line @typescript-eslint/no-unused-vars type TypeBuilder, } from './type_builders'; -import type { TableSchema, UntypedTableDef } from './table'; +import type { UntypedTableDef } from './table'; import { clientConnected, clientDisconnected, @@ -23,6 +23,9 @@ import { } from './algebraic_type'; import type RawScopedTypeNameV9 from './autogen/raw_scoped_type_name_v_9_type'; import type { CamelCase } from './type_util'; +import type { TableSchema } from './table_schema'; + +export type TableNamesOf = S['tables'][number]['name']; /** * An untyped representation of the database schema. @@ -363,3 +366,13 @@ export function schema[]>( return new Schema(tableDefs, MODULE_DEF.typespace); } + +type HasAccessor = { accessorName: PropertyKey }; + +export type ConvertToAccessorMap = { + [Tbl in TableDefs[number] as Tbl["accessorName"]]: Tbl +}; + +export function convertToAccessorMap(arr: T): ConvertToAccessorMap { + return Object.fromEntries(arr.map(v => [v.accessorName, v])) as ConvertToAccessorMap; +} diff --git a/crates/bindings-typescript/src/lib/table.ts b/crates/bindings-typescript/src/lib/table.ts index 9bbc3e9e99c..4fe94e545df 100644 --- a/crates/bindings-typescript/src/lib/table.ts +++ b/crates/bindings-typescript/src/lib/table.ts @@ -7,6 +7,7 @@ import type RawTableDefV9 from './autogen/raw_table_def_v_9_type'; import type { AllUnique, ConstraintOpts } from './constraints'; import type { ColumnIndex, IndexColumns, Indexes, IndexOpts, ReadonlyIndexes } from './indexes'; import { MODULE_DEF, splitName } from './schema'; +import type { TableSchema } from './table_schema'; import { ProductBuilder, RowBuilder, @@ -16,7 +17,7 @@ import { type RowObj, type TypeBuilder, } from './type_builders'; -import type { Prettify } from './type_util'; +import type { Prettify, PrettifyDeep } from './type_util'; export type AlgebraicTypeRef = number; type ColId = number; @@ -35,7 +36,7 @@ export type RowType = InferTypeOfRow< export type CoerceColumn< Col extends TypeBuilder | ColumnBuilder, > = - Col extends TypeBuilder ? ColumnBuilder : Col; + Col extends TypeBuilder ? ColumnBuilder> : Col; /** * Coerces a RowObj where TypeBuilders are replaced with ColumnBuilders @@ -55,7 +56,7 @@ type CoerceArray[]> = X; export type UntypedTableDef = { name: string; accessorName: string; - columns: Record>>; + columns: CoerceRow; rowType: ProductType; indexes: IndexOpts[]; }; @@ -149,37 +150,6 @@ export type TableMethods = ReadonlyTableMethod delete(row: RowType): boolean; }; -/** - * Represents a handle to a database table, including its name, row type, and row spacetime type. - */ -export type TableSchema< - TableName extends string, - Row extends Record>, - Idx extends readonly IndexOpts[], -> = { - readonly rowType: RowBuilder; - - /** - * The name of the table. - */ - readonly tableName: TableName; - - /** - * The {@link ProductType} representing the structure of a row in the table. - */ - readonly rowSpacetimeType: ProductType; - - /** - * The {@link RawTableDefV9} of the configured table - */ - readonly tableDef: RawTableDefV9; - - /** - * The indexes defined on the table. - */ - readonly idxs: Idx; -}; - /** * Defines a database table with schema and options * @param opts - Table configuration including name, indexes, and access control diff --git a/crates/bindings-typescript/src/lib/table_schema.ts b/crates/bindings-typescript/src/lib/table_schema.ts new file mode 100644 index 00000000000..a76d05ef4ec --- /dev/null +++ b/crates/bindings-typescript/src/lib/table_schema.ts @@ -0,0 +1,38 @@ +import type { ProductType } from "./algebraic_type"; +import type RawTableDefV9 from "./autogen/raw_table_def_v_9_type"; +import type { IndexOpts } from "./indexes"; +import type { ColumnBuilder, RowBuilder } from "./type_builders"; + +/** + * Represents a handle to a database table, including its name, row type, and row spacetime type. + */ +export type TableSchema< + TableName extends string, + Row extends Record>, + Idx extends readonly IndexOpts[], +> = { + /** + * The name of the table. + */ + readonly tableName: TableName; + + /** + * The TypeBuilder representation of the type of the rows in the table. + **/ + readonly rowType: RowBuilder; + + /** + * The {@link ProductType} representing the structure of a row in the table. + */ + readonly rowSpacetimeType: ProductType; + + /** + * The {@link RawTableDefV9} of the configured table + */ + readonly tableDef: RawTableDefV9; + + /** + * The indexes defined on the table. + */ + readonly idxs: Idx; +}; \ No newline at end of file diff --git a/crates/bindings-typescript/src/lib/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts index 771cce3b188..c046b9a4067 100644 --- a/crates/bindings-typescript/src/lib/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -15,7 +15,7 @@ import { import type { OptionAlgebraicType } from './option'; import { addType, MODULE_DEF } from '../lib/schema'; import type { CoerceRow } from './table'; -import { set, type Set } from './type_util'; +import { set, type SetField } from './type_util'; /** * Helper type to extract the TypeScript type from a TypeBuilder @@ -54,7 +54,7 @@ type CollapseColumn< */ export type RowObj = Record< string, - TypeBuilder | ColumnBuilder + TypeBuilder | ColumnBuilder> >; /** @@ -199,7 +199,7 @@ interface PrimaryKeyable< primaryKey(): ColumnBuilder< Type, SpacetimeType, - Set + SetField >; } @@ -228,7 +228,7 @@ interface Uniqueable< /** * Specify this column as unique */ - unique(): ColumnBuilder>; + unique(): ColumnBuilder>; } /** @@ -256,10 +256,10 @@ interface Indexable< * Specify the index type for this column * @param algorithm The index algorithm to use */ - index(): ColumnBuilder>; + index(): ColumnBuilder>; index>( algorithm: N - ): ColumnBuilder>; + ): ColumnBuilder>; } /** @@ -290,7 +290,7 @@ interface AutoIncrementable< autoInc(): ColumnBuilder< Type, SpacetimeType, - Set + SetField >; } @@ -344,7 +344,7 @@ interface Defaultable< */ default( value: Type - ): ColumnBuilder>; + ): ColumnBuilder>; } export class U8Builder @@ -359,28 +359,28 @@ export class U8Builder constructor() { super(AlgebraicType.U8); } - index(): U8ColumnBuilder>; + index(): U8ColumnBuilder>; index>( algorithm: N - ): U8ColumnBuilder>; + ): U8ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U8ColumnBuilder> { + ): U8ColumnBuilder> { return new U8ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): U8ColumnBuilder> { + unique(): U8ColumnBuilder> { return new U8ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U8ColumnBuilder> { + primaryKey(): U8ColumnBuilder> { return new U8ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U8ColumnBuilder> { + autoInc(): U8ColumnBuilder> { return new U8ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -388,7 +388,7 @@ export class U8Builder } default( value: number - ): U8ColumnBuilder> { + ): U8ColumnBuilder> { return new U8ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -408,28 +408,28 @@ export class U16Builder constructor() { super(AlgebraicType.U16); } - index(): U16ColumnBuilder>; + index(): U16ColumnBuilder>; index>( algorithm: N - ): U16ColumnBuilder>; + ): U16ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U16ColumnBuilder> { + ): U16ColumnBuilder> { return new U16ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): U16ColumnBuilder> { + unique(): U16ColumnBuilder> { return new U16ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U16ColumnBuilder> { + primaryKey(): U16ColumnBuilder> { return new U16ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U16ColumnBuilder> { + autoInc(): U16ColumnBuilder> { return new U16ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -437,7 +437,7 @@ export class U16Builder } default( value: number - ): U16ColumnBuilder> { + ): U16ColumnBuilder> { return new U16ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -457,28 +457,28 @@ export class U32Builder constructor() { super(AlgebraicType.U32); } - index(): U32ColumnBuilder>; + index(): U32ColumnBuilder>; index>( algorithm: N - ): U32ColumnBuilder>; + ): U32ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U32ColumnBuilder> { + ): U32ColumnBuilder> { return new U32ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): U32ColumnBuilder> { + unique(): U32ColumnBuilder> { return new U32ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U32ColumnBuilder> { + primaryKey(): U32ColumnBuilder> { return new U32ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U32ColumnBuilder> { + autoInc(): U32ColumnBuilder> { return new U32ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -486,7 +486,7 @@ export class U32Builder } default( value: number - ): U32ColumnBuilder> { + ): U32ColumnBuilder> { return new U32ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -506,28 +506,28 @@ export class U64Builder constructor() { super(AlgebraicType.U64); } - index(): U64ColumnBuilder>; + index(): U64ColumnBuilder>; index>( algorithm: N - ): U64ColumnBuilder>; + ): U64ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U64ColumnBuilder> { + ): U64ColumnBuilder> { return new U64ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): U64ColumnBuilder> { + unique(): U64ColumnBuilder> { return new U64ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U64ColumnBuilder> { + primaryKey(): U64ColumnBuilder> { return new U64ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U64ColumnBuilder> { + autoInc(): U64ColumnBuilder> { return new U64ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -535,7 +535,7 @@ export class U64Builder } default( value: bigint - ): U64ColumnBuilder> { + ): U64ColumnBuilder> { return new U64ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -555,31 +555,31 @@ export class U128Builder constructor() { super(AlgebraicType.U128); } - index(): U128ColumnBuilder>; + index(): U128ColumnBuilder>; index>( algorithm: N - ): U128ColumnBuilder>; + ): U128ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U128ColumnBuilder> { + ): U128ColumnBuilder> { return new U128ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): U128ColumnBuilder> { + unique(): U128ColumnBuilder> { return new U128ColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): U128ColumnBuilder> { + primaryKey(): U128ColumnBuilder> { return new U128ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U128ColumnBuilder> { + autoInc(): U128ColumnBuilder> { return new U128ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -587,7 +587,7 @@ export class U128Builder } default( value: bigint - ): U128ColumnBuilder> { + ): U128ColumnBuilder> { return new U128ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -607,31 +607,31 @@ export class U256Builder constructor() { super(AlgebraicType.U256); } - index(): U256ColumnBuilder>; + index(): U256ColumnBuilder>; index>( algorithm: N - ): U256ColumnBuilder>; + ): U256ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U256ColumnBuilder> { + ): U256ColumnBuilder> { return new U256ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): U256ColumnBuilder> { + unique(): U256ColumnBuilder> { return new U256ColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): U256ColumnBuilder> { + primaryKey(): U256ColumnBuilder> { return new U256ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U256ColumnBuilder> { + autoInc(): U256ColumnBuilder> { return new U256ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -639,7 +639,7 @@ export class U256Builder } default( value: bigint - ): U256ColumnBuilder> { + ): U256ColumnBuilder> { return new U256ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -659,28 +659,28 @@ export class I8Builder constructor() { super(AlgebraicType.I8); } - index(): I8ColumnBuilder>; + index(): I8ColumnBuilder>; index>( algorithm: N - ): I8ColumnBuilder>; + ): I8ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I8ColumnBuilder> { + ): I8ColumnBuilder> { return new I8ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): I8ColumnBuilder> { + unique(): I8ColumnBuilder> { return new I8ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I8ColumnBuilder> { + primaryKey(): I8ColumnBuilder> { return new I8ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I8ColumnBuilder> { + autoInc(): I8ColumnBuilder> { return new I8ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -688,7 +688,7 @@ export class I8Builder } default( value: number - ): I8ColumnBuilder> { + ): I8ColumnBuilder> { return new I8ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -708,28 +708,28 @@ export class I16Builder constructor() { super(AlgebraicType.I16); } - index(): I16ColumnBuilder>; + index(): I16ColumnBuilder>; index>( algorithm: N - ): I16ColumnBuilder>; + ): I16ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I16ColumnBuilder> { + ): I16ColumnBuilder> { return new I16ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): I16ColumnBuilder> { + unique(): I16ColumnBuilder> { return new I16ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I16ColumnBuilder> { + primaryKey(): I16ColumnBuilder> { return new I16ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I16ColumnBuilder> { + autoInc(): I16ColumnBuilder> { return new I16ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -737,7 +737,7 @@ export class I16Builder } default( value: number - ): I16ColumnBuilder> { + ): I16ColumnBuilder> { return new I16ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -758,28 +758,28 @@ export class I32Builder constructor() { super(AlgebraicType.I32); } - index(): I32ColumnBuilder>; + index(): I32ColumnBuilder>; index>( algorithm: N - ): I32ColumnBuilder>; + ): I32ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I32ColumnBuilder> { + ): I32ColumnBuilder> { return new I32ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): I32ColumnBuilder> { + unique(): I32ColumnBuilder> { return new I32ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I32ColumnBuilder> { + primaryKey(): I32ColumnBuilder> { return new I32ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I32ColumnBuilder> { + autoInc(): I32ColumnBuilder> { return new I32ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -787,7 +787,7 @@ export class I32Builder } default( value: number - ): I32ColumnBuilder> { + ): I32ColumnBuilder> { return new I32ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -807,28 +807,28 @@ export class I64Builder constructor() { super(AlgebraicType.I64); } - index(): I64ColumnBuilder>; + index(): I64ColumnBuilder>; index>( algorithm: N - ): I64ColumnBuilder>; + ): I64ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I64ColumnBuilder> { + ): I64ColumnBuilder> { return new I64ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): I64ColumnBuilder> { + unique(): I64ColumnBuilder> { return new I64ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I64ColumnBuilder> { + primaryKey(): I64ColumnBuilder> { return new I64ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I64ColumnBuilder> { + autoInc(): I64ColumnBuilder> { return new I64ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -836,7 +836,7 @@ export class I64Builder } default( value: bigint - ): I64ColumnBuilder> { + ): I64ColumnBuilder> { return new I64ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -856,31 +856,31 @@ export class I128Builder constructor() { super(AlgebraicType.I128); } - index(): I128ColumnBuilder>; + index(): I128ColumnBuilder>; index>( algorithm: N - ): I128ColumnBuilder>; + ): I128ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I128ColumnBuilder> { + ): I128ColumnBuilder> { return new I128ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): I128ColumnBuilder> { + unique(): I128ColumnBuilder> { return new I128ColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): I128ColumnBuilder> { + primaryKey(): I128ColumnBuilder> { return new I128ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I128ColumnBuilder> { + autoInc(): I128ColumnBuilder> { return new I128ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -888,7 +888,7 @@ export class I128Builder } default( value: bigint - ): I128ColumnBuilder> { + ): I128ColumnBuilder> { return new I128ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -908,31 +908,31 @@ export class I256Builder constructor() { super(AlgebraicType.I256); } - index(): I256ColumnBuilder>; + index(): I256ColumnBuilder>; index>( algorithm: N - ): I256ColumnBuilder>; + ): I256ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I256ColumnBuilder> { + ): I256ColumnBuilder> { return new I256ColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): I256ColumnBuilder> { + unique(): I256ColumnBuilder> { return new I256ColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): I256ColumnBuilder> { + primaryKey(): I256ColumnBuilder> { return new I256ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I256ColumnBuilder> { + autoInc(): I256ColumnBuilder> { return new I256ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -940,7 +940,7 @@ export class I256Builder } default( value: bigint - ): I256ColumnBuilder> { + ): I256ColumnBuilder> { return new I256ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -957,7 +957,7 @@ export class F32Builder } default( value: number - ): F32ColumnBuilder> { + ): F32ColumnBuilder> { return new F32ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -974,7 +974,7 @@ export class F64Builder } default( value: number - ): F64ColumnBuilder> { + ): F64ColumnBuilder> { return new F64ColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -993,25 +993,25 @@ export class BoolBuilder constructor() { super(AlgebraicType.Bool); } - index(): BoolColumnBuilder>; + index(): BoolColumnBuilder>; index>( algorithm: N - ): BoolColumnBuilder>; + ): BoolColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): BoolColumnBuilder> { + ): BoolColumnBuilder> { return new BoolColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): BoolColumnBuilder> { + unique(): BoolColumnBuilder> { return new BoolColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): BoolColumnBuilder> { + primaryKey(): BoolColumnBuilder> { return new BoolColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) @@ -1019,7 +1019,7 @@ export class BoolBuilder } default( value: boolean - ): BoolColumnBuilder> { + ): BoolColumnBuilder> { return new BoolColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1038,26 +1038,26 @@ export class StringBuilder constructor() { super(AlgebraicType.String); } - index(): StringColumnBuilder>; + index(): StringColumnBuilder>; index>( algorithm: N - ): StringColumnBuilder>; + ): StringColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): StringColumnBuilder> { + ): StringColumnBuilder> { return new StringColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): StringColumnBuilder> { + unique(): StringColumnBuilder> { return new StringColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } primaryKey(): StringColumnBuilder< - Set + SetField > { return new StringColumnBuilder( this, @@ -1066,7 +1066,7 @@ export class StringBuilder } default( value: string - ): StringColumnBuilder> { + ): StringColumnBuilder> { return new StringColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1091,7 +1091,7 @@ export class ArrayBuilder> } default( value: Array> - ): ArrayColumnBuilder> { + ): ArrayColumnBuilder> { return new ArrayColumnBuilder( this.element, set(defaultMetadata, { defaultValue: value }) @@ -1125,7 +1125,7 @@ export class OptionBuilder> value: InferTypeOfTypeBuilder | undefined ): OptionColumnBuilder< InferTypeOfTypeBuilder, - Set< + SetField< DefaultMetadata, 'defaultValue', InferTypeOfTypeBuilder | undefined @@ -1166,7 +1166,7 @@ export class ProductBuilder } default( value: ObjectType - ): ProductColumnBuilder> { + ): ProductColumnBuilder> { return new ProductColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1229,7 +1229,7 @@ export class SumBuilder extends TypeBuilder< } default( value: EnumType - ): SumColumnBuilder> { + ): SumColumnBuilder> { return new SumColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1257,16 +1257,16 @@ export class SimpleSumBuilder { index(): SimpleSumColumnBuilder< Variants, - Set + SetField >; index>( algorithm: N - ): SimpleSumColumnBuilder>; + ): SimpleSumColumnBuilder>; index( algorithm: IndexTypes = 'btree' ): SimpleSumColumnBuilder< Variants, - Set + SetField > { return new SimpleSumColumnBuilder( this, @@ -1275,7 +1275,7 @@ export class SimpleSumBuilder } primaryKey(): SimpleSumColumnBuilder< Variants, - Set + SetField > { return new SimpleSumColumnBuilder( this, @@ -1295,26 +1295,26 @@ export class IdentityBuilder constructor() { super(Identity.getAlgebraicType()); } - index(): IdentityColumnBuilder>; + index(): IdentityColumnBuilder>; index>( algorithm: N - ): IdentityColumnBuilder>; + ): IdentityColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): IdentityColumnBuilder> { + ): IdentityColumnBuilder> { return new IdentityColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): IdentityColumnBuilder> { + unique(): IdentityColumnBuilder> { return new IdentityColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } primaryKey(): IdentityColumnBuilder< - Set + SetField > { return new IdentityColumnBuilder( this, @@ -1322,7 +1322,7 @@ export class IdentityBuilder ); } autoInc(): IdentityColumnBuilder< - Set + SetField > { return new IdentityColumnBuilder( this, @@ -1331,7 +1331,7 @@ export class IdentityBuilder } default( value: Identity - ): IdentityColumnBuilder> { + ): IdentityColumnBuilder> { return new IdentityColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1351,27 +1351,27 @@ export class ConnectionIdBuilder super(ConnectionId.getAlgebraicType()); } index(): ConnectionIdColumnBuilder< - Set + SetField >; index>( algorithm: N - ): ConnectionIdColumnBuilder>; + ): ConnectionIdColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): ConnectionIdColumnBuilder> { + ): ConnectionIdColumnBuilder> { return new ConnectionIdColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): ConnectionIdColumnBuilder> { + unique(): ConnectionIdColumnBuilder> { return new ConnectionIdColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } primaryKey(): ConnectionIdColumnBuilder< - Set + SetField > { return new ConnectionIdColumnBuilder( this, @@ -1379,7 +1379,7 @@ export class ConnectionIdBuilder ); } autoInc(): ConnectionIdColumnBuilder< - Set + SetField > { return new ConnectionIdColumnBuilder( this, @@ -1389,7 +1389,7 @@ export class ConnectionIdBuilder default( value: ConnectionId ): ConnectionIdColumnBuilder< - Set + SetField > { return new ConnectionIdColumnBuilder( this, @@ -1409,26 +1409,26 @@ export class TimestampBuilder constructor() { super(Timestamp.getAlgebraicType()); } - index(): TimestampColumnBuilder>; + index(): TimestampColumnBuilder>; index>( algorithm: N - ): TimestampColumnBuilder>; + ): TimestampColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): TimestampColumnBuilder> { + ): TimestampColumnBuilder> { return new TimestampColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): TimestampColumnBuilder> { + unique(): TimestampColumnBuilder> { return new TimestampColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } primaryKey(): TimestampColumnBuilder< - Set + SetField > { return new TimestampColumnBuilder( this, @@ -1436,7 +1436,7 @@ export class TimestampBuilder ); } autoInc(): TimestampColumnBuilder< - Set + SetField > { return new TimestampColumnBuilder( this, @@ -1445,7 +1445,7 @@ export class TimestampBuilder } default( value: Timestamp - ): TimestampColumnBuilder> { + ): TimestampColumnBuilder> { return new TimestampColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1465,27 +1465,27 @@ export class TimeDurationBuilder super(TimeDuration.getAlgebraicType()); } index(): TimeDurationColumnBuilder< - Set + SetField >; index>( algorithm: N - ): TimeDurationColumnBuilder>; + ): TimeDurationColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): TimeDurationColumnBuilder> { + ): TimeDurationColumnBuilder> { return new TimeDurationColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): TimeDurationColumnBuilder> { + unique(): TimeDurationColumnBuilder> { return new TimeDurationColumnBuilder( this, set(defaultMetadata, { isUnique: true }) ); } primaryKey(): TimeDurationColumnBuilder< - Set + SetField > { return new TimeDurationColumnBuilder( this, @@ -1493,7 +1493,7 @@ export class TimeDurationBuilder ); } autoInc(): TimeDurationColumnBuilder< - Set + SetField > { return new TimeDurationColumnBuilder( this, @@ -1503,7 +1503,7 @@ export class TimeDurationBuilder default( value: TimeDuration ): TimeDurationColumnBuilder< - Set + SetField > { return new TimeDurationColumnBuilder( this, @@ -1570,37 +1570,37 @@ export class U8ColumnBuilder = DefaultMetadata> AutoIncrementable, Defaultable { - index(): U8ColumnBuilder>; + index(): U8ColumnBuilder>; index>( algorithm: N - ): U8ColumnBuilder>; + ): U8ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U8ColumnBuilder> { + ): U8ColumnBuilder> { return new U8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): U8ColumnBuilder> { + unique(): U8ColumnBuilder> { return new U8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): U8ColumnBuilder> { + primaryKey(): U8ColumnBuilder> { return new U8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): U8ColumnBuilder> { + autoInc(): U8ColumnBuilder> { return new U8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): U8ColumnBuilder> { + default(value: number): U8ColumnBuilder> { return new U8ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1619,37 +1619,37 @@ export class U16ColumnBuilder< AutoIncrementable, Defaultable { - index(): U16ColumnBuilder>; + index(): U16ColumnBuilder>; index>( algorithm: N - ): U16ColumnBuilder>; + ): U16ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U16ColumnBuilder> { + ): U16ColumnBuilder> { return new U16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): U16ColumnBuilder> { + unique(): U16ColumnBuilder> { return new U16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): U16ColumnBuilder> { + primaryKey(): U16ColumnBuilder> { return new U16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): U16ColumnBuilder> { + autoInc(): U16ColumnBuilder> { return new U16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): U16ColumnBuilder> { + default(value: number): U16ColumnBuilder> { return new U16ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1668,37 +1668,37 @@ export class U32ColumnBuilder< AutoIncrementable, Defaultable { - index(): U32ColumnBuilder>; + index(): U32ColumnBuilder>; index>( algorithm: N - ): U32ColumnBuilder>; + ): U32ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U32ColumnBuilder> { + ): U32ColumnBuilder> { return new U32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): U32ColumnBuilder> { + unique(): U32ColumnBuilder> { return new U32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): U32ColumnBuilder> { + primaryKey(): U32ColumnBuilder> { return new U32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): U32ColumnBuilder> { + autoInc(): U32ColumnBuilder> { return new U32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): U32ColumnBuilder> { + default(value: number): U32ColumnBuilder> { return new U32ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1717,37 +1717,37 @@ export class U64ColumnBuilder< AutoIncrementable, Defaultable { - index(): U64ColumnBuilder>; + index(): U64ColumnBuilder>; index>( algorithm: N - ): U64ColumnBuilder>; + ): U64ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U64ColumnBuilder> { + ): U64ColumnBuilder> { return new U64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): U64ColumnBuilder> { + unique(): U64ColumnBuilder> { return new U64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): U64ColumnBuilder> { + primaryKey(): U64ColumnBuilder> { return new U64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): U64ColumnBuilder> { + autoInc(): U64ColumnBuilder> { return new U64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): U64ColumnBuilder> { + default(value: bigint): U64ColumnBuilder> { return new U64ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1766,37 +1766,37 @@ export class U128ColumnBuilder< AutoIncrementable, Defaultable { - index(): U128ColumnBuilder>; + index(): U128ColumnBuilder>; index>( algorithm: N - ): U128ColumnBuilder>; + ): U128ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U128ColumnBuilder> { + ): U128ColumnBuilder> { return new U128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): U128ColumnBuilder> { + unique(): U128ColumnBuilder> { return new U128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): U128ColumnBuilder> { + primaryKey(): U128ColumnBuilder> { return new U128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): U128ColumnBuilder> { + autoInc(): U128ColumnBuilder> { return new U128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): U128ColumnBuilder> { + default(value: bigint): U128ColumnBuilder> { return new U128ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1815,37 +1815,37 @@ export class U256ColumnBuilder< AutoIncrementable, Defaultable { - index(): U256ColumnBuilder>; + index(): U256ColumnBuilder>; index>( algorithm: N - ): U256ColumnBuilder>; + ): U256ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): U256ColumnBuilder> { + ): U256ColumnBuilder> { return new U256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): U256ColumnBuilder> { + unique(): U256ColumnBuilder> { return new U256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): U256ColumnBuilder> { + primaryKey(): U256ColumnBuilder> { return new U256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): U256ColumnBuilder> { + autoInc(): U256ColumnBuilder> { return new U256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): U256ColumnBuilder> { + default(value: bigint): U256ColumnBuilder> { return new U256ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1862,37 +1862,37 @@ export class I8ColumnBuilder = DefaultMetadata> AutoIncrementable, Defaultable { - index(): I8ColumnBuilder>; + index(): I8ColumnBuilder>; index>( algorithm: N - ): I8ColumnBuilder>; + ): I8ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I8ColumnBuilder> { + ): I8ColumnBuilder> { return new I8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): I8ColumnBuilder> { + unique(): I8ColumnBuilder> { return new I8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): I8ColumnBuilder> { + primaryKey(): I8ColumnBuilder> { return new I8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): I8ColumnBuilder> { + autoInc(): I8ColumnBuilder> { return new I8ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): I8ColumnBuilder> { + default(value: number): I8ColumnBuilder> { return new I8ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1911,37 +1911,37 @@ export class I16ColumnBuilder< AutoIncrementable, Defaultable { - index(): I16ColumnBuilder>; + index(): I16ColumnBuilder>; index>( algorithm: N - ): I16ColumnBuilder>; + ): I16ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I16ColumnBuilder> { + ): I16ColumnBuilder> { return new I16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): I16ColumnBuilder> { + unique(): I16ColumnBuilder> { return new I16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): I16ColumnBuilder> { + primaryKey(): I16ColumnBuilder> { return new I16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): I16ColumnBuilder> { + autoInc(): I16ColumnBuilder> { return new I16ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): I16ColumnBuilder> { + default(value: number): I16ColumnBuilder> { return new I16ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -1960,37 +1960,37 @@ export class I32ColumnBuilder< AutoIncrementable, Defaultable { - index(): I32ColumnBuilder>; + index(): I32ColumnBuilder>; index>( algorithm: N - ): I32ColumnBuilder>; + ): I32ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I32ColumnBuilder> { + ): I32ColumnBuilder> { return new I32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): I32ColumnBuilder> { + unique(): I32ColumnBuilder> { return new I32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): I32ColumnBuilder> { + primaryKey(): I32ColumnBuilder> { return new I32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): I32ColumnBuilder> { + autoInc(): I32ColumnBuilder> { return new I32ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): I32ColumnBuilder> { + default(value: number): I32ColumnBuilder> { return new I32ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2009,37 +2009,37 @@ export class I64ColumnBuilder< AutoIncrementable, Defaultable { - index(): I64ColumnBuilder>; + index(): I64ColumnBuilder>; index>( algorithm: N - ): I64ColumnBuilder>; + ): I64ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I64ColumnBuilder> { + ): I64ColumnBuilder> { return new I64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): I64ColumnBuilder> { + unique(): I64ColumnBuilder> { return new I64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): I64ColumnBuilder> { + primaryKey(): I64ColumnBuilder> { return new I64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): I64ColumnBuilder> { + autoInc(): I64ColumnBuilder> { return new I64ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): I64ColumnBuilder> { + default(value: bigint): I64ColumnBuilder> { return new I64ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2058,37 +2058,37 @@ export class I128ColumnBuilder< AutoIncrementable, Defaultable { - index(): I128ColumnBuilder>; + index(): I128ColumnBuilder>; index>( algorithm: N - ): I128ColumnBuilder>; + ): I128ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I128ColumnBuilder> { + ): I128ColumnBuilder> { return new I128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): I128ColumnBuilder> { + unique(): I128ColumnBuilder> { return new I128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): I128ColumnBuilder> { + primaryKey(): I128ColumnBuilder> { return new I128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): I128ColumnBuilder> { + autoInc(): I128ColumnBuilder> { return new I128ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): I128ColumnBuilder> { + default(value: bigint): I128ColumnBuilder> { return new I128ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2107,37 +2107,37 @@ export class I256ColumnBuilder< AutoIncrementable, Defaultable { - index(): I256ColumnBuilder>; + index(): I256ColumnBuilder>; index>( algorithm: N - ): I256ColumnBuilder>; + ): I256ColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): I256ColumnBuilder> { + ): I256ColumnBuilder> { return new I256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): I256ColumnBuilder> { + unique(): I256ColumnBuilder> { return new I256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): I256ColumnBuilder> { + primaryKey(): I256ColumnBuilder> { return new I256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - autoInc(): I256ColumnBuilder> { + autoInc(): I256ColumnBuilder> { return new I256ColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): I256ColumnBuilder> { + default(value: bigint): I256ColumnBuilder> { return new I256ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2151,7 +2151,7 @@ export class F32ColumnBuilder< extends ColumnBuilder implements Defaultable { - default(value: number): F32ColumnBuilder> { + default(value: number): F32ColumnBuilder> { return new F32ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2165,7 +2165,7 @@ export class F64ColumnBuilder< extends ColumnBuilder implements Defaultable { - default(value: number): F64ColumnBuilder> { + default(value: number): F64ColumnBuilder> { return new F64ColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2183,31 +2183,31 @@ export class BoolColumnBuilder< PrimaryKeyable, Defaultable { - index(): BoolColumnBuilder>; + index(): BoolColumnBuilder>; index>( algorithm: N - ): BoolColumnBuilder>; + ): BoolColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): BoolColumnBuilder> { + ): BoolColumnBuilder> { return new BoolColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): BoolColumnBuilder> { + unique(): BoolColumnBuilder> { return new BoolColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): BoolColumnBuilder> { + primaryKey(): BoolColumnBuilder> { return new BoolColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - default(value: boolean): BoolColumnBuilder> { + default(value: boolean): BoolColumnBuilder> { return new BoolColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2225,31 +2225,31 @@ export class StringColumnBuilder< PrimaryKeyable, Defaultable { - index(): StringColumnBuilder>; + index(): StringColumnBuilder>; index>( algorithm: N - ): StringColumnBuilder>; + ): StringColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): StringColumnBuilder> { + ): StringColumnBuilder> { return new StringColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): StringColumnBuilder> { + unique(): StringColumnBuilder> { return new StringColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): StringColumnBuilder> { + primaryKey(): StringColumnBuilder> { return new StringColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) ); } - default(value: string): StringColumnBuilder> { + default(value: string): StringColumnBuilder> { return new StringColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2278,7 +2278,7 @@ export class ArrayColumnBuilder< value: Array> ): ArrayColumnBuilder< Element, - Set>> + SetField>> > { return new ArrayColumnBuilder(this.typeBuilder, { ...this.columnMetadata, @@ -2305,7 +2305,7 @@ export class OptionColumnBuilder< value: InferTypeOfTypeBuilder | undefined ): OptionColumnBuilder< InferTypeOfTypeBuilder, - Set | undefined> + SetField | undefined> > { return new OptionColumnBuilder(this.typeBuilder, { ...this.columnMetadata, @@ -2330,7 +2330,7 @@ export class ProductColumnBuilder< { default( value: ObjectType - ): ProductColumnBuilder> { + ): ProductColumnBuilder> { return new ProductColumnBuilder( this.typeBuilder, set(this.columnMetadata, { defaultValue: value }) @@ -2351,7 +2351,7 @@ export class SumColumnBuilder< { default( value: EnumType - ): SumColumnBuilder> { + ): SumColumnBuilder> { return new SumColumnBuilder( this.typeBuilder, set(this.columnMetadata, { defaultValue: value }) @@ -2370,16 +2370,16 @@ export class SimpleSumColumnBuilder< { index(): SimpleSumColumnBuilder< Variants, - Set + SetField >; index>( algorithm: N - ): SimpleSumColumnBuilder>; + ): SimpleSumColumnBuilder>; index( algorithm: IndexTypes = 'btree' ): SimpleSumColumnBuilder< Variants, - Set + SetField > { return new SimpleSumColumnBuilder( this.typeBuilder, @@ -2388,7 +2388,7 @@ export class SimpleSumColumnBuilder< } primaryKey(): SimpleSumColumnBuilder< Variants, - Set + SetField > { return new SimpleSumColumnBuilder( this.typeBuilder, @@ -2407,25 +2407,25 @@ export class IdentityColumnBuilder< PrimaryKeyable, Defaultable { - index(): IdentityColumnBuilder>; + index(): IdentityColumnBuilder>; index>( algorithm: N - ): IdentityColumnBuilder>; + ): IdentityColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): IdentityColumnBuilder> { + ): IdentityColumnBuilder> { return new IdentityColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): IdentityColumnBuilder> { + unique(): IdentityColumnBuilder> { return new IdentityColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): IdentityColumnBuilder> { + primaryKey(): IdentityColumnBuilder> { return new IdentityColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) @@ -2433,7 +2433,7 @@ export class IdentityColumnBuilder< } default( value: Identity - ): IdentityColumnBuilder> { + ): IdentityColumnBuilder> { return new IdentityColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2451,25 +2451,25 @@ export class ConnectionIdColumnBuilder< PrimaryKeyable, Defaultable { - index(): ConnectionIdColumnBuilder>; + index(): ConnectionIdColumnBuilder>; index>( algorithm: N - ): ConnectionIdColumnBuilder>; + ): ConnectionIdColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): ConnectionIdColumnBuilder> { + ): ConnectionIdColumnBuilder> { return new ConnectionIdColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): ConnectionIdColumnBuilder> { + unique(): ConnectionIdColumnBuilder> { return new ConnectionIdColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): ConnectionIdColumnBuilder> { + primaryKey(): ConnectionIdColumnBuilder> { return new ConnectionIdColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) @@ -2477,7 +2477,7 @@ export class ConnectionIdColumnBuilder< } default( value: ConnectionId - ): ConnectionIdColumnBuilder> { + ): ConnectionIdColumnBuilder> { return new ConnectionIdColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2495,25 +2495,25 @@ export class TimestampColumnBuilder< PrimaryKeyable, Defaultable { - index(): TimestampColumnBuilder>; + index(): TimestampColumnBuilder>; index>( algorithm: N - ): TimestampColumnBuilder>; + ): TimestampColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): TimestampColumnBuilder> { + ): TimestampColumnBuilder> { return new TimestampColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): TimestampColumnBuilder> { + unique(): TimestampColumnBuilder> { return new TimestampColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): TimestampColumnBuilder> { + primaryKey(): TimestampColumnBuilder> { return new TimestampColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) @@ -2521,7 +2521,7 @@ export class TimestampColumnBuilder< } default( value: Timestamp - ): TimestampColumnBuilder> { + ): TimestampColumnBuilder> { return new TimestampColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, @@ -2539,25 +2539,25 @@ export class TimeDurationColumnBuilder< PrimaryKeyable, Defaultable { - index(): TimeDurationColumnBuilder>; + index(): TimeDurationColumnBuilder>; index>( algorithm: N - ): TimeDurationColumnBuilder>; + ): TimeDurationColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): TimeDurationColumnBuilder> { + ): TimeDurationColumnBuilder> { return new TimeDurationColumnBuilder( this.typeBuilder, set(this.columnMetadata, { indexType: algorithm }) ); } - unique(): TimeDurationColumnBuilder> { + unique(): TimeDurationColumnBuilder> { return new TimeDurationColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isUnique: true }) ); } - primaryKey(): TimeDurationColumnBuilder> { + primaryKey(): TimeDurationColumnBuilder> { return new TimeDurationColumnBuilder( this.typeBuilder, set(this.columnMetadata, { isPrimaryKey: true }) @@ -2565,7 +2565,7 @@ export class TimeDurationColumnBuilder< } default( value: TimeDuration - ): TimeDurationColumnBuilder> { + ): TimeDurationColumnBuilder> { return new TimeDurationColumnBuilder(this.typeBuilder, { ...this.columnMetadata, defaultValue: value, diff --git a/crates/bindings-typescript/src/lib/type_util.ts b/crates/bindings-typescript/src/lib/type_util.ts index ae9e978c7e9..04a33bfd528 100644 --- a/crates/bindings-typescript/src/lib/type_util.ts +++ b/crates/bindings-typescript/src/lib/type_util.ts @@ -3,10 +3,24 @@ */ export type Prettify = { [K in keyof T]: T[K] } & {}; +type Builtin = + | string | number | boolean | symbol | bigint | null | undefined + | Function | Date | RegExp | Error + | Map | Set | WeakMap | WeakSet + | Promise; + +// Deep +export type PrettifyDeep = + T extends Builtin ? T : + T extends readonly [...infer _] ? { [K in keyof T]: PrettifyDeep } : + T extends ReadonlyArray ? ReadonlyArray> : + T extends object ? Prettify<{ [K in keyof T]: PrettifyDeep }> : + T; + /** * Helper function to sets a field in an object */ -export type Set = Prettify< +export type SetField = Prettify< Omit & { [K in F]: V } >; @@ -19,7 +33,7 @@ export type Set = Prettify< export function set( x: T, t: { [k in F]: V } -): Set { +): SetField { return { ...x, ...t }; } diff --git a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts index 0ca560b86ec..c728d424548 100644 --- a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts @@ -12,33 +12,32 @@ import { ConnectionId } from '../lib/connection_id'; import type { UntypedRemoteModule } from '../sdk/spacetime_module'; export interface SpacetimeDBProviderProps< - RemoteModule extends UntypedRemoteModule, - DbConnection extends DbConnectionImpl = DbConnectionImpl, + DbConnection extends DbConnectionImpl, > { - connectionBuilder: DbConnectionBuilder; + connectionBuilder: DbConnectionBuilder; children?: React.ReactNode; } export function SpacetimeDBProvider< DbConnection extends DbConnectionImpl, ->({ connectionBuilder, children }: SpacetimeDBProviderProps>) { +>({ connectionBuilder, children }: SpacetimeDBProviderProps) { // Holds the imperative connection instance when (and only when) we’re on the client. const connRef = React.useRef(null); const getConnection = React.useCallback(() => connRef.current, []); - const [state, setState] = React.useState>({ + const [state, setState] = React.useState({ isActive: false, identity: undefined, token: undefined, connectionId: ConnectionId.random(), connectionError: undefined, - getConnection, + getConnection: getConnection as ConnectionState["getConnection"], }); // Build on the client only; useEffect won't run during SSR. React.useEffect(() => { // Register callback for onConnect to update state - const onConnect = (conn: DbConnectionImpl>) => { + const onConnect = (conn: DbConnection) => { setState(s => ({ ...s, isActive: conn.isActive, @@ -75,7 +74,7 @@ export function SpacetimeDBProvider< // Lazily build once if (!connRef.current) { - connRef.current = connectionBuilder.build() as unknown as DbConnection; + connRef.current = connectionBuilder.build(); } return () => { diff --git a/crates/bindings-typescript/src/react/connection_state.ts b/crates/bindings-typescript/src/react/connection_state.ts index 6d63e91d693..f9c333c100f 100644 --- a/crates/bindings-typescript/src/react/connection_state.ts +++ b/crates/bindings-typescript/src/react/connection_state.ts @@ -1,13 +1,12 @@ import type { ConnectionId } from "../lib/connection_id"; import type { Identity } from "../lib/identity"; import type { DbConnectionImpl } from "../sdk/db_connection_impl"; -import type { UntypedRemoteModule } from "../sdk/spacetime_module"; -export type ConnectionState> = { +export type ConnectionState = { isActive: boolean; identity?: Identity; token?: string; connectionId: ConnectionId; connectionError?: Error; - getConnection(): DbConnection | null; + getConnection>(): DbConnection | null; }; diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index 06eb76da311..ee21aceb8c3 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -1,16 +1,15 @@ -import type { DbConnectionImpl, RemoteModuleOf } from "../sdk/db_connection_impl"; -import type { ReducersView } from "../sdk/reducers"; -import type { UntypedRemoteModule } from "../sdk/spacetime_module"; +import type { InferTypeOfRow } from "../lib/type_builders"; +import type { PrettifyDeep } from "../lib/type_util"; +import type { UntypedReducerDef } from "../sdk/reducers"; import { useSpacetimeDB, } from "./useSpacetimeDB"; - export function useReducer< - DbConnection extends DbConnectionImpl, - ReducerName extends keyof ReducersView> = keyof ReducersView> + ReducerDef extends UntypedReducerDef >( - reducerName: ReducerName -): ReducersView>[ReducerName] { - const connectionState = useSpacetimeDB(); + reducerDef: ReducerDef, +): (params: PrettifyDeep>) => void { + const reducerName = reducerDef.accessorName; + const connectionState = useSpacetimeDB(); const connection = connectionState.getConnection()!; - return connection.reducers[reducerName]; + return connection.reducers[reducerName as any] as (params: PrettifyDeep>) => void; } \ No newline at end of file diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index 8eb1a1e01d1..619a5b39dea 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -3,12 +3,12 @@ import type { DbConnectionImpl } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -export const SpacetimeDBContext = createContext> | undefined>(undefined); +export const SpacetimeDBContext = createContext(undefined); // Throws an error if used outside of a SpacetimeDBProvider // Error is caught by other hooks like useTable so they can provide better error messages -export function useSpacetimeDB>(): ConnectionState { - const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; +export function useSpacetimeDB(): ConnectionState { + const context = useContext(SpacetimeDBContext) as ConnectionState | undefined; if (!context) { throw new Error( 'useSpacetimeDB must be used within a SpacetimeDBProvider component. Did you forget to add a `SpacetimeDBProvider` to your component tree?' diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index 0dc69d54f6a..b8ac3fab862 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -6,15 +6,15 @@ import { useSyncExternalStore, } from 'react'; import { useSpacetimeDB } from './useSpacetimeDB'; -import { DbConnectionImpl, TableCache, type EventContextInterface, type RemoteModuleOf } from '../sdk/db_connection_impl'; +import { DbConnectionImpl, type EventContextInterface, type RemoteModuleOf } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; -import type { RemoteModule, UntypedRemoteModule } from '../sdk/spacetime_module'; -import type { ClientDbView } from '../sdk/db_view'; -import type { RowType, UntypedTableDef } from '../lib/table'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module'; +import type { RowType, Table, UntypedTableDef } from '../lib/table'; import type { ClientTable } from '../sdk/client_table'; -import type { Infer, InferTypeOfRow } from '../lib/type_builders'; +import type { InferTypeOfRow } from '../lib/type_builders'; +import type { PrettifyDeep } from '../lib/type_util'; -export interface UseQueryCallbacks { +export interface UseTableCallbacks { onInsert?: (row: RowType) => void; onDelete?: (row: RowType) => void; onUpdate?: (oldRow: RowType, newRow: RowType) => void; @@ -72,11 +72,9 @@ export const isOr = ( e: Expr ): e is Extract, { type: 'or' }> => e.type === 'or'; -type RecordLike = Record; - export function evaluate( expr: Expr, - row: RecordLike + row: Record ): boolean { switch (expr.type) { case 'eq': { @@ -142,11 +140,6 @@ export function where(expr: Expr): Expr { return expr; } -type Snapshot = { - readonly rows: readonly RowType[]; - readonly state: 'loading' | 'ready'; -}; - type MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut'; function classifyMembership< @@ -182,14 +175,10 @@ type ColumnsFromRow = { }[keyof R] & string; -// From a ClientTable, get Tbl -type TableDefOfClient = T extends ClientTable ? Tbl : never; - // Row type for a given connection + table key type RowTypeOfTable< - C extends DbConnectionImpl, - K extends keyof ClientDbView> -> = InferTypeOfRow>[K]>>>; + TableDef extends UntypedTableDef, +> = InferTypeOfRow>; /** * React hook to subscribe to a table in SpacetimeDB and receive live updates as rows are inserted, updated, or deleted. @@ -225,13 +214,12 @@ type RowTypeOfTable< * ``` */ export function useTable< - DbConnection extends DbConnectionImpl, - TableName extends keyof ClientDbView> + TableDef extends UntypedTableDef, >( - tableName: TableName, - where: Expr>> , - callbacks?: UseQueryCallbacks> -): Snapshot>; + tableDef: TableDef, + where: Expr>>, + callbacks?: UseTableCallbacks> +): PrettifyDeep>[]; /** * React hook to subscribe to a table in SpacetimeDB and receive live updates as rows are inserted, updated, or deleted. @@ -266,25 +254,31 @@ export function useTable< * }); * ``` */ +// export function useTable< +// TableDef extends UntypedTableDef, +// >( +// tableDef: TableDef, +// where: Expr>>, +// callbacks?: UseQueryCallbacks> +// ): Snapshot>>; export function useTable< - DbConnection extends DbConnectionImpl, - TableName extends keyof ClientDbView> + TableDef extends UntypedTableDef, >( - tableName: TableName, - callbacks?: UseQueryCallbacks> -): Snapshot>; + tableDef: TableDef, + callbacks?: UseTableCallbacks> +): PrettifyDeep>[]; export function useTable< - DbConnection extends DbConnectionImpl, - TableName extends keyof ClientDbView>, + TableDef extends UntypedTableDef, >( - tableName: TableName, + tableDef: TableDef, whereClauseOrCallbacks?: - | Expr>> - | UseQueryCallbacks>, - callbacks?: UseQueryCallbacks> -): Snapshot> { - type UseTableRowType = RowTypeOfTable; + | Expr>> + | UseTableCallbacks>, + callbacks?: UseTableCallbacks> +): readonly RowTypeOfTable[] { + type UseTableRowType = RowTypeOfTable; + const tableName = tableDef.name; let whereClause: Expr> | undefined; if ( whereClauseOrCallbacks && @@ -294,13 +288,13 @@ export function useTable< whereClause = whereClauseOrCallbacks as Expr>; } else { callbacks = whereClauseOrCallbacks as - | UseQueryCallbacks + | UseTableCallbacks | undefined; } const [subscribeApplied, setSubscribeApplied] = useState(false); - let connectionState: ConnectionState | undefined; + let connectionState: ConnectionState | undefined; try { - connectionState = useSpacetimeDB(); + connectionState = useSpacetimeDB(); } catch { throw new Error( 'Could not find SpacetimeDB client! Did you forget to add a ' + @@ -310,27 +304,24 @@ export function useTable< } const query = - `SELECT * FROM ${String(tableName)}` + + `SELECT * FROM ${tableName}` + (whereClause ? ` WHERE ${toString(whereClause)}` : ''); const latestTransactionEvent = useRef(null); - const lastSnapshotRef = useRef | null>(null); + const lastSnapshotRef = useRef(null); const whereKey = whereClause ? toString(whereClause) : ''; - const computeSnapshot = useCallback((): Snapshot => { + const computeSnapshot = useCallback((): readonly UseTableRowType[] => { const connection = connectionState.getConnection(); if (!connection) { - return { rows: [], state: 'loading' }; + return []; } - const table = connection.db[tableName]; + const table = connection.db[tableName] as ClientTable; const result: readonly UseTableRowType[] = whereClause - ? Array.from(table.iter()).filter(row => evaluate(whereClause, row as any)) as unknown as readonly UseTableRowType[] - : Array.from(table.iter()) as unknown as readonly UseTableRowType[]; - return { - rows: result, - state: subscribeApplied ? 'ready' : 'loading', - }; + ? (Array.from(table.iter()).filter(row => evaluate(whereClause, row as UseTableRowType)) as UseTableRowType[]) + : (Array.from(table.iter()) as UseTableRowType[]); + return result; // eslint-disable-next-line react-hooks/exhaustive-deps }, [connectionState, tableName, whereKey, subscribeApplied]); @@ -413,9 +404,7 @@ export function useTable< return () => {}; } - const table = connection.db[ - tableName as keyof typeof connection.db - ]; + const table = connection.db[tableName]; table.onInsert(onInsert); table.onDelete(onDelete); table.onUpdate?.(onUpdate); @@ -437,7 +426,7 @@ export function useTable< ] ); - const getSnapshot = useCallback((): Snapshot => { + const getSnapshot = useCallback((): readonly UseTableRowType[] => { if (!lastSnapshotRef.current) { lastSnapshotRef.current = computeSnapshot(); } diff --git a/crates/bindings-typescript/src/sdk/client_cache.ts b/crates/bindings-typescript/src/sdk/client_cache.ts index 125a43ee99c..8f07f15af37 100644 --- a/crates/bindings-typescript/src/sdk/client_cache.ts +++ b/crates/bindings-typescript/src/sdk/client_cache.ts @@ -1,20 +1,20 @@ -import type { UntypedSchemaDef } from '../lib/schema.ts'; +import type { TableNamesOf, UntypedSchemaDef } from '../lib/schema.ts'; import type { UntypedTableDef } from '../lib/table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; import { TableCache } from './table_cache.ts'; type TableName = - [SchemaDef] extends [UntypedSchemaDef] ? SchemaDef['tables'][number]['name'] : string; + [SchemaDef] extends [UntypedSchemaDef] ? TableNamesOf : string; -export type TableDefForTableName = +export type TableDefForTableName> = [SchemaDef] extends [UntypedSchemaDef] - ? Extract + ? (SchemaDef['tables'][number] & { name: N }) : UntypedTableDef; type TableCacheForTableName< RemoteModule extends UntypedRemoteModule, - N -> = TableCache>; + TableName extends TableNamesOf, +> = TableCache; /** * This is a helper class that provides a mapping from table names to their corresponding TableCache instances @@ -92,7 +92,7 @@ export class ClientCache { return table; } - const newTable = new TableCache>(tableDef); + const newTable = new TableCache(tableDef); this.tables.set(name, newTable); return newTable; } diff --git a/crates/bindings-typescript/src/sdk/client_table.ts b/crates/bindings-typescript/src/sdk/client_table.ts index 6d0e006e9b2..bafaf8eb49f 100644 --- a/crates/bindings-typescript/src/sdk/client_table.ts +++ b/crates/bindings-typescript/src/sdk/client_table.ts @@ -1,34 +1,55 @@ import type { ReadonlyIndexes } from "../lib/indexes"; +import type { TableNamesOf } from "../lib/schema"; import type { ReadonlyTableMethods, RowType, TableIndexes, UntypedTableDef } from "../lib/table"; +import type { ColumnBuilder } from "../lib/type_builders"; import type { Prettify } from "../lib/type_util"; +import type { TableDefForTableName } from "./client_cache"; import type { EventContextInterface } from "./event_context"; import type { UntypedRemoteModule } from "./spacetime_module"; +export type ClientTablePrimaryKeyMethods< + RemoteModule extends UntypedRemoteModule, + TableName extends TableNamesOf, +> = { + /** + * Registers a callback to be invoked when a row is updated in the table. + * Requires that the table has a primary key defined. + * @param cb The callback to invoke when a row is updated. + */ + onUpdate(cb: (ctx: EventContextInterface, oldRow: RowType>, newRow: RowType>) => void): void; + + /** + * Removes a previously registered update event listener. + * @param cb The callback to remove from the update event listeners. + */ + removeOnUpdate(cb: (ctx: EventContextInterface, oldRow: RowType>, newRow: RowType>) => void): void; +}; + export type ClientTableMethods< RemoteModule extends UntypedRemoteModule, - TableDef extends UntypedTableDef, + TableName extends TableNamesOf, > = { /** * Registers a callback to be invoked when a row is inserted into the table. */ - onInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; + onInsert(cb: (ctx: EventContextInterface, row: RowType>) => void): void; /** * Removes a previously registered insert event listener. * @param cb The callback to remove from the insert event listeners. */ - removeOnInsert(cb: (ctx: EventContextInterface, row: RowType) => void): void; + removeOnInsert(cb: (ctx: EventContextInterface, row: RowType>) => void): void; /** * Registers a callback to be invoked when a row is deleted from the table. */ - onDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; + onDelete(cb: (ctx: EventContextInterface, row: RowType>) => void): void; /** * Removes a previously registered delete event listener. * @param cb The callback to remove from the delete event listeners. */ - removeOnDelete(cb: (ctx: EventContextInterface, row: RowType) => void): void; + removeOnDelete(cb: (ctx: EventContextInterface, row: RowType>) => void): void; }; /** @@ -40,19 +61,55 @@ export type ClientTableMethods< */ export type ClientTable< RemoteModule extends UntypedRemoteModule, - TableDef extends UntypedTableDef + TableName extends TableNamesOf, > = Prettify< - ClientTableCore & - ReadonlyIndexes> + ClientTableCore & + ReadonlyIndexes, TableIndexes>> >; +type HasPrimaryKey = + ColumnsHavePrimaryKey; + +type ColumnsHavePrimaryKey< + Cs extends Record> +> = + { + [K in keyof Cs]: + Cs[K] extends ColumnBuilder + ? (M extends { isPrimaryKey: true } ? true : never) + : never + }[keyof Cs] extends true ? true : false; + +type MaybePKMethods< + RemoteModule extends UntypedRemoteModule, + TableName extends TableNamesOf, +> = Partial>; + +/** + * A variant of ClientTableCore where the primary key methods are always optional, + * allowing for classes like TableCache to implement this interface + */ +export type ClientTableCoreImplementable< + RemoteModule extends UntypedRemoteModule, + TableName extends TableNamesOf, +> = + ReadonlyTableMethods> & + ClientTableMethods & + // always present but optional -> statically known member set + MaybePKMethods; + /** * Core methods of ClientTable, without the indexes mixed in. * Includes only staticly known methods. */ export type ClientTableCore< RemoteModule extends UntypedRemoteModule, - TableDef extends UntypedTableDef, + TableName extends TableNamesOf, > = - ReadonlyTableMethods & - ClientTableMethods; \ No newline at end of file + ReadonlyTableMethods> & + ClientTableMethods & + ( + HasPrimaryKey> extends true + ? ClientTablePrimaryKeyMethods + : {} + ); \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index cf33668d4c7..90ac3cd5015 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -7,11 +7,14 @@ import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; /** * The database client connection to a SpacetimeDB server. - * NOTE: DbConnectionImpl is used here + * NOTE: DbConnectionImpl is used here because UntypedRemoteModule causes + * variance issues with function paramters, and the end user will never be + * constructing a DbConnectionBuilder directly since it's code generated. We will + * always have a concrete RemoteModule type in those cases. Even if they user + * did do this, they would just lose type safety on the RemoteModule. */ export class DbConnectionBuilder< - RemoteModule extends UntypedRemoteModule, - DbConnection extends DbConnectionImpl + DbConnection extends DbConnectionImpl > { #uri?: URL; #nameOrAddress?: string; diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 04b1a7967bd..d51dded9905 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -25,7 +25,7 @@ import { } from './event_context.ts'; import { EventEmitter } from './event_emitter.ts'; import { decompress } from './decompress.ts'; -import type { Identity } from '../'; +import type { Identity, InferTypeOfRow } from '../'; import type { IdentityTokenMessage, Message, @@ -50,7 +50,7 @@ import { } from './subscription_builder_impl.ts'; import { stdbLogger } from './logger.ts'; import { fromByteArray } from 'base64-js'; -import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; +import type { ReducerEventInfo, ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; import type { ClientDbView } from './db_view.ts'; import type { UntypedTableDef } from '../lib/table.ts'; import { toCamelCase } from '../lib/utils.ts'; @@ -261,11 +261,11 @@ export class DbConnectionImpl< for (const reducer of def.reducers) { const key = toCamelCase(reducer.name); - (out as any)[key] = (params: typeof reducer.params) => { + (out as any)[key] = (params: InferTypeOfRow) => { const flags = this.#callReducerFlags.get(reducer.name) ?? 'FullUpdate'; this.callReducerWithParams( reducer.name, - reducer.paramsSpacetimeType, + reducer.paramsType, params, flags ); @@ -291,7 +291,7 @@ export class DbConnectionImpl< } #makeEventContext( - event: Event + event: Event>> ): EventContextInterface { // Bind methods to preserve `this` (#private fields safe) return { @@ -648,7 +648,7 @@ export class DbConnectionImpl< case 'TransactionUpdate': { let reducerInfo = message.reducerInfo; let unknownTransaction = false; - let reducerArgs: any | undefined; + let reducerArgs: InferTypeOfRow | undefined; const reducer = this.#remoteModule.reducers.find(t => t.name === reducerInfo!.reducerName)!; if (!reducerInfo) { unknownTransaction = true; @@ -658,7 +658,7 @@ export class DbConnectionImpl< const reader = new BinaryReader(reducerInfo.args as Uint8Array); reducerArgs = ProductType.deserializeValue( reader, - reducer?.paramsSpacetimeType + reducer?.paramsType ); } catch { // This should only be printed in development, since it's @@ -686,6 +686,7 @@ export class DbConnectionImpl< // At this point, we know that `reducerInfo` is not null because // we return if `unknownTransaction` is true. reducerInfo = reducerInfo!; + reducerArgs = reducerArgs!; // Thus this must be a reducer event create it and emit it. const reducerEvent = { @@ -696,9 +697,7 @@ export class DbConnectionImpl< energyConsumed: message.energyConsumed, reducer: { name: reducerInfo.reducerName, - // TODO(cloutiertyler): rename back to args to maintain API compatibility - params: reducerArgs, - paramsSpacetimeType: reducer.paramsSpacetimeType, + args: reducerArgs, }, }; const event: Event = { @@ -718,7 +717,7 @@ export class DbConnectionImpl< const argsArray: any[] = []; ( - reducer.paramsSpacetimeType + reducer.paramsType ).elements.forEach(element => { argsArray.push(reducerArgs[element.name!]); }); diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts index afcdbd4d4d0..ffdee9e7199 100644 --- a/crates/bindings-typescript/src/sdk/db_view.ts +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -5,5 +5,5 @@ import type { ClientTable } from "./client_table"; * A type representing a client-side database view, mapping table names to their corresponding client Table handles. */ export type ClientDbView = { - readonly [Tbl in RemoteModule['tables'][number] as Extract]: ClientTable; + readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable; }; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/event.ts b/crates/bindings-typescript/src/sdk/event.ts index 98254af3ca5..b3d07405d37 100644 --- a/crates/bindings-typescript/src/sdk/event.ts +++ b/crates/bindings-typescript/src/sdk/event.ts @@ -1,7 +1,7 @@ import type { ReducerEvent } from './reducer_event'; -import type { UntypedReducerDef } from './reducers'; +import type { ReducerEventInfo } from './reducers'; -export type Event = +export type Event = | { tag: 'Reducer'; value: ReducerEvent } | { tag: 'SubscribeApplied' } | { tag: 'UnsubscribeApplied' } diff --git a/crates/bindings-typescript/src/sdk/event_context.ts b/crates/bindings-typescript/src/sdk/event_context.ts index 1a69b4c2a3c..8511832cac3 100644 --- a/crates/bindings-typescript/src/sdk/event_context.ts +++ b/crates/bindings-typescript/src/sdk/event_context.ts @@ -1,6 +1,8 @@ +import type { InferTypeOfRow } from '../lib/type_builders.ts'; import type { DbContext } from './db_context'; import type { Event } from './event.ts'; import type { ReducerEvent } from './reducer_event.ts'; +import type { ReducerEventInfo } from './reducers.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; export type UntypedEventContext = EventContextInterface; @@ -9,14 +11,14 @@ export interface EventContextInterface< RemoteModule extends UntypedRemoteModule, > extends DbContext { /** Enum with variants for all possible events. */ - event: Event; + event: Event>>; } export interface ReducerEventContextInterface< RemoteModule extends UntypedRemoteModule, > extends DbContext { /** Enum with variants for all possible events. */ - event: ReducerEvent; + event: ReducerEvent>>; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type diff --git a/crates/bindings-typescript/src/sdk/index.ts b/crates/bindings-typescript/src/sdk/index.ts index 3a066053531..06cf0cdc3b9 100644 --- a/crates/bindings-typescript/src/sdk/index.ts +++ b/crates/bindings-typescript/src/sdk/index.ts @@ -6,6 +6,6 @@ export { type ClientTable } from './client_table.ts'; export { type RemoteModule } from './spacetime_module.ts'; export { type SetReducerFlags } from './reducers.ts'; export * from '../lib/type_builders.ts'; -export { schema } from '../lib/schema.ts'; +export { schema, convertToAccessorMap } from '../lib/schema.ts'; export { table } from '../lib/table.ts'; export { reducerSchema, reducers } from '../lib/reducers.ts'; \ No newline at end of file diff --git a/crates/bindings-typescript/src/sdk/reducer_event.ts b/crates/bindings-typescript/src/sdk/reducer_event.ts index 1ffe5c79177..60638bd2f43 100644 --- a/crates/bindings-typescript/src/sdk/reducer_event.ts +++ b/crates/bindings-typescript/src/sdk/reducer_event.ts @@ -2,9 +2,9 @@ import { ConnectionId } from '../'; import { Timestamp } from '../'; import type { UpdateStatus } from './client_api/index.ts'; import { Identity } from '../'; -import type { UntypedReducerDef } from './reducers.ts'; +import type { ReducerEventInfo } from './reducers.ts'; -export type ReducerEvent = { +export type ReducerEvent = { /** * The time when the reducer started running. * diff --git a/crates/bindings-typescript/src/sdk/reducers.ts b/crates/bindings-typescript/src/sdk/reducers.ts index 28a7b6804c8..567c16df2f7 100644 --- a/crates/bindings-typescript/src/sdk/reducers.ts +++ b/crates/bindings-typescript/src/sdk/reducers.ts @@ -1,19 +1,25 @@ import type { ProductType } from "../lib/algebraic_type"; import type { ParamsObj } from "../lib/reducers"; -import type { CamelCase } from "../lib/type_util"; +import type { CoerceRow } from "../lib/table"; +import type { InferTypeOfRow } from "../lib/type_builders"; +import type { CamelCase, PrettifyDeep } from "../lib/type_util"; import type { CallReducerFlags } from "./db_connection_impl"; export type ReducersView = { - [I in keyof R['reducers'] as CamelCase]: - (params: R['reducers'][number]['params']) => void + [I in keyof R['reducers'] as CamelCase]: + (params: PrettifyDeep>) => void }; -export type UntypedReducers = Record void>; +export type ReducerEventInfo = { + name: string; + args: Args; +}; export type UntypedReducerDef = { name: string; - params: ParamsObj; - paramsSpacetimeType: ProductType; + accessorName: string; + params: CoerceRow; + paramsType: ProductType; }; export type UntypedReducersDef = { diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index c87afc5e895..655a17278f9 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -2,10 +2,11 @@ import { EventEmitter } from './event_emitter.ts'; import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; -import type { EventContextInterface, ClientTable } from './index.ts'; +import type { EventContextInterface, ClientTable, TableDefForTableName } from './index.ts'; import type { RowType, Table, UntypedTableDef } from '../lib/table.ts'; -import type { ClientTableCore } from './client_table.ts'; +import type { ClientTableCore, ClientTableCoreImplementable } from './client_table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; +import type { TableNamesOf } from '../lib/schema.ts'; export type Operation< RowType extends Record = Record, @@ -35,10 +36,10 @@ export type PendingCallback = { */ export class TableCache< RemoteModule extends UntypedRemoteModule, - TableDef extends UntypedTableDef, -> implements ClientTableCore { - private rows: Map, number]>; - private tableDef: TableDef; + TableName extends TableNamesOf, +> implements ClientTableCoreImplementable { + private rows: Map>, number]>; + private tableDef: TableDefForTableName; private emitter: EventEmitter<'insert' | 'delete' | 'update'>; /** @@ -47,7 +48,7 @@ export class TableCache< * @param primaryKey column name designated as `#[primarykey]` * @param entityClass the entityClass */ - constructor(tableDef: TableDef) { + constructor(tableDef: TableDefForTableName) { this.tableDef = tableDef; this.rows = new Map(); this.emitter = new EventEmitter(); @@ -63,8 +64,8 @@ export class TableCache< /** * @returns The values of the rows in the table */ - iter(): IterableIterator> { - function* generator(rows: Map, number]>): IterableIterator> { + iter(): IterableIterator>> { + function* generator(rows: Map>, number]>): IterableIterator>> { for (const [row] of rows.values()) { yield row; } @@ -76,12 +77,12 @@ export class TableCache< * Allows iteration over the rows in the table * @returns An iterator over the rows in the table */ - [Symbol.iterator](): IterableIterator> { + [Symbol.iterator](): IterableIterator>> { return this.iter(); } applyOperations = ( - operations: Operation>[], + operations: Operation>>[], ctx: EventContextInterface ): PendingCallback[] => { const pendingCallbacks: PendingCallback[] = []; @@ -90,11 +91,11 @@ export class TableCache< if (hasPrimaryKey) { const insertMap = new Map< ComparablePrimitive, - [Operation>, number] + [Operation>>, number] >(); const deleteMap = new Map< ComparablePrimitive, - [Operation>, number] + [Operation>>, number] >(); for (const op of operations) { if (op.type === 'insert') { @@ -157,7 +158,7 @@ export class TableCache< update = ( ctx: EventContextInterface, rowId: ComparablePrimitive, - newRow: RowType, + newRow: RowType>, refCountDelta: number = 0 ): PendingCallback | undefined => { const existingEntry = this.rows.get(rowId); @@ -204,7 +205,7 @@ export class TableCache< insert = ( ctx: EventContextInterface, - operation: Operation>, + operation: Operation>>, count: number = 1 ): PendingCallback | undefined => { const [_, previousCount] = this.rows.get(operation.rowId) || [ @@ -227,7 +228,7 @@ export class TableCache< delete = ( ctx: EventContextInterface, - operation: Operation>, + operation: Operation>>, count: number = 1 ): PendingCallback | undefined => { const [_, previousCount] = this.rows.get(operation.rowId) || [ @@ -271,7 +272,7 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onInsert = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType>) => void ): void => { this.emitter.on('insert', cb); }; @@ -292,7 +293,7 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onDelete = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType>) => void ): void => { this.emitter.on('delete', cb); }; @@ -313,7 +314,7 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onUpdate = ( - cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void + cb: (ctx: EventContextInterface, oldRow: RowType>, row: RowType>) => void ): void => { this.emitter.on('update', cb); }; @@ -324,7 +325,7 @@ export class TableCache< * @param cb Callback to be removed */ removeOnInsert = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType>) => void ): void => { this.emitter.off('insert', cb); }; @@ -335,7 +336,7 @@ export class TableCache< * @param cb Callback to be removed */ removeOnDelete = ( - cb: (ctx: EventContextInterface, row: RowType) => void + cb: (ctx: EventContextInterface, row: RowType>) => void ): void => { this.emitter.off('delete', cb); }; @@ -346,7 +347,7 @@ export class TableCache< * @param cb Callback to be removed */ removeOnUpdate = ( - cb: (ctx: EventContextInterface, oldRow: RowType, row: RowType) => void + cb: (ctx: EventContextInterface, oldRow: RowType>, row: RowType>) => void ): void => { this.emitter.off('update', cb); }; diff --git a/crates/bindings-typescript/test-app/src/App.tsx b/crates/bindings-typescript/test-app/src/App.tsx index bb2e7b1b7bf..fdb4fcdf554 100644 --- a/crates/bindings-typescript/test-app/src/App.tsx +++ b/crates/bindings-typescript/test-app/src/App.tsx @@ -1,23 +1,19 @@ -import { DbConnection, Player } from './module_bindings'; +import { tables, reducers, DbConnection } from './module_bindings'; import { useEffect } from 'react'; import './App.css'; import { useReducer, useSpacetimeDB, useTable } from '../../src/react'; function App() { - const x = useReducer; - const createPlayer = useReducer('createPlayer'); - const connection = useSpacetimeDB(); - const players = useTable('player', { - onInsert: player => { - console.log(player); - }, - }); + const connection = useSpacetimeDB(); + const players = useTable(tables.player); + const createPlayer = useReducer(reducers.createPlayer); + createPlayer({ name: 'Test', location: { x: 0, y: 0 } }); useEffect(() => { setTimeout(() => { - console.log(Array.from(players.rows)); + console.log(Array.from(players)); }, 5000); - }, [connection, players.rows]); + }, [connection, players]); return (
@@ -26,7 +22,10 @@ function App() { +
+ {Array.from(players).map((player, i) => ( +
+ {player.name} - ({player.location.x}, {player.location.y}) +
+ ))} +
); } diff --git a/crates/bindings-typescript/test-app/src/main.tsx b/crates/bindings-typescript/test-app/src/main.tsx index fcb7271984b..028148b3eb7 100644 --- a/crates/bindings-typescript/test-app/src/main.tsx +++ b/crates/bindings-typescript/test-app/src/main.tsx @@ -12,8 +12,8 @@ const connectionBuilder = DbConnection.builder() .onDisconnect(() => { console.log('disconnected'); }) - .onConnectError(() => { - console.log('client_error'); + .onConnectError((ctx, err) => { + console.log('client_error: ', err); }) .onConnect((conn, identity, _token) => { console.log( @@ -22,10 +22,7 @@ const connectionBuilder = DbConnection.builder() ); conn.subscriptionBuilder().subscribe('SELECT * FROM player'); - }) - .withToken( - 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwMUpCQTBYRzRESFpIWUdQQk5GRFk5RDQ2SiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zdGFnaW5nLnNwYWNldGltZWRiLmNvbSIsImlhdCI6MTczMDgwODUwNSwiZXhwIjoxNzkzODgwNTA1fQ.kGM4HGX0c0twL8NJoSQowzSZa8dc2Ogc-fsvaDK7otUrcdGFsZ3KsNON2eNkFh73FER0hl55_eJStr2tgoPwfTyl_v_TqkY45iUOUlLmHfB-X42cMzpE7PXbR_PKYcp-P-Wa4jGtVl4oF7CvdGKxlhIYEk3e0ElQlA9ThnZN4IEciYV0vwAXGqbaO9SOG8jbrmlmfN7oKgl02EgpodEAHTrnB2mD1qf1YyOw7_9n_EkxJxWLkJf9-nFCVRrbfSLqSJBeE6OKNAu2VLLYrSFE7GkVXNCFVugoCDM2oVJogX75AgzWimrp75QRmLsXbvB-YvvRkQ8Gfb2RZnqCj9kiYg' - ); + }); ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts index 8acf176ea68..ee98682e78b 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_table.ts @@ -12,7 +12,8 @@ import { import Point from './point_type'; export default __t.row({ - ownerId: __t.string(), + id: __t.u32(), + userId: __t.identity(), name: __t.string(), get location() { return Point; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts index 3a1d4492baa..596b73f7d06 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/player_type.ts @@ -12,7 +12,8 @@ import { import Point from './point_type'; export default __t.object('Player', { - ownerId: __t.string(), + id: __t.u32(), + userId: __t.identity(), name: __t.string(), get location() { return Point; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts index 8acf176ea68..96f056baeb7 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts @@ -12,7 +12,8 @@ import { import Point from './point_type'; export default __t.row({ - ownerId: __t.string(), + id: __t.u32(), + ownerId: __t.identity(), name: __t.string(), get location() { return Point; diff --git a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts index f697863ed0c..5f717022485 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_type.ts @@ -12,7 +12,8 @@ import { import Point from './point_type'; export default __t.object('UnindexedPlayer', { - ownerId: __t.string(), + id: __t.u32(), + ownerId: __t.identity(), name: __t.string(), get location() { return Point; From 16814cb5555ef9c95ddff6e56d7c9b975de82d6d Mon Sep 17 00:00:00 2001 From: = Date: Sat, 8 Nov 2025 21:23:32 -0500 Subject: [PATCH 17/23] Small adjustments --- crates/bindings-typescript/src/lib/connection_id.ts | 7 +++++++ crates/bindings-typescript/src/lib/identity.ts | 9 ++++++++- crates/bindings-typescript/test-app/src/App.tsx | 9 +++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/bindings-typescript/src/lib/connection_id.ts b/crates/bindings-typescript/src/lib/connection_id.ts index 64da6361395..80a940115c4 100644 --- a/crates/bindings-typescript/src/lib/connection_id.ts +++ b/crates/bindings-typescript/src/lib/connection_id.ts @@ -63,6 +63,13 @@ export class ConnectionId { return this.__connection_id__ == other.__connection_id__; } + /** + * Check if two connection IDs are equal. + */ + equals(other: ConnectionId): boolean { + return this.isEqual(other); + } + /** * Print the connection ID as a hexadecimal string. */ diff --git a/crates/bindings-typescript/src/lib/identity.ts b/crates/bindings-typescript/src/lib/identity.ts index 3b2b0e60ed8..68d890c4f39 100644 --- a/crates/bindings-typescript/src/lib/identity.ts +++ b/crates/bindings-typescript/src/lib/identity.ts @@ -36,12 +36,19 @@ export class Identity { } /** - * Compare two identities for equality. + * Check if two identities are equal. */ isEqual(other: Identity): boolean { return this.toHexString() === other.toHexString(); } + /** + * Check if two identities are equal. + */ + equals(other: Identity): boolean { + return this.isEqual(other); + } + /** * Print the identity as a hexadecimal string. */ diff --git a/crates/bindings-typescript/test-app/src/App.tsx b/crates/bindings-typescript/test-app/src/App.tsx index d68312f00d9..3ef576fb0e1 100644 --- a/crates/bindings-typescript/test-app/src/App.tsx +++ b/crates/bindings-typescript/test-app/src/App.tsx @@ -1,7 +1,7 @@ import { tables, reducers } from './module_bindings'; import { useEffect } from 'react'; import './App.css'; -import { useReducer, useSpacetimeDB, useTable } from '../../src/react'; +import { eq, useReducer, useSpacetimeDB, useTable, where } from '../../src/react'; function getRandomInt(max: number) { return Math.floor(Math.random() * max); @@ -9,7 +9,12 @@ function getRandomInt(max: number) { function App() { const connection = useSpacetimeDB(); - const players = useTable(tables.player); + const players = useTable(tables.player, where(eq('name', 'Hello')), { + onInsert: (row) => { + console.log('Player inserted:', rows); + }, + + }); const x = players[0]; const createPlayer = useReducer(reducers.createPlayer); From 384f8b02b50eb5d5467004b6e1336a962c1aeb83 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 9 Nov 2025 01:04:52 -0500 Subject: [PATCH 18/23] pnpm format --- .../examples/quickstart-chat/src/App.tsx | 2 +- .../src/module_bindings/user_table.ts | 6 +- .../src/lib/algebraic_type.ts | 17 +- .../src/lib/algebraic_type_variants.ts | 8 +- .../src/lib/constraints.ts | 2 +- .../bindings-typescript/src/lib/identity.ts | 2 +- crates/bindings-typescript/src/lib/indexes.ts | 2 +- .../src/lib/reducer_schema.ts | 16 +- .../bindings-typescript/src/lib/reducers.ts | 22 +- crates/bindings-typescript/src/lib/schema.ts | 89 +- crates/bindings-typescript/src/lib/table.ts | 62 +- .../src/lib/table_schema.ts | 10 +- .../src/lib/type_builders.ts | 1031 ++++++++++------- .../bindings-typescript/src/lib/type_util.ts | 35 +- .../src/react/SpacetimeDBProvider.ts | 20 +- .../src/react/connection_state.ts | 10 +- .../src/react/useReducer.ts | 50 +- .../src/react/useSpacetimeDB.ts | 6 +- .../bindings-typescript/src/react/useTable.ts | 77 +- .../src/sdk/client_cache.ts | 57 +- .../src/sdk/client_table.ts | 111 +- .../src/sdk/db_connection_builder.ts | 30 +- .../src/sdk/db_connection_impl.ts | 109 +- .../bindings-typescript/src/sdk/db_context.ts | 10 +- crates/bindings-typescript/src/sdk/db_view.ts | 11 +- .../src/sdk/event_context.ts | 18 +- crates/bindings-typescript/src/sdk/index.ts | 2 +- .../src/sdk/message_types.ts | 23 +- .../src/sdk/reducer_handle.ts | 4 +- .../bindings-typescript/src/sdk/reducers.ts | 25 +- .../src/sdk/set_reducer_flags.ts | 7 +- .../src/sdk/spacetime_module.ts | 22 +- .../src/sdk/subscription_builder_impl.ts | 60 +- .../src/sdk/table_cache.ts | 100 +- .../src/sdk/websocket_test_adapter.ts | 6 +- .../bindings-typescript/src/server/db_view.ts | 6 +- .../bindings-typescript/src/server/runtime.ts | 14 +- .../bindings-typescript/test-app/src/App.tsx | 13 +- .../bindings-typescript/tests/serde.test.ts | 9 +- 39 files changed, 1307 insertions(+), 797 deletions(-) diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx b/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx index 0fa172b20ab..a6cf1f2751c 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx +++ b/crates/bindings-typescript/examples/quickstart-chat/src/App.tsx @@ -18,7 +18,7 @@ function App() { const [newMessage, setNewMessage] = useState(''); const conn = useSpacetimeDB(); - conn.setReducerFlags() + conn.setReducerFlags(); const { identity, isActive: connected } = conn; // Subscribe to all messages in the chat diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts index 9b35746a2ab..61c82b8de12 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts +++ b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/user_table.ts @@ -46,9 +46,9 @@ declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; * but to directly chain method calls, * like `ctx.db.user.on_insert(...)`. */ -export class UserTableHandle - extends ClientTable -{ +export class UserTableHandle< + TableName extends string, +> extends ClientTable { // phantom type to track the table name readonly tableName!: TableName; tableCache: __TableCache; diff --git a/crates/bindings-typescript/src/lib/algebraic_type.ts b/crates/bindings-typescript/src/lib/algebraic_type.ts index 4bb42a475e9..2f8b4bdbba3 100644 --- a/crates/bindings-typescript/src/lib/algebraic_type.ts +++ b/crates/bindings-typescript/src/lib/algebraic_type.ts @@ -69,17 +69,24 @@ export type AlgebraicType = AlgebraicTypeType; /** * The variant types of the Algebraic Type tagged union. */ -export { AlgebraicTypeVariants } +export { AlgebraicTypeVariants }; // A value with helper functions to construct the type. export const AlgebraicType = { Ref: (value: number): AlgebraicTypeVariants.Ref => ({ tag: 'Ref', value }), - Sum: (value: T): { tag: 'Sum'; value: T } => ({ tag: 'Sum', value }), - Product: (value: T): { tag: 'Product'; value: T } => ({ + Sum: (value: T): { tag: 'Sum'; value: T } => ({ + tag: 'Sum', + value, + }), + Product: ( + value: T + ): { tag: 'Product'; value: T } => ({ tag: 'Product', value, }), - Array: (value: T): { tag: 'Array'; value: T } => ({ + Array: ( + value: T + ): { tag: 'Array'; value: T } => ({ tag: 'Array', value, }), @@ -280,7 +287,7 @@ export const AlgebraicType = { } } }, -} +}; /** * A structural product type of the factors given by `elements`. diff --git a/crates/bindings-typescript/src/lib/algebraic_type_variants.ts b/crates/bindings-typescript/src/lib/algebraic_type_variants.ts index 0a911a40438..5890d53c148 100644 --- a/crates/bindings-typescript/src/lib/algebraic_type_variants.ts +++ b/crates/bindings-typescript/src/lib/algebraic_type_variants.ts @@ -1,4 +1,8 @@ -import type { AlgebraicTypeType, ProductTypeType, SumTypeType } from "./algebraic_type"; +import type { + AlgebraicTypeType, + ProductTypeType, + SumTypeType, +} from './algebraic_type'; export type Ref = { tag: 'Ref'; value: number }; export type Sum = { tag: 'Sum'; value: SumTypeType }; @@ -19,4 +23,4 @@ export type U128 = { tag: 'U128' }; export type I256 = { tag: 'I256' }; export type U256 = { tag: 'U256' }; export type F32 = { tag: 'F32' }; -export type F64 = { tag: 'F64' }; \ No newline at end of file +export type F64 = { tag: 'F64' }; diff --git a/crates/bindings-typescript/src/lib/constraints.ts b/crates/bindings-typescript/src/lib/constraints.ts index 4b206a8c858..c3ee5a87c6f 100644 --- a/crates/bindings-typescript/src/lib/constraints.ts +++ b/crates/bindings-typescript/src/lib/constraints.ts @@ -47,4 +47,4 @@ export type ConstraintOpts = { } & ( | { constraint: 'unique'; columns: [AllowedCol] } | { constraint: 'primaryKey'; columns: [AllowedCol] } -); \ No newline at end of file +); diff --git a/crates/bindings-typescript/src/lib/identity.ts b/crates/bindings-typescript/src/lib/identity.ts index 68d890c4f39..3ec9081c835 100644 --- a/crates/bindings-typescript/src/lib/identity.ts +++ b/crates/bindings-typescript/src/lib/identity.ts @@ -36,7 +36,7 @@ export class Identity { } /** - * Check if two identities are equal. + * Check if two identities are equal. */ isEqual(other: Identity): boolean { return this.toHexString() === other.toHexString(); diff --git a/crates/bindings-typescript/src/lib/indexes.ts b/crates/bindings-typescript/src/lib/indexes.ts index 5c93c9aac9b..04e3215e466 100644 --- a/crates/bindings-typescript/src/lib/indexes.ts +++ b/crates/bindings-typescript/src/lib/indexes.ts @@ -100,7 +100,7 @@ export type UniqueIndex< }; /** - * A type representing a read-only ranged index on a database table. + * A type representing a read-only ranged index on a database table. */ export type ReadonlyRangedIndex< TableDef extends UntypedTableDef, diff --git a/crates/bindings-typescript/src/lib/reducer_schema.ts b/crates/bindings-typescript/src/lib/reducer_schema.ts index d8fd614de91..c68a350f2d8 100644 --- a/crates/bindings-typescript/src/lib/reducer_schema.ts +++ b/crates/bindings-typescript/src/lib/reducer_schema.ts @@ -1,8 +1,8 @@ -import type { ProductType } from "./algebraic_type"; -import type RawReducerDefV9 from "./autogen/raw_reducer_def_v_9_type"; -import type { ParamsObj } from "./reducers"; -import type { Infer, RowBuilder, RowObj } from "./type_builders"; -import type { CamelCase } from "./type_util"; +import type { ProductType } from './algebraic_type'; +import type RawReducerDefV9 from './autogen/raw_reducer_def_v_9_type'; +import type { ParamsObj } from './reducers'; +import type { Infer, RowBuilder, RowObj } from './type_builders'; +import type { CamelCase } from './type_util'; /** * Represents a handle to a database reducer, including its name and argument type. @@ -15,7 +15,7 @@ export type ReducerSchema< * The name of the reducer. */ readonly reducerName: ReducerName; - + /** * The accessor name for the reducer. */ @@ -25,7 +25,7 @@ export type ReducerSchema< * The TypeBuilder representation of the reducer's parameter type. */ readonly params: RowBuilder; - + /** * The {@link ProductType} representing the structure of the reducer's parameters. */ @@ -35,4 +35,4 @@ export type ReducerSchema< * The {@link RawReducerDefV9} of the configured reducer. */ readonly reducerDef: Infer; -}; \ No newline at end of file +}; diff --git a/crates/bindings-typescript/src/lib/reducers.ts b/crates/bindings-typescript/src/lib/reducers.ts index 14f7a9caffa..da7191d8f76 100644 --- a/crates/bindings-typescript/src/lib/reducers.ts +++ b/crates/bindings-typescript/src/lib/reducers.ts @@ -288,9 +288,9 @@ type ReducersToSchema[]> = { }; export function reducersToSchema< - const T extends readonly ReducerSchema[] + const T extends readonly ReducerSchema[], >(reducers: T): ReducersToSchema { - const mapped = reducers.map((r) => { + const mapped = reducers.map(r => { const paramsRow = r.params.row; return { @@ -308,7 +308,7 @@ export function reducersToSchema< paramsType: T[I]['paramsSpacetimeType']; }; }; - + const result = { reducers: mapped } satisfies ReducersToSchema; return result; } @@ -339,21 +339,18 @@ export function reducers[]>( ): Reducers>; export function reducers[]>( - ...args: - | [H] - | H + ...args: [H] | H ): Reducers> { - const handles = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as H; + const handles = ( + args.length === 1 && Array.isArray(args[0]) ? args[0] : args + ) as H; return new Reducers(handles); } export function reducerSchema< ReducerName extends string, Params extends ParamsObj, ->( - name: ReducerName, - params: Params -): ReducerSchema { +>(name: ReducerName, params: Params): ReducerSchema { const paramType: ProductType = { elements: Object.entries(params).map(([n, c]) => ({ name: n, @@ -371,5 +368,4 @@ export function reducerSchema< lifecycle: undefined, }, }; -} - +} diff --git a/crates/bindings-typescript/src/lib/schema.ts b/crates/bindings-typescript/src/lib/schema.ts index a06293e8f49..5c764537d88 100644 --- a/crates/bindings-typescript/src/lib/schema.ts +++ b/crates/bindings-typescript/src/lib/schema.ts @@ -34,7 +34,8 @@ import type { CamelCase } from './type_util'; import type { TableSchema } from './table_schema'; import { toCamelCase } from './utils'; -export type TableNamesOf = S['tables'][number]['name']; +export type TableNamesOf = + S['tables'][number]['name']; /** * An untyped representation of the database schema. @@ -60,10 +61,10 @@ type TablesToSchema[]> = { }; export function tablesToSchema< - const T extends readonly TableSchema[] + const T extends readonly TableSchema[], >(tables: T): TablesToSchema { const result = { - tables: tables.map((schema) => { + tables: tables.map(schema => { return { name: schema.tableName, accessorName: toCamelCase(schema.tableName), @@ -73,15 +74,15 @@ export function tablesToSchema< indexes: [...schema.idxs], } as const; }) as { - // preserve tuple indices so the return type matches `[i in keyof T]` - readonly [I in keyof T]: { - name: T[I]['tableName']; - accessorName: CamelCase; - columns: T[I]['rowType']['row']; - rowType: T[I]['rowSpacetimeType']; - indexes: T[I]['idxs']; - }; - }, + // preserve tuple indices so the return type matches `[i in keyof T]` + readonly [I in keyof T]: { + name: T[I]['tableName']; + accessorName: CamelCase; + columns: T[I]['rowType']['row']; + rowType: T[I]['rowSpacetimeType']; + indexes: T[I]['idxs']; + }; + }, } satisfies TablesToSchema; return result; } @@ -123,12 +124,15 @@ export function resolveType( /** * Adds a type to the module definition's typespace as a `Ref` if it is a named compound type (Product or Sum). * Otherwise, returns the type as is. - * @param name - * @param ty - * @returns + * @param name + * @param ty + * @returns */ export function registerTypesRecursively( - typeBuilder: SumBuilder | ProductBuilder | RowBuilder + typeBuilder: + | SumBuilder + | ProductBuilder + | RowBuilder ): RefBuilder { const ty = typeBuilder.algebraicType; const name = typeBuilder.typeName; @@ -142,21 +146,42 @@ export function registerTypesRecursively( // Recursively register nested compound types if (typeBuilder instanceof RowBuilder) { for (const [name, elem] of Object.entries(typeBuilder.row)) { - if (!(elem instanceof ProductBuilder || elem instanceof SumBuilder || elem instanceof RowBuilder)) { + if ( + !( + elem instanceof ProductBuilder || + elem instanceof SumBuilder || + elem instanceof RowBuilder + ) + ) { continue; } - typeBuilder.row[name] = new ColumnBuilder(registerTypesRecursively(elem), {}); + typeBuilder.row[name] = new ColumnBuilder( + registerTypesRecursively(elem), + {} + ); } } else if (typeBuilder instanceof ProductBuilder) { for (const [name, elem] of Object.entries(typeBuilder.elements)) { - if (!(elem instanceof ProductBuilder || elem instanceof SumBuilder || elem instanceof RowBuilder)) { + if ( + !( + elem instanceof ProductBuilder || + elem instanceof SumBuilder || + elem instanceof RowBuilder + ) + ) { continue; } typeBuilder.elements[name] = registerTypesRecursively(elem); } } else if (typeBuilder instanceof SumBuilder) { for (const [name, variant] of Object.entries(typeBuilder.variants)) { - if (!(variant instanceof ProductBuilder || variant instanceof SumBuilder || variant instanceof RowBuilder)) { + if ( + !( + variant instanceof ProductBuilder || + variant instanceof SumBuilder || + variant instanceof RowBuilder + ) + ) { continue; } typeBuilder.variants[name] = registerTypesRecursively(variant); @@ -221,7 +246,11 @@ class Schema { readonly typespace: Infer; readonly schemaType: S; - constructor(tables: Infer[], typespace: Infer, handles: readonly TableSchema[]) { + constructor( + tables: Infer[], + typespace: Infer, + handles: readonly TableSchema[] + ) { this.tablesDef = { tables }; this.typespace = typespace; // TODO: TableSchema and TableDef should really be unified @@ -428,11 +457,11 @@ export function schema[]>( * ``` */ export function schema[]>( - ...args: - | [H] - | H + ...args: [H] | H ): Schema> { - const handles = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as H; + const handles = ( + args.length === 1 && Array.isArray(args[0]) ? args[0] : args + ) as H; const tableDefs = handles.map(h => h.tableDef); // Side-effect: @@ -455,9 +484,13 @@ export function schema[]>( type HasAccessor = { accessorName: PropertyKey }; export type ConvertToAccessorMap = { - [Tbl in TableDefs[number] as Tbl["accessorName"]]: Tbl + [Tbl in TableDefs[number] as Tbl['accessorName']]: Tbl; }; -export function convertToAccessorMap(arr: T): ConvertToAccessorMap { - return Object.fromEntries(arr.map(v => [v.accessorName, v])) as ConvertToAccessorMap; +export function convertToAccessorMap( + arr: T +): ConvertToAccessorMap { + return Object.fromEntries( + arr.map(v => [v.accessorName, v]) + ) as ConvertToAccessorMap; } diff --git a/crates/bindings-typescript/src/lib/table.ts b/crates/bindings-typescript/src/lib/table.ts index a57f8fbebb5..6d27e10a799 100644 --- a/crates/bindings-typescript/src/lib/table.ts +++ b/crates/bindings-typescript/src/lib/table.ts @@ -5,8 +5,19 @@ import type RawIndexDefV9 from './autogen/raw_index_def_v_9_type'; import type RawSequenceDefV9 from './autogen/raw_sequence_def_v_9_type'; import type RawTableDefV9 from './autogen/raw_table_def_v_9_type'; import type { AllUnique, ConstraintOpts } from './constraints'; -import type { ColumnIndex, IndexColumns, Indexes, IndexOpts, ReadonlyIndexes } from './indexes'; -import { registerTypesRecursively, MODULE_DEF, resolveType, splitName } from './schema'; +import type { + ColumnIndex, + IndexColumns, + Indexes, + IndexOpts, + ReadonlyIndexes, +} from './indexes'; +import { + registerTypesRecursively, + MODULE_DEF, + resolveType, + splitName, +} from './schema'; import type { TableSchema } from './table_schema'; import { RowBuilder, @@ -17,7 +28,7 @@ import { type RowObj, type TypeBuilder, } from './type_builders'; -import type { Prettify, } from './type_util'; +import type { Prettify } from './type_util'; export type AlgebraicTypeRef = number; type ColId = number; @@ -26,9 +37,9 @@ type ColList = ColId[]; /** * A helper type to extract the row type from a TableDef */ -export type RowType = Prettify = InferTypeOfRow< TableDef['columns'] ->>; +>; /** * Coerces a column which may be a TypeBuilder or ColumnBuilder into a ColumnBuilder @@ -36,7 +47,9 @@ export type RowType = Prettify | ColumnBuilder, > = - Col extends TypeBuilder ? ColumnBuilder> : Col; + Col extends TypeBuilder + ? ColumnBuilder> + : Col; /** * Coerces a RowObj where TypeBuilders are replaced with ColumnBuilders @@ -136,19 +149,20 @@ export type ReadonlyTableMethods = { /** * A type representing the methods available on a table. */ -export type TableMethods = ReadonlyTableMethods & { - /** - * Insert and return the inserted row (auto-increment fields filled). - * - * May throw on error: - * * If there are any unique or primary key columns in this table, may throw {@link UniqueAlreadyExists}. - * * If there are any auto-incrementing columns in this table, may throw {@link AutoIncOverflow}. - * */ - insert(row: RowType): RowType; - - /** Delete a row equal to `row`. Returns true if something was deleted. */ - delete(row: RowType): boolean; -}; +export type TableMethods = + ReadonlyTableMethods & { + /** + * Insert and return the inserted row (auto-increment fields filled). + * + * May throw on error: + * * If there are any unique or primary key columns in this table, may throw {@link UniqueAlreadyExists}. + * * If there are any auto-incrementing columns in this table, may throw {@link AutoIncOverflow}. + * */ + insert(row: RowType): RowType; + + /** Delete a row equal to `row`. Returns true if something was deleted. */ + delete(row: RowType): boolean; + }; /** * Defines a database table with schema and options @@ -279,7 +293,7 @@ export function table>( }; constraints.push({ name: constraintOpts.name, data }); continue; - } + } if (constraintOpts.constraint === 'primaryKey') { pk.push(...constraintOpts.columns.map(c => colIds.get(c)!)); } @@ -325,9 +339,11 @@ export function table>( } const productType = { - elements: resolveType(MODULE_DEF.typespace, row).value.elements.map(elem => { - return { name: elem.name, algebraicType: elem.algebraicType }; - }), + elements: resolveType(MODULE_DEF.typespace, row).value.elements.map( + elem => { + return { name: elem.name, algebraicType: elem.algebraicType }; + } + ), }; return { diff --git a/crates/bindings-typescript/src/lib/table_schema.ts b/crates/bindings-typescript/src/lib/table_schema.ts index c160989e4a3..540af16673a 100644 --- a/crates/bindings-typescript/src/lib/table_schema.ts +++ b/crates/bindings-typescript/src/lib/table_schema.ts @@ -1,7 +1,7 @@ -import type { ProductType } from "./algebraic_type"; -import type RawTableDefV9 from "./autogen/raw_table_def_v_9_type"; -import type { IndexOpts } from "./indexes"; -import type { ColumnBuilder, Infer, RowBuilder } from "./type_builders"; +import type { ProductType } from './algebraic_type'; +import type RawTableDefV9 from './autogen/raw_table_def_v_9_type'; +import type { IndexOpts } from './indexes'; +import type { ColumnBuilder, Infer, RowBuilder } from './type_builders'; /** * Represents a handle to a database table, including its name, row type, and row spacetime type. @@ -35,4 +35,4 @@ export type TableSchema< * The indexes defined on the table. */ readonly idxs: Idx; -}; \ No newline at end of file +}; diff --git a/crates/bindings-typescript/src/lib/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts index b616e5d27ab..567bae3975f 100644 --- a/crates/bindings-typescript/src/lib/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -117,9 +117,9 @@ type IsUnit = B extends UnitBuilder ? true : false; * e.g. { A: I32TypeBuilder, B: StringBuilder } -> { tag: "A", value: number } | { tag: "B", value: string } */ type EnumType = { - [K in keyof Variants & string]: - (IsUnit extends true ? { tag: K } - : { tag: K; value: InferTypeOfTypeBuilder }) + [K in keyof Variants & string]: IsUnit extends true + ? { tag: K } + : { tag: K; value: InferTypeOfTypeBuilder }; }[keyof Variants & string]; /** @@ -135,7 +135,8 @@ type VariantsArrayFromVariantsObj = { * and the corresponding `AlgebraicType`. */ export class TypeBuilder - implements Optional { + implements Optional +{ /** * The TypeScript phantom type. This is not stored at runtime, * but is visible to the compiler @@ -247,7 +248,11 @@ interface Indexable< * Specify the index type for this column * @param algorithm The index algorithm to use */ - index(): ColumnBuilder>; + index(): ColumnBuilder< + Type, + SpacetimeType, + SetField + >; index>( algorithm: N ): ColumnBuilder>; @@ -341,11 +346,12 @@ interface Defaultable< export class U8Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.U8); } @@ -364,13 +370,17 @@ export class U8Builder unique(): U8ColumnBuilder> { return new U8ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U8ColumnBuilder> { + primaryKey(): U8ColumnBuilder< + SetField + > { return new U8ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U8ColumnBuilder> { + autoInc(): U8ColumnBuilder< + SetField + > { return new U8ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -389,11 +399,12 @@ export class U8Builder export class U16Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.U16); } @@ -412,13 +423,17 @@ export class U16Builder unique(): U16ColumnBuilder> { return new U16ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U16ColumnBuilder> { + primaryKey(): U16ColumnBuilder< + SetField + > { return new U16ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U16ColumnBuilder> { + autoInc(): U16ColumnBuilder< + SetField + > { return new U16ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -437,11 +452,12 @@ export class U16Builder export class U32Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.U32); } @@ -460,13 +476,17 @@ export class U32Builder unique(): U32ColumnBuilder> { return new U32ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U32ColumnBuilder> { + primaryKey(): U32ColumnBuilder< + SetField + > { return new U32ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U32ColumnBuilder> { + autoInc(): U32ColumnBuilder< + SetField + > { return new U32ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -485,11 +505,12 @@ export class U32Builder export class U64Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.U64); } @@ -508,13 +529,17 @@ export class U64Builder unique(): U64ColumnBuilder> { return new U64ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): U64ColumnBuilder> { + primaryKey(): U64ColumnBuilder< + SetField + > { return new U64ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U64ColumnBuilder> { + autoInc(): U64ColumnBuilder< + SetField + > { return new U64ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -533,11 +558,12 @@ export class U64Builder export class U128Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.U128); } @@ -559,13 +585,17 @@ export class U128Builder set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): U128ColumnBuilder> { + primaryKey(): U128ColumnBuilder< + SetField + > { return new U128ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U128ColumnBuilder> { + autoInc(): U128ColumnBuilder< + SetField + > { return new U128ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -584,11 +614,12 @@ export class U128Builder export class U256Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.U256); } @@ -610,13 +641,17 @@ export class U256Builder set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): U256ColumnBuilder> { + primaryKey(): U256ColumnBuilder< + SetField + > { return new U256ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): U256ColumnBuilder> { + autoInc(): U256ColumnBuilder< + SetField + > { return new U256ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -635,11 +670,12 @@ export class U256Builder export class I8Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.I8); } @@ -658,13 +694,17 @@ export class I8Builder unique(): I8ColumnBuilder> { return new I8ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I8ColumnBuilder> { + primaryKey(): I8ColumnBuilder< + SetField + > { return new I8ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I8ColumnBuilder> { + autoInc(): I8ColumnBuilder< + SetField + > { return new I8ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -683,11 +723,12 @@ export class I8Builder export class I16Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.I16); } @@ -706,13 +747,17 @@ export class I16Builder unique(): I16ColumnBuilder> { return new I16ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I16ColumnBuilder> { + primaryKey(): I16ColumnBuilder< + SetField + > { return new I16ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I16ColumnBuilder> { + autoInc(): I16ColumnBuilder< + SetField + > { return new I16ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -731,12 +776,13 @@ export class I16Builder export class I32Builder extends TypeBuilder implements - TypeBuilder, - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + TypeBuilder, + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.I32); } @@ -755,13 +801,17 @@ export class I32Builder unique(): I32ColumnBuilder> { return new I32ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I32ColumnBuilder> { + primaryKey(): I32ColumnBuilder< + SetField + > { return new I32ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I32ColumnBuilder> { + autoInc(): I32ColumnBuilder< + SetField + > { return new I32ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -780,11 +830,12 @@ export class I32Builder export class I64Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.I64); } @@ -803,13 +854,17 @@ export class I64Builder unique(): I64ColumnBuilder> { return new I64ColumnBuilder(this, set(defaultMetadata, { isUnique: true })); } - primaryKey(): I64ColumnBuilder> { + primaryKey(): I64ColumnBuilder< + SetField + > { return new I64ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I64ColumnBuilder> { + autoInc(): I64ColumnBuilder< + SetField + > { return new I64ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -828,11 +883,12 @@ export class I64Builder export class I128Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.I128); } @@ -854,13 +910,17 @@ export class I128Builder set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): I128ColumnBuilder> { + primaryKey(): I128ColumnBuilder< + SetField + > { return new I128ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I128ColumnBuilder> { + autoInc(): I128ColumnBuilder< + SetField + > { return new I128ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -879,11 +939,12 @@ export class I128Builder export class I256Builder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ constructor() { super(AlgebraicType.I256); } @@ -905,13 +966,17 @@ export class I256Builder set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): I256ColumnBuilder> { + primaryKey(): I256ColumnBuilder< + SetField + > { return new I256ColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) ); } - autoInc(): I256ColumnBuilder> { + autoInc(): I256ColumnBuilder< + SetField + > { return new I256ColumnBuilder( this, set(defaultMetadata, { isAutoIncrement: true }) @@ -929,7 +994,8 @@ export class I256Builder export class F32Builder extends TypeBuilder - implements Defaultable { + implements Defaultable +{ constructor() { super(AlgebraicType.F32); } @@ -945,7 +1011,8 @@ export class F32Builder export class F64Builder extends TypeBuilder - implements Defaultable { + implements Defaultable +{ constructor() { super(AlgebraicType.F64); } @@ -962,10 +1029,11 @@ export class F64Builder export class BoolBuilder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ constructor() { super(AlgebraicType.Bool); } @@ -987,7 +1055,9 @@ export class BoolBuilder set(defaultMetadata, { isUnique: true }) ); } - primaryKey(): BoolColumnBuilder> { + primaryKey(): BoolColumnBuilder< + SetField + > { return new BoolColumnBuilder( this, set(defaultMetadata, { isPrimaryKey: true }) @@ -1006,10 +1076,11 @@ export class BoolBuilder export class StringBuilder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ constructor() { super(AlgebraicType.String); } @@ -1054,7 +1125,8 @@ export class ArrayBuilder> Array>, { tag: 'Array'; value: InferSpacetimeTypeOfTypeBuilder } > - implements Defaultable>, any> { + implements Defaultable>, any> +{ /** * The phantom element type of the array for TypeScript */ @@ -1065,7 +1137,10 @@ export class ArrayBuilder> } default( value: Array> - ): ArrayColumnBuilder> { + ): ArrayColumnBuilder< + Element, + SetField + > { return new ArrayColumnBuilder( this.element, set(defaultMetadata, { defaultValue: value }) @@ -1078,13 +1153,13 @@ export class ByteArrayBuilder Uint8Array, { tag: 'Array'; value: AlgebraicTypeVariants.U8 } > - implements Defaultable { - + implements Defaultable +{ constructor() { super(AlgebraicType.Array(AlgebraicType.U8)); } default( - value: Uint8Array + value: Uint8Array ): ByteArrayColumnBuilder> { return new ByteArrayColumnBuilder( set(defaultMetadata, { defaultValue: value }) @@ -1098,7 +1173,8 @@ export class OptionBuilder> OptionAlgebraicType > implements - Defaultable | undefined, OptionAlgebraicType> { + Defaultable | undefined, OptionAlgebraicType> +{ /** * The phantom value type of the option for TypeScript */ @@ -1138,7 +1214,8 @@ export class ProductBuilder value: { elements: ElementsArrayFromElementsObj }; } > - implements Defaultable, any> { + implements Defaultable, any> +{ readonly typeName: string | undefined; readonly elements: Elements; constructor(elements: Elements, name?: string) { @@ -1158,7 +1235,10 @@ export class ProductBuilder } default( value: ObjectType - ): ProductColumnBuilder> { + ): ProductColumnBuilder< + Elements, + SetField + > { return new ProductColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1199,8 +1279,8 @@ export class RowBuilder extends TypeBuilder< // Value type produced for a given variant key + builder type EnumValue> = IsUnit extends true - ? { tag: K } - : { tag: K; value: InferTypeOfTypeBuilder }; + ? { tag: K } + : { tag: K; value: InferTypeOfTypeBuilder }; export class SumBuilder extends TypeBuilder< EnumType, @@ -1232,19 +1312,23 @@ export class SumBuilder extends TypeBuilder< */ create( ...args: IsUnit extends true - ? [tag: K] // unit variant: only the tag + ? [tag: K] // unit variant: only the tag : [tag: K, value: InferTypeOfTypeBuilder] // payload variant: tag + value ): EnumValue { const [tag, value] = args as [K, unknown]; // just return the shape, types guarantee correctness - return (value === undefined - ? { tag } - : { tag, value }) as EnumValue; + return (value === undefined ? { tag } : { tag, value }) as EnumValue< + K, + Variants[K] + >; } default( value: EnumType - ): SumColumnBuilder> { + ): SumColumnBuilder< + Variants, + SetField + > { return new SumColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1255,27 +1339,31 @@ export class SumBuilder extends TypeBuilder< export class SimpleSumBuilder extends SumBuilder implements - Indexable< - EnumType, - { - tag: 'Sum'; - value: { variants: VariantsArrayFromVariantsObj }; - } - >, - PrimaryKeyable< - EnumType, - { - tag: 'Sum'; - value: { variants: VariantsArrayFromVariantsObj }; - } - > { + Indexable< + EnumType, + { + tag: 'Sum'; + value: { variants: VariantsArrayFromVariantsObj }; + } + >, + PrimaryKeyable< + EnumType, + { + tag: 'Sum'; + value: { variants: VariantsArrayFromVariantsObj }; + } + > +{ index(): SimpleSumColumnBuilder< Variants, SetField >; index>( algorithm: N - ): SimpleSumColumnBuilder>; + ): SimpleSumColumnBuilder< + Variants, + SetField + >; index( algorithm: IndexTypes = 'btree' ): SimpleSumColumnBuilder< @@ -1301,14 +1389,17 @@ export class SimpleSumBuilder export class IdentityBuilder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ constructor() { super(Identity.getAlgebraicType()); } - index(): IdentityColumnBuilder>; + index(): IdentityColumnBuilder< + SetField + >; index>( algorithm: N ): IdentityColumnBuilder>; @@ -1344,7 +1435,9 @@ export class IdentityBuilder } default( value: Identity - ): IdentityColumnBuilder> { + ): IdentityColumnBuilder< + SetField + > { return new IdentityColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1355,10 +1448,11 @@ export class IdentityBuilder export class ConnectionIdBuilder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ constructor() { super(ConnectionId.getAlgebraicType()); } @@ -1370,13 +1464,17 @@ export class ConnectionIdBuilder ): ConnectionIdColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): ConnectionIdColumnBuilder> { + ): ConnectionIdColumnBuilder< + SetField + > { return new ConnectionIdColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): ConnectionIdColumnBuilder> { + unique(): ConnectionIdColumnBuilder< + SetField + > { return new ConnectionIdColumnBuilder( this, set(defaultMetadata, { isUnique: true }) @@ -1413,26 +1511,33 @@ export class ConnectionIdBuilder export class TimestampBuilder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ constructor() { super(Timestamp.getAlgebraicType()); } - index(): TimestampColumnBuilder>; + index(): TimestampColumnBuilder< + SetField + >; index>( algorithm: N ): TimestampColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): TimestampColumnBuilder> { + ): TimestampColumnBuilder< + SetField + > { return new TimestampColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): TimestampColumnBuilder> { + unique(): TimestampColumnBuilder< + SetField + > { return new TimestampColumnBuilder( this, set(defaultMetadata, { isUnique: true }) @@ -1456,7 +1561,9 @@ export class TimestampBuilder } default( value: Timestamp - ): TimestampColumnBuilder> { + ): TimestampColumnBuilder< + SetField + > { return new TimestampColumnBuilder( this, set(defaultMetadata, { defaultValue: value }) @@ -1467,10 +1574,11 @@ export class TimestampBuilder export class TimeDurationBuilder extends TypeBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ constructor() { super(TimeDuration.getAlgebraicType()); } @@ -1482,13 +1590,17 @@ export class TimeDurationBuilder ): TimeDurationColumnBuilder>; index( algorithm: IndexTypes = 'btree' - ): TimeDurationColumnBuilder> { + ): TimeDurationColumnBuilder< + SetField + > { return new TimeDurationColumnBuilder( this, set(defaultMetadata, { indexType: algorithm }) ); } - unique(): TimeDurationColumnBuilder> { + unique(): TimeDurationColumnBuilder< + SetField + > { return new TimeDurationColumnBuilder( this, set(defaultMetadata, { isUnique: true }) @@ -1556,8 +1668,8 @@ const defaultMetadata: ColumnMetadata = {}; * * It carries both a phantom TypeScript type (the `Type`) and * runtime algebraic type information. - * - * IMPORTANT! We have deliberately chosen to not have {@link ColumnBuilder} + * + * IMPORTANT! We have deliberately chosen to not have {@link ColumnBuilder} * extend {@link TypeBuilder} so that you cannot pass a {@link ColumnBuilder} * where a {@link TypeBuilder} is expected. i.e. We want to maintain * contravariance for functions that accept {@link TypeBuilder} parameters. @@ -1579,11 +1691,12 @@ export class ColumnBuilder< export class U8ColumnBuilder = DefaultMetadata> extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): U8ColumnBuilder>; index>( algorithm: N @@ -1615,22 +1728,26 @@ export class U8ColumnBuilder = DefaultMetadata> ); } default(value: number): U8ColumnBuilder> { - return new U8ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + return new U8ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class U16ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): U16ColumnBuilder>; index>( algorithm: N @@ -1661,23 +1778,29 @@ export class U16ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): U16ColumnBuilder> { - return new U16ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: number + ): U16ColumnBuilder> { + return new U16ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class U32ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): U32ColumnBuilder>; index>( algorithm: N @@ -1708,23 +1831,29 @@ export class U32ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): U32ColumnBuilder> { - return new U32ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: number + ): U32ColumnBuilder> { + return new U32ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class U64ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): U64ColumnBuilder>; index>( algorithm: N @@ -1755,23 +1884,29 @@ export class U64ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): U64ColumnBuilder> { - return new U64ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: bigint + ): U64ColumnBuilder> { + return new U64ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class U128ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): U128ColumnBuilder>; index>( algorithm: N @@ -1802,23 +1937,29 @@ export class U128ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): U128ColumnBuilder> { - return new U128ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: bigint + ): U128ColumnBuilder> { + return new U128ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class U256ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): U256ColumnBuilder>; index>( algorithm: N @@ -1849,21 +1990,27 @@ export class U256ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): U256ColumnBuilder> { - return new U256ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: bigint + ): U256ColumnBuilder> { + return new U256ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class I8ColumnBuilder = DefaultMetadata> extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): I8ColumnBuilder>; index>( algorithm: N @@ -1895,22 +2042,26 @@ export class I8ColumnBuilder = DefaultMetadata> ); } default(value: number): I8ColumnBuilder> { - return new I8ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + return new I8ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class I16ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): I16ColumnBuilder>; index>( algorithm: N @@ -1941,23 +2092,29 @@ export class I16ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): I16ColumnBuilder> { - return new I16ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: number + ): I16ColumnBuilder> { + return new I16ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class I32ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): I32ColumnBuilder>; index>( algorithm: N @@ -1988,23 +2145,29 @@ export class I32ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: number): I32ColumnBuilder> { - return new I32ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: number + ): I32ColumnBuilder> { + return new I32ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class I64ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): I64ColumnBuilder>; index>( algorithm: N @@ -2035,23 +2198,29 @@ export class I64ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): I64ColumnBuilder> { - return new I64ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: bigint + ): I64ColumnBuilder> { + return new I64ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class I128ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): I128ColumnBuilder>; index>( algorithm: N @@ -2082,23 +2251,29 @@ export class I128ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): I128ColumnBuilder> { - return new I128ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: bigint + ): I128ColumnBuilder> { + return new I128ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class I256ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - AutoIncrementable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + AutoIncrementable, + Defaultable +{ index(): I256ColumnBuilder>; index>( algorithm: N @@ -2129,46 +2304,64 @@ export class I256ColumnBuilder< set(this.columnMetadata, { isAutoIncrement: true }) ); } - default(value: bigint): I256ColumnBuilder> { - return new I256ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: bigint + ): I256ColumnBuilder> { + return new I256ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class F32ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder - implements Defaultable { - default(value: number): F32ColumnBuilder> { - return new F32ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + implements Defaultable +{ + default( + value: number + ): F32ColumnBuilder> { + return new F32ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class F64ColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder - implements Defaultable { - default(value: number): F64ColumnBuilder> { - return new F64ColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + implements Defaultable +{ + default( + value: number + ): F64ColumnBuilder> { + return new F64ColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class BoolColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ index(): BoolColumnBuilder>; index>( algorithm: N @@ -2193,22 +2386,28 @@ export class BoolColumnBuilder< set(this.columnMetadata, { isPrimaryKey: true }) ); } - default(value: boolean): BoolColumnBuilder> { - return new BoolColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: boolean + ): BoolColumnBuilder> { + return new BoolColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class StringColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ index(): StringColumnBuilder>; index>( algorithm: N @@ -2233,80 +2432,98 @@ export class StringColumnBuilder< set(this.columnMetadata, { isPrimaryKey: true }) ); } - default(value: string): StringColumnBuilder> { - return new StringColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + default( + value: string + ): StringColumnBuilder> { + return new StringColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class ArrayColumnBuilder< - Element extends TypeBuilder, - M extends ColumnMetadata< - Array> - > = DefaultMetadata, -> + Element extends TypeBuilder, + M extends ColumnMetadata< + Array> + > = DefaultMetadata, + > extends ColumnBuilder< Array>, { tag: 'Array'; value: InferSpacetimeTypeOfTypeBuilder }, M > implements - Defaultable< - Array>, - AlgebraicTypeVariants.Array - > { + Defaultable< + Array>, + AlgebraicTypeVariants.Array + > +{ default( value: Array> ): ArrayColumnBuilder< Element, SetField>> > { - return new ArrayColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + return new ArrayColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } -export class ByteArrayColumnBuilder = DefaultMetadata> - extends ColumnBuilder = DefaultMetadata, +> extends ColumnBuilder< + Uint8Array, + { tag: 'Array'; value: AlgebraicTypeVariants.U8; - }, M> { + }, + M +> { constructor(metadata: M) { super(new TypeBuilder(AlgebraicType.Array(AlgebraicType.U8)), metadata); } } export class OptionColumnBuilder< - Value extends TypeBuilder, - M extends ColumnMetadata< - InferTypeOfTypeBuilder | undefined - > = DefaultMetadata, -> + Value extends TypeBuilder, + M extends ColumnMetadata< + InferTypeOfTypeBuilder | undefined + > = DefaultMetadata, + > extends ColumnBuilder< InferTypeOfTypeBuilder | undefined, OptionAlgebraicType, M > implements - Defaultable | undefined, OptionAlgebraicType> { + Defaultable | undefined, OptionAlgebraicType> +{ default( value: InferTypeOfTypeBuilder | undefined ): OptionColumnBuilder< Value, SetField | undefined> > { - return new OptionColumnBuilder(this.typeBuilder, set(this.columnMetadata, { - defaultValue: value, - })); + return new OptionColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); } } export class ProductColumnBuilder< - Elements extends ElementsObj, - M extends ColumnMetadata> = DefaultMetadata, -> + Elements extends ElementsObj, + M extends ColumnMetadata> = DefaultMetadata, + > extends ColumnBuilder< ObjectType, { @@ -2315,10 +2532,14 @@ export class ProductColumnBuilder< }, M > - implements Defaultable, AlgebraicTypeVariants.Product> { + implements Defaultable, AlgebraicTypeVariants.Product> +{ default( value: ObjectType - ): ProductColumnBuilder> { + ): ProductColumnBuilder< + Elements, + SetField + > { return new ProductColumnBuilder( this.typeBuilder, set(this.columnMetadata, { defaultValue: value }) @@ -2327,18 +2548,22 @@ export class ProductColumnBuilder< } export class SumColumnBuilder< - Variants extends VariantsObj, - M extends ColumnMetadata> = DefaultMetadata, -> + Variants extends VariantsObj, + M extends ColumnMetadata> = DefaultMetadata, + > extends ColumnBuilder< EnumType, { tag: 'Sum'; value: { variants: VariantsArrayFromVariantsObj } }, M > - implements Defaultable, AlgebraicTypeVariants.Sum> { + implements Defaultable, AlgebraicTypeVariants.Sum> +{ default( value: EnumType - ): SumColumnBuilder> { + ): SumColumnBuilder< + Variants, + SetField + > { return new SumColumnBuilder( this.typeBuilder, set(this.columnMetadata, { defaultValue: value }) @@ -2347,20 +2572,24 @@ export class SumColumnBuilder< } export class SimpleSumColumnBuilder< - Variants extends VariantsObj, - M extends ColumnMetadata> = DefaultMetadata, -> + Variants extends VariantsObj, + M extends ColumnMetadata> = DefaultMetadata, + > extends SumColumnBuilder implements - Indexable, AlgebraicTypeVariants.Sum>, - PrimaryKeyable, AlgebraicTypeVariants.Sum> { + Indexable, AlgebraicTypeVariants.Sum>, + PrimaryKeyable, AlgebraicTypeVariants.Sum> +{ index(): SimpleSumColumnBuilder< Variants, SetField >; index>( algorithm: N - ): SimpleSumColumnBuilder>; + ): SimpleSumColumnBuilder< + Variants, + SetField + >; index( algorithm: IndexTypes = 'btree' ): SimpleSumColumnBuilder< @@ -2384,14 +2613,15 @@ export class SimpleSumColumnBuilder< } export class IdentityColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ index(): IdentityColumnBuilder>; index>( algorithm: N @@ -2419,19 +2649,23 @@ export class IdentityColumnBuilder< default( value: Identity ): IdentityColumnBuilder> { - return new IdentityColumnBuilder(this.typeBuilder, set(this.columnMetadata, { defaultValue: value })); + return new IdentityColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { defaultValue: value }) + ); } } export class ConnectionIdColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ index(): ConnectionIdColumnBuilder>; index>( algorithm: N @@ -2459,19 +2693,23 @@ export class ConnectionIdColumnBuilder< default( value: ConnectionId ): ConnectionIdColumnBuilder> { - return new ConnectionIdColumnBuilder(this.typeBuilder, set(this.columnMetadata, { defaultValue: value })); + return new ConnectionIdColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { defaultValue: value }) + ); } } export class TimestampColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ index(): TimestampColumnBuilder>; index>( algorithm: N @@ -2499,19 +2737,23 @@ export class TimestampColumnBuilder< default( value: Timestamp ): TimestampColumnBuilder> { - return new TimestampColumnBuilder(this.typeBuilder, set(this.columnMetadata, { defaultValue: value })); + return new TimestampColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { defaultValue: value }) + ); } } export class TimeDurationColumnBuilder< - M extends ColumnMetadata = DefaultMetadata, -> + M extends ColumnMetadata = DefaultMetadata, + > extends ColumnBuilder implements - Indexable, - Uniqueable, - PrimaryKeyable, - Defaultable { + Indexable, + Uniqueable, + PrimaryKeyable, + Defaultable +{ index(): TimeDurationColumnBuilder>; index>( algorithm: N @@ -2539,7 +2781,10 @@ export class TimeDurationColumnBuilder< default( value: TimeDuration ): TimeDurationColumnBuilder> { - return new TimeDurationColumnBuilder(this.typeBuilder, set(this.columnMetadata, { defaultValue: value })); + return new TimeDurationColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { defaultValue: value }) + ); } } @@ -2592,10 +2837,10 @@ const enumImpl = ((nameOrObj: any, maybeObj?: any) => { let obj: any = nameOrObj; let name: string | undefined = undefined; - if (typeof nameOrObj === "string") { + if (typeof nameOrObj === 'string') { if (!maybeObj) { throw new TypeError( - "When providing a name, you must also provide the variants object or array." + 'When providing a name, you must also provide the variants object or array.' ); } obj = maybeObj; @@ -2834,9 +3079,9 @@ export const t = { /** * Creates a lazily-evaluated {@link TypeBuilder}. This is useful for creating - * recursive types, such as a tree or linked list. + * recursive types, such as a tree or linked list. * @param thunk A function that returns a {@link TypeBuilder}. - * @returns A proxy {@link TypeBuilder} that evaluates the thunk on first access. + * @returns A proxy {@link TypeBuilder} that evaluates the thunk on first access. */ lazy TypeBuilder>( thunk: Build @@ -2849,7 +3094,7 @@ export const t = { get(_t, prop, recv) { const target = get() as any; const val = Reflect.get(target, prop, recv); - return typeof val === "function" ? val.bind(target) : val; + return typeof val === 'function' ? val.bind(target) : val; }, set(_t, prop, value, recv) { return Reflect.set(get() as any, prop, value, recv); @@ -2937,7 +3182,7 @@ export const t = { /** * This is a convenience method for creating a column with the {@link ByteArray} type. - * You can create a column of the same type by constructing an `array` of `u8`. + * You can create a column of the same type by constructing an `array` of `u8`. * The TypeScript representation is {@link Uint8Array}. * @returns A new {@link ByteArrayBuilder} instance with the {@link ByteArray} type. */ diff --git a/crates/bindings-typescript/src/lib/type_util.ts b/crates/bindings-typescript/src/lib/type_util.ts index 1c0bc697284..55aa63ec162 100644 --- a/crates/bindings-typescript/src/lib/type_util.ts +++ b/crates/bindings-typescript/src/lib/type_util.ts @@ -1,16 +1,22 @@ -import type { ConnectionId } from "./connection_id"; -import type { Identity } from "./identity"; -import type { ScheduleAt } from "./schedule_at"; -import type { TimeDuration } from "./time_duration"; -import type { Timestamp } from "./timestamp"; - -type DoNotPrettify = Identity | ConnectionId | Timestamp | TimeDuration | ScheduleAt; +import type { ConnectionId } from './connection_id'; +import type { Identity } from './identity'; +import type { ScheduleAt } from './schedule_at'; +import type { TimeDuration } from './time_duration'; +import type { Timestamp } from './timestamp'; + +type DoNotPrettify = + | Identity + | ConnectionId + | Timestamp + | TimeDuration + | ScheduleAt; /** * Utility to make TS show cleaner types by flattening intersections. */ -export type Prettify = - T extends DoNotPrettify ? T : { [K in keyof T]: T[K] } & {}; +export type Prettify = T extends DoNotPrettify + ? T + : { [K in keyof T]: T[K] } & {}; /** * Helper function to sets a field in an object @@ -42,14 +48,15 @@ export type Values = T[keyof T]; */ export type CollapseTuple
= A extends [infer T] ? T : A; -type CamelCaseImpl = - S extends `${infer Head}_${infer Tail}` ? `${Head}${Capitalize>}` : - S extends `${infer Head}-${infer Tail}` ? `${Head}${Capitalize>}` : - S; +type CamelCaseImpl = S extends `${infer Head}_${infer Tail}` + ? `${Head}${Capitalize>}` + : S extends `${infer Head}-${infer Tail}` + ? `${Head}${Capitalize>}` + : S; /** * Convert "Some_identifier-name" -> "someIdentifierName" * - No spaces; allowed separators: "_" and "-" * - Normalizes the *first* character to lowercase (e.g. "User_Name" -> "userName") */ -export type CamelCase = Uncapitalize>; \ No newline at end of file +export type CamelCase = Uncapitalize>; diff --git a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts index 26ccbb78c89..5c10b2aed8c 100644 --- a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts @@ -18,7 +18,10 @@ export interface SpacetimeDBProviderProps< export function SpacetimeDBProvider< DbConnection extends DbConnectionImpl, ->({ connectionBuilder, children }: SpacetimeDBProviderProps): React.JSX.Element { +>({ + connectionBuilder, + children, +}: SpacetimeDBProviderProps): React.JSX.Element { // Holds the imperative connection instance when (and only when) we’re on the client. const connRef = React.useRef(null); const getConnection = React.useCallback(() => connRef.current, []); @@ -29,7 +32,7 @@ export function SpacetimeDBProvider< token: undefined, connectionId: ConnectionId.random(), connectionError: undefined, - getConnection: getConnection as ConnectionState["getConnection"], + getConnection: getConnection as ConnectionState['getConnection'], }); // Build on the client only; useEffect won't run during SSR. @@ -37,7 +40,7 @@ export function SpacetimeDBProvider< if (!connRef.current) { connRef.current = connectionBuilder.build(); } - console.log("HAPPPP"); + console.log('HAPPPP'); // Register callback for onConnect to update state const onConnect = (conn: DbConnection) => { setState(s => ({ @@ -48,13 +51,18 @@ export function SpacetimeDBProvider< connectionId: conn.connectionId, })); }; - const onDisconnect = (ctx: ErrorContextInterface>) => { + const onDisconnect = ( + ctx: ErrorContextInterface> + ) => { setState(s => ({ ...s, isActive: ctx.isActive, })); }; - const onConnectError = (ctx: ErrorContextInterface>, err: Error) => { + const onConnectError = ( + ctx: ErrorContextInterface>, + err: Error + ) => { setState(s => ({ ...s, isActive: ctx.isActive, @@ -88,4 +96,4 @@ export function SpacetimeDBProvider< { value: state }, children ); - } \ No newline at end of file +} diff --git a/crates/bindings-typescript/src/react/connection_state.ts b/crates/bindings-typescript/src/react/connection_state.ts index f9c333c100f..056a3c7b3d3 100644 --- a/crates/bindings-typescript/src/react/connection_state.ts +++ b/crates/bindings-typescript/src/react/connection_state.ts @@ -1,6 +1,6 @@ -import type { ConnectionId } from "../lib/connection_id"; -import type { Identity } from "../lib/identity"; -import type { DbConnectionImpl } from "../sdk/db_connection_impl"; +import type { ConnectionId } from '../lib/connection_id'; +import type { Identity } from '../lib/identity'; +import type { DbConnectionImpl } from '../sdk/db_connection_impl'; export type ConnectionState = { isActive: boolean; @@ -8,5 +8,7 @@ export type ConnectionState = { token?: string; connectionId: ConnectionId; connectionError?: Error; - getConnection>(): DbConnection | null; + getConnection< + DbConnection extends DbConnectionImpl, + >(): DbConnection | null; }; diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index 3d43e672718..6a16f1a2a38 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -1,17 +1,18 @@ -import { useCallback, useEffect, useRef } from "react"; -import type { InferTypeOfRow } from "../lib/type_builders"; -import type { UntypedReducerDef } from "../sdk/reducers"; -import { useSpacetimeDB, } from "./useSpacetimeDB"; +import { useCallback, useEffect, useRef } from 'react'; +import type { InferTypeOfRow } from '../lib/type_builders'; +import type { UntypedReducerDef } from '../sdk/reducers'; +import { useSpacetimeDB } from './useSpacetimeDB'; +import type { Prettify } from '../lib/type_util'; export function useReducer( - reducerDef: ReducerDef, -): (params: InferTypeOfRow) => void { + reducerDef: ReducerDef +): (params: Prettify>) => void { const { getConnection, isActive } = useSpacetimeDB(); const reducerName = reducerDef.accessorName; // Holds calls made before the connection exists const queueRef = useRef< - Array> + Array>> >([]); // Flush when we finally have a connection @@ -20,26 +21,27 @@ export function useReducer( if (!conn) { return; } - const fn = - (conn.reducers as any)[reducerName] as - (p: InferTypeOfRow) => void; + const fn = (conn.reducers as any)[reducerName] as ( + p: InferTypeOfRow + ) => void; if (queueRef.current.length) { const pending = queueRef.current.splice(0); for (const params of pending) fn(params); } }, [getConnection, reducerName, isActive]); - return useCallback(( - params: InferTypeOfRow - ) => { - const conn = getConnection(); - if (!conn) { - queueRef.current.push(params); - return; - } - const fn = - (conn.reducers as any)[reducerName] as - (p: InferTypeOfRow) => void; - return fn(params); - }, [getConnection, reducerName]); -} \ No newline at end of file + return useCallback( + (params: Prettify>) => { + const conn = getConnection(); + if (!conn) { + queueRef.current.push(params); + return; + } + const fn = (conn.reducers as any)[reducerName] as ( + p: Prettify> + ) => void; + return fn(params); + }, + [getConnection, reducerName] + ); +} diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index 619a5b39dea..9298eb3fda4 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -3,7 +3,9 @@ import type { DbConnectionImpl } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -export const SpacetimeDBContext = createContext(undefined); +export const SpacetimeDBContext = createContext( + undefined +); // Throws an error if used outside of a SpacetimeDBProvider // Error is caught by other hooks like useTable so they can provide better error messages @@ -15,4 +17,4 @@ export function useSpacetimeDB(): ConnectionState { ); } return context; -} \ No newline at end of file +} diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index fe337e1780c..98b4d55a68c 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -206,13 +206,11 @@ type ColumnsFromRow = { * }); * ``` */ -export function useTable< - TableDef extends UntypedTableDef, ->( +export function useTable( tableDef: TableDef, where: Expr>>, - callbacks?: UseTableCallbacks> -): readonly RowType[]; + callbacks?: UseTableCallbacks>> +): readonly Prettify>[]; /** * React hook to subscribe to a table in SpacetimeDB and receive live updates as rows are inserted, updated, or deleted. @@ -247,22 +245,18 @@ export function useTable< * }); * ``` */ -export function useTable< - TableDef extends UntypedTableDef, ->( +export function useTable( tableDef: TableDef, - callbacks?: UseTableCallbacks> -): readonly RowType[]; + callbacks?: UseTableCallbacks>> +): readonly Prettify>[]; -export function useTable< - TableDef extends UntypedTableDef, ->( +export function useTable( tableDef: TableDef, whereClauseOrCallbacks?: | Expr>> | UseTableCallbacks>, callbacks?: UseTableCallbacks> -): readonly RowType[] { +): readonly Prettify>[] { type UseTableRowType = RowType; const tableName = tableDef.name; let whereClause: Expr> | undefined; @@ -271,7 +265,9 @@ export function useTable< typeof whereClauseOrCallbacks === 'object' && 'type' in whereClauseOrCallbacks ) { - whereClause = whereClauseOrCallbacks as Expr>; + whereClause = whereClauseOrCallbacks as Expr< + ColumnsFromRow + >; } else { callbacks = whereClauseOrCallbacks as | UseTableCallbacks @@ -294,27 +290,32 @@ export function useTable< (whereClause ? ` WHERE ${toString(whereClause)}` : ''); const latestTransactionEvent = useRef(null); - const lastSnapshotRef = useRef(null); + const lastSnapshotRef = useRef[] | null>( + null + ); const whereKey = whereClause ? toString(whereClause) : ''; - const computeSnapshot = useCallback((): readonly UseTableRowType[] => { - const connection = connectionState.getConnection(); - if (!connection) { - return []; - } - const table = connection.db[tableName]; - const result: readonly UseTableRowType[] = whereClause - ? (Array.from(table.iter()).filter(row => evaluate(whereClause, row as UseTableRowType)) as UseTableRowType[]) - : (Array.from(table.iter()) as UseTableRowType[]); - return result; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [connectionState, tableName, whereKey, subscribeApplied]); + const computeSnapshot = + useCallback((): readonly Prettify[] => { + const connection = connectionState.getConnection(); + if (!connection) { + return []; + } + const table = connection.db[tableName]; + const result: readonly Prettify[] = whereClause + ? (Array.from(table.iter()).filter(row => + evaluate(whereClause, row as UseTableRowType) + ) as Prettify[]) + : (Array.from(table.iter()) as Prettify[]); + return result; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [connectionState, tableName, whereKey, subscribeApplied]); useEffect(() => { const connection = connectionState.getConnection()!; if (connectionState.isActive && connection) { - const cancel = connection + const cancel = connection .subscriptionBuilder() .onApplied(() => { setSubscribeApplied(true); @@ -328,7 +329,10 @@ export function useTable< const subscribe = useCallback( (onStoreChange: () => void) => { - const onInsert = (ctx: EventContextInterface, row: any) => { + const onInsert = ( + ctx: EventContextInterface, + row: any + ) => { if (whereClause && !evaluate(whereClause, row)) { return; } @@ -343,7 +347,10 @@ export function useTable< } }; - const onDelete = (ctx: EventContextInterface, row: any) => { + const onDelete = ( + ctx: EventContextInterface, + row: any + ) => { if (whereClause && !evaluate(whereClause, row)) { return; } @@ -358,7 +365,11 @@ export function useTable< } }; - const onUpdate = (ctx: EventContextInterface, oldRow: any, newRow: any) => { + const onUpdate = ( + ctx: EventContextInterface, + oldRow: any, + newRow: any + ) => { const change = classifyMembership(whereClause, oldRow, newRow); switch (change) { @@ -412,7 +423,7 @@ export function useTable< ] ); - const getSnapshot = useCallback((): readonly UseTableRowType[] => { + const getSnapshot = useCallback((): readonly Prettify[] => { if (!lastSnapshotRef.current) { lastSnapshotRef.current = computeSnapshot(); } diff --git a/crates/bindings-typescript/src/sdk/client_cache.ts b/crates/bindings-typescript/src/sdk/client_cache.ts index 8f07f15af37..afadb578a50 100644 --- a/crates/bindings-typescript/src/sdk/client_cache.ts +++ b/crates/bindings-typescript/src/sdk/client_cache.ts @@ -3,13 +3,16 @@ import type { UntypedTableDef } from '../lib/table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; import { TableCache } from './table_cache.ts'; -type TableName = - [SchemaDef] extends [UntypedSchemaDef] ? TableNamesOf : string; +type TableName = [SchemaDef] extends [UntypedSchemaDef] + ? TableNamesOf + : string; -export type TableDefForTableName> = - [SchemaDef] extends [UntypedSchemaDef] - ? (SchemaDef['tables'][number] & { name: N }) - : UntypedTableDef; +export type TableDefForTableName< + SchemaDef extends UntypedSchemaDef, + N extends TableName, +> = [SchemaDef] extends [UntypedSchemaDef] + ? SchemaDef['tables'][number] & { name: N } + : UntypedTableDef; type TableCacheForTableName< RemoteModule extends UntypedRemoteModule, @@ -21,14 +24,24 @@ type TableCacheForTableName< * while preserving the correspondence between the key and value type. */ class TableMap { - private readonly map: Map>> = new Map(); + private readonly map: Map< + string, + TableCacheForTableName> + > = new Map(); - get>(key: K): TableCacheForTableName | undefined { + get>( + key: K + ): TableCacheForTableName | undefined { // Cast required: a Map can't refine the union to the exact K-specific member on get(key: K). - return this.map.get(key) as TableCacheForTableName | undefined; + return this.map.get(key) as + | TableCacheForTableName + | undefined; } - set>(key: K, value: TableCacheForTableName): this { + set>( + key: K, + value: TableCacheForTableName + ): this { this.map.set(key, value); return this; } @@ -42,10 +55,22 @@ class TableMap { } // optional: iteration stays broadly typed (cannot express per-key relation here) - keys(): IterableIterator { return this.map.keys(); } - values(): IterableIterator>> { return this.map.values(); } - entries(): IterableIterator<[string, TableCacheForTableName>]> { return this.map.entries(); } - [Symbol.iterator]() { return this.entries(); } + keys(): IterableIterator { + return this.map.keys(); + } + values(): IterableIterator< + TableCacheForTableName> + > { + return this.map.values(); + } + entries(): IterableIterator< + [string, TableCacheForTableName>] + > { + return this.map.entries(); + } + [Symbol.iterator]() { + return this.entries(); + } } /** @@ -65,7 +90,9 @@ export class ClientCache { * and the return type matches that table. * - If SchemaDef is undefined, `name` is string and the return type is untyped. */ - getTable>(name: N): TableCacheForTableName { + getTable>( + name: N + ): TableCacheForTableName { const table = this.tables.get(name); if (!table) { console.error( diff --git a/crates/bindings-typescript/src/sdk/client_table.ts b/crates/bindings-typescript/src/sdk/client_table.ts index bafaf8eb49f..ac20b83decf 100644 --- a/crates/bindings-typescript/src/sdk/client_table.ts +++ b/crates/bindings-typescript/src/sdk/client_table.ts @@ -1,11 +1,16 @@ -import type { ReadonlyIndexes } from "../lib/indexes"; -import type { TableNamesOf } from "../lib/schema"; -import type { ReadonlyTableMethods, RowType, TableIndexes, UntypedTableDef } from "../lib/table"; -import type { ColumnBuilder } from "../lib/type_builders"; -import type { Prettify } from "../lib/type_util"; -import type { TableDefForTableName } from "./client_cache"; -import type { EventContextInterface } from "./event_context"; -import type { UntypedRemoteModule } from "./spacetime_module"; +import type { ReadonlyIndexes } from '../lib/indexes'; +import type { TableNamesOf } from '../lib/schema'; +import type { + ReadonlyTableMethods, + RowType, + TableIndexes, + UntypedTableDef, +} from '../lib/table'; +import type { ColumnBuilder } from '../lib/type_builders'; +import type { Prettify } from '../lib/type_util'; +import type { TableDefForTableName } from './client_cache'; +import type { EventContextInterface } from './event_context'; +import type { UntypedRemoteModule } from './spacetime_module'; export type ClientTablePrimaryKeyMethods< RemoteModule extends UntypedRemoteModule, @@ -16,13 +21,25 @@ export type ClientTablePrimaryKeyMethods< * Requires that the table has a primary key defined. * @param cb The callback to invoke when a row is updated. */ - onUpdate(cb: (ctx: EventContextInterface, oldRow: RowType>, newRow: RowType>) => void): void; + onUpdate( + cb: ( + ctx: EventContextInterface, + oldRow: RowType>, + newRow: RowType> + ) => void + ): void; /** - * Removes a previously registered update event listener. + * Removes a previously registered update event listener. * @param cb The callback to remove from the update event listeners. */ - removeOnUpdate(cb: (ctx: EventContextInterface, oldRow: RowType>, newRow: RowType>) => void): void; + removeOnUpdate( + cb: ( + ctx: EventContextInterface, + oldRow: RowType>, + newRow: RowType> + ) => void + ): void; }; export type ClientTableMethods< @@ -32,24 +49,44 @@ export type ClientTableMethods< /** * Registers a callback to be invoked when a row is inserted into the table. */ - onInsert(cb: (ctx: EventContextInterface, row: RowType>) => void): void; + onInsert( + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void + ): void; /** - * Removes a previously registered insert event listener. + * Removes a previously registered insert event listener. * @param cb The callback to remove from the insert event listeners. */ - removeOnInsert(cb: (ctx: EventContextInterface, row: RowType>) => void): void; + removeOnInsert( + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void + ): void; /** * Registers a callback to be invoked when a row is deleted from the table. */ - onDelete(cb: (ctx: EventContextInterface, row: RowType>) => void): void; + onDelete( + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void + ): void; /** * Removes a previously registered delete event listener. * @param cb The callback to remove from the delete event listeners. */ - removeOnDelete(cb: (ctx: EventContextInterface, row: RowType>) => void): void; + removeOnDelete( + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void + ): void; }; /** @@ -64,21 +101,27 @@ export type ClientTable< TableName extends TableNamesOf, > = Prettify< ClientTableCore & - ReadonlyIndexes, TableIndexes>> + ReadonlyIndexes< + TableDefForTableName, + TableIndexes> + > >; -type HasPrimaryKey = - ColumnsHavePrimaryKey; +type HasPrimaryKey = ColumnsHavePrimaryKey< + TableDef['columns'] +>; type ColumnsHavePrimaryKey< - Cs extends Record> -> = - { - [K in keyof Cs]: - Cs[K] extends ColumnBuilder - ? (M extends { isPrimaryKey: true } ? true : never) - : never - }[keyof Cs] extends true ? true : false; + Cs extends Record>, +> = { + [K in keyof Cs]: Cs[K] extends ColumnBuilder + ? M extends { isPrimaryKey: true } + ? true + : never + : never; +}[keyof Cs] extends true + ? true + : false; type MaybePKMethods< RemoteModule extends UntypedRemoteModule, @@ -92,8 +135,7 @@ type MaybePKMethods< export type ClientTableCoreImplementable< RemoteModule extends UntypedRemoteModule, TableName extends TableNamesOf, -> = - ReadonlyTableMethods> & +> = ReadonlyTableMethods> & ClientTableMethods & // always present but optional -> statically known member set MaybePKMethods; @@ -105,11 +147,8 @@ export type ClientTableCoreImplementable< export type ClientTableCore< RemoteModule extends UntypedRemoteModule, TableName extends TableNamesOf, -> = - ReadonlyTableMethods> & +> = ReadonlyTableMethods> & ClientTableMethods & - ( - HasPrimaryKey> extends true - ? ClientTablePrimaryKeyMethods - : {} - ); \ No newline at end of file + (HasPrimaryKey> extends true + ? ClientTablePrimaryKeyMethods + : {}); diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index 1e5b4be9abf..1842f31fde2 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -1,6 +1,12 @@ import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl'; import { EventEmitter } from './event_emitter'; -import type { DbConnectionConfig, ErrorContextInterface, Identity, RemoteModuleOf, SubscriptionEventContextInterface } from '../'; +import type { + DbConnectionConfig, + ErrorContextInterface, + Identity, + RemoteModuleOf, + SubscriptionEventContextInterface, +} from '../'; import { ensureMinimumVersionOrThrow } from './version'; import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; @@ -12,9 +18,7 @@ import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; * always have a concrete RemoteModule type in those cases. Even if they user * did do this, they would just lose type safety on the RemoteModule. */ -export class DbConnectionBuilder< - DbConnection extends DbConnectionImpl -> { +export class DbConnectionBuilder> { #uri?: URL; #nameOrAddress?: string; #identity?: Identity; @@ -35,7 +39,9 @@ export class DbConnectionBuilder< */ constructor( private remoteModule: RemoteModuleOf, - private dbConnectionCtor: (config: DbConnectionConfig>) => DbConnection + private dbConnectionCtor: ( + config: DbConnectionConfig> + ) => DbConnection ) { this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn; } @@ -181,7 +187,12 @@ export class DbConnectionBuilder< * }); * ``` */ - onConnectError(callback: (ctx: ErrorContextInterface>, error: Error) => void): this { + onConnectError( + callback: ( + ctx: ErrorContextInterface>, + error: Error + ) => void + ): this { this.#emitter.on('connectError', callback); return this; } @@ -213,7 +224,10 @@ export class DbConnectionBuilder< * @throws {Error} Throws an error if called multiple times on the same `DbConnectionBuilder`. */ onDisconnect( - callback: (ctx: ErrorContextInterface>, error?: Error | undefined) => void + callback: ( + ctx: ErrorContextInterface>, + error?: Error | undefined + ) => void ): this { this.#emitter.on('disconnect', callback); return this; @@ -258,6 +272,6 @@ export class DbConnectionBuilder< confirmedReads: this.#confirmedReads, createWSFn: this.#createWSFn, remoteModule: this.remoteModule, - }) + }); } } diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index e2a7ced5371..cae78d0564c 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -1,8 +1,5 @@ import { ConnectionId, ProductType } from '../'; -import { - AlgebraicType, - type ComparablePrimitive, -} from '../'; +import { AlgebraicType, type ComparablePrimitive } from '../'; import { BinaryReader } from '../'; import { BinaryWriter } from '../'; import BsatnRowList from './client_api/bsatn_row_list_type.ts'; @@ -31,7 +28,10 @@ import type { UnsubscribeAppliedMessage, } from './message_types.ts'; import type { ReducerEvent } from './reducer_event.ts'; -import { type RemoteModule, type UntypedRemoteModule } from './spacetime_module.ts'; +import { + type RemoteModule, + type UntypedRemoteModule, +} from './spacetime_module.ts'; import { TableCache, type Operation, @@ -48,14 +48,20 @@ import { } from './subscription_builder_impl.ts'; import { stdbLogger } from './logger.ts'; import { fromByteArray } from 'base64-js'; -import type { ReducerEventInfo, ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers.ts'; +import type { + ReducerEventInfo, + ReducersView, + SetReducerFlags, + UntypedReducersDef, +} from './reducers.ts'; import type { ClientDbView } from './db_view.ts'; import type { UntypedTableDef } from '../lib/table.ts'; import { toCamelCase } from '../lib/utils.ts'; export { DbConnectionBuilder, SubscriptionBuilderImpl, TableCache, type Event }; -export type RemoteModuleOf = C extends DbConnectionImpl ? RM : never; +export type RemoteModuleOf = + C extends DbConnectionImpl ? RM : never; export type { DbContext, @@ -69,13 +75,16 @@ export type { export type ConnectionEvent = 'connect' | 'disconnect' | 'connectError'; export type CallReducerFlags = 'FullUpdate' | 'NoSuccessNotify'; -type ReducerEventCallback = ( +type ReducerEventCallback< + RemoteModule extends UntypedRemoteModule, + ReducerArgs extends any[] = any[], +> = ( ctx: ReducerEventContextInterface, ...args: ReducerArgs ) => void; type SubscriptionEventCallback = ( - ctx: SubscriptionEventContextInterface, + ctx: SubscriptionEventContextInterface ) => void; function callReducerFlagsToNumber(flags: CallReducerFlags): number { @@ -100,9 +109,9 @@ export type DbConnectionConfig = { remoteModule: RemoteModule; }; -export class DbConnectionImpl< - RemoteModule extends UntypedRemoteModule, -> implements DbContext { +export class DbConnectionImpl + implements DbContext +{ /** * Whether or not the connection is active. */ @@ -240,7 +249,7 @@ export class DbConnectionImpl< for (const tbl of def.tables) { // ClientDbView uses this name verbatim - const key = tbl.accessorName; + const key = tbl.accessorName; Object.defineProperty(view, key, { enumerable: true, configurable: false, @@ -289,7 +298,11 @@ export class DbConnectionImpl< } #makeEventContext( - event: Event>> + event: Event< + ReducerEventInfo< + InferTypeOfRow + > + > ): EventContextInterface { // Bind methods to preserve `this` (#private fields safe) return { @@ -316,7 +329,10 @@ export class DbConnectionImpl< registerSubscription( handle: SubscriptionHandleImpl, - handleEmitter: EventEmitter>, + handleEmitter: EventEmitter< + SubscribeEvent, + SubscriptionEventCallback + >, querySql: string[] ): number { const queryId = this.#getNextQueryId(); @@ -364,14 +380,17 @@ export class DbConnectionImpl< const table = this.#remoteModule.tables.find(t => t.name === tableName); const rowType = table!.rowType; const columnsArray = Object.entries(table!.columns); - const primaryKeyColumnEntry = columnsArray.find(col => col[1].columnMetadata.isPrimaryKey); + const primaryKeyColumnEntry = columnsArray.find( + col => col[1].columnMetadata.isPrimaryKey + ); let previousOffset = 0; while (reader.remaining > 0) { const row = ProductType.deserializeValue(reader, rowType); let rowId: ComparablePrimitive | undefined = undefined; if (primaryKeyColumnEntry !== undefined) { const primaryKeyColName = primaryKeyColumnEntry[0]; - const primaryKeyColType = primaryKeyColumnEntry[1].typeBuilder.algebraicType; + const primaryKeyColType = + primaryKeyColumnEntry[1].typeBuilder.algebraicType; rowId = AlgebraicType.intoMapKey( primaryKeyColType, row[primaryKeyColName] @@ -494,9 +513,9 @@ export class DbConnectionImpl< let reducerInfo: | { - reducerName: string; - args: Uint8Array; - } + reducerName: string; + args: Uint8Array; + } | undefined; if (reducerName !== '') { reducerInfo = { @@ -573,7 +592,11 @@ export class DbConnectionImpl< this.wsPromise.then(wsResolved => { if (wsResolved) { const writer = new BinaryWriter(1024); - AlgebraicType.serializeValue(writer, ClientMessage.algebraicType, message); + AlgebraicType.serializeValue( + writer, + ClientMessage.algebraicType, + message + ); const encoded = writer.getBuffer(); wsResolved.send(encoded); } @@ -596,7 +619,9 @@ export class DbConnectionImpl< // Get table information for the table being updated const tableName = tableUpdate.tableName; // TODO: performance - const tableDef = this.#remoteModule.tables.find(t => t.name === tableName)!; + const tableDef = this.#remoteModule.tables.find( + t => t.name === tableName + )!; const table = this.clientCache.getOrCreateTable(tableDef); const newCallbacks = table.applyOperations( tableUpdate.operations, @@ -610,7 +635,10 @@ export class DbConnectionImpl< } async #processMessage(data: Uint8Array): Promise { - const serverMessage = AlgebraicType.deserializeValue(new BinaryReader(data), ServerMessage.algebraicType); + const serverMessage = AlgebraicType.deserializeValue( + new BinaryReader(data), + ServerMessage.algebraicType + ); const message = await this.#processParsedMessage(serverMessage); if (!message) { return; @@ -651,7 +679,9 @@ export class DbConnectionImpl< let reducerInfo = message.reducerInfo; let unknownTransaction = false; let reducerArgs: InferTypeOfRow | undefined; - const reducer = this.#remoteModule.reducers.find(t => t.name === reducerInfo!.reducerName)!; + const reducer = this.#remoteModule.reducers.find( + t => t.name === reducerInfo!.reducerName + )!; if (!reducerInfo) { unknownTransaction = true; } else { @@ -706,7 +736,7 @@ export class DbConnectionImpl< tag: 'Reducer', value: reducerEvent, }; - const eventContext = this.#makeEventContext(event); + const eventContext = this.#makeEventContext(event); const reducerEventContext = { ...eventContext, event: reducerEvent, @@ -718,9 +748,7 @@ export class DbConnectionImpl< ); const argsArray: any[] = []; - ( - reducer.paramsType - ).elements.forEach(element => { + reducer.paramsType.elements.forEach(element => { argsArray.push(reducerArgs[element.name!]); }); this.#reducerEmitter.emit( @@ -861,17 +889,18 @@ export class DbConnectionImpl< * Call a reducer on your SpacetimeDB module with typed arguments. * @param reducerSchema The schema of the reducer to call * @param callReducerFlags The flags for the reducer call - * @param params The arguments to pass to the reducer + * @param params The arguments to pass to the reducer */ - callReducerWithParams(reducerName: string, paramsType: ProductType, params: object, flags: CallReducerFlags) { + callReducerWithParams( + reducerName: string, + paramsType: ProductType, + params: object, + flags: CallReducerFlags + ) { let writer = new BinaryWriter(1024); ProductType.serializeValue(writer, paramsType, params); let argsBuffer = writer.getBuffer(); - this.callReducer( - reducerName, - argsBuffer, - flags - ); + this.callReducer(reducerName, argsBuffer, flags); } /** @@ -944,13 +973,19 @@ export class DbConnectionImpl< // Note: This is required to be public because it needs to be // called from the `RemoteReducers` class. - onReducer(reducerName: string, callback: ReducerEventCallback): void { + onReducer( + reducerName: string, + callback: ReducerEventCallback + ): void { this.#reducerEmitter.on(reducerName, callback); } // Note: This is required to be public because it needs to be // called from the `RemoteReducers` class. - offReducer(reducerName: string, callback: ReducerEventCallback): void { + offReducer( + reducerName: string, + callback: ReducerEventCallback + ): void { this.#reducerEmitter.off(reducerName, callback); } } diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index 65c127d67d5..a991794594e 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,5 +1,9 @@ import type { ClientDbView } from './db_view'; -import type { ReducersView, SetReducerFlags, UntypedReducersDef } from './reducers'; +import type { + ReducersView, + SetReducerFlags, + UntypedReducersDef, +} from './reducers'; import type { UntypedRemoteModule } from './spacetime_module'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; @@ -21,9 +25,7 @@ export interface DbContext { * * @returns The subscription builder. */ - subscriptionBuilder(): SubscriptionBuilderImpl< - RemoteModule - >; + subscriptionBuilder(): SubscriptionBuilderImpl; /** * Disconnects from the database. diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts index ffdee9e7199..dcfd6b13517 100644 --- a/crates/bindings-typescript/src/sdk/db_view.ts +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -1,9 +1,12 @@ -import type { UntypedRemoteModule } from "./spacetime_module"; -import type { ClientTable } from "./client_table"; +import type { UntypedRemoteModule } from './spacetime_module'; +import type { ClientTable } from './client_table'; /** * A type representing a client-side database view, mapping table names to their corresponding client Table handles. */ export type ClientDbView = { - readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable; -}; \ No newline at end of file + readonly [Tbl in RemoteModule['tables'][number] as Tbl['accessorName']]: ClientTable< + RemoteModule, + Tbl['name'] + >; +}; diff --git a/crates/bindings-typescript/src/sdk/event_context.ts b/crates/bindings-typescript/src/sdk/event_context.ts index 8511832cac3..677adcac90d 100644 --- a/crates/bindings-typescript/src/sdk/event_context.ts +++ b/crates/bindings-typescript/src/sdk/event_context.ts @@ -7,18 +7,21 @@ import type { UntypedRemoteModule } from './spacetime_module.ts'; export type UntypedEventContext = EventContextInterface; -export interface EventContextInterface< - RemoteModule extends UntypedRemoteModule, -> extends DbContext { +export interface EventContextInterface + extends DbContext { /** Enum with variants for all possible events. */ - event: Event>>; + event: Event< + ReducerEventInfo> + >; } export interface ReducerEventContextInterface< RemoteModule extends UntypedRemoteModule, > extends DbContext { /** Enum with variants for all possible events. */ - event: ReducerEvent>>; + event: ReducerEvent< + ReducerEventInfo> + >; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type @@ -28,9 +31,8 @@ export interface SubscriptionEventContextInterface< /** No event is provided **/ } -export interface ErrorContextInterface< - RemoteModule extends UntypedRemoteModule, -> extends DbContext { +export interface ErrorContextInterface + extends DbContext { /** Enum with variants for all possible events. */ event?: Error; } diff --git a/crates/bindings-typescript/src/sdk/index.ts b/crates/bindings-typescript/src/sdk/index.ts index 06cf0cdc3b9..30f7b0e90cd 100644 --- a/crates/bindings-typescript/src/sdk/index.ts +++ b/crates/bindings-typescript/src/sdk/index.ts @@ -8,4 +8,4 @@ export { type SetReducerFlags } from './reducers.ts'; export * from '../lib/type_builders.ts'; export { schema, convertToAccessorMap } from '../lib/schema.ts'; export { table } from '../lib/table.ts'; -export { reducerSchema, reducers } from '../lib/reducers.ts'; \ No newline at end of file +export { reducerSchema, reducers } from '../lib/reducers.ts'; diff --git a/crates/bindings-typescript/src/sdk/message_types.ts b/crates/bindings-typescript/src/sdk/message_types.ts index 3ae8314b2c2..24e834a1ebe 100644 --- a/crates/bindings-typescript/src/sdk/message_types.ts +++ b/crates/bindings-typescript/src/sdk/message_types.ts @@ -25,11 +25,10 @@ export type TransactionUpdateMessage = { energyConsumed: bigint; }; -export type TransactionUpdateLightMessage = - { - tag: 'TransactionUpdateLight'; - tableUpdates: TableUpdate[]; - }; +export type TransactionUpdateLightMessage = { + tag: 'TransactionUpdateLight'; + tableUpdates: TableUpdate[]; +}; export type IdentityTokenMessage = { tag: 'IdentityToken'; @@ -57,10 +56,10 @@ export type SubscriptionError = { }; export type Message = - | InitialSubscriptionMessage - | TransactionUpdateMessage - | TransactionUpdateLightMessage - | IdentityTokenMessage - | SubscribeAppliedMessage - | UnsubscribeAppliedMessage - | SubscriptionError; + | InitialSubscriptionMessage + | TransactionUpdateMessage + | TransactionUpdateLightMessage + | IdentityTokenMessage + | SubscribeAppliedMessage + | UnsubscribeAppliedMessage + | SubscriptionError; diff --git a/crates/bindings-typescript/src/sdk/reducer_handle.ts b/crates/bindings-typescript/src/sdk/reducer_handle.ts index a4e76eda4a4..17a80e72000 100644 --- a/crates/bindings-typescript/src/sdk/reducer_handle.ts +++ b/crates/bindings-typescript/src/sdk/reducer_handle.ts @@ -3,10 +3,10 @@ export type ReducerHandle = { readonly reducerName?: ReducerName; }; -export type ReducerNamesFromReducers = R extends object +export type ReducerNamesFromReducers = R extends object ? { [K in keyof R]: R[K] extends ReducerHandle - ? ReducerName + ? ReducerName : never; }[keyof R] : never; diff --git a/crates/bindings-typescript/src/sdk/reducers.ts b/crates/bindings-typescript/src/sdk/reducers.ts index f341e73dd36..e8bf048e868 100644 --- a/crates/bindings-typescript/src/sdk/reducers.ts +++ b/crates/bindings-typescript/src/sdk/reducers.ts @@ -1,13 +1,14 @@ -import type { ProductType } from "../lib/algebraic_type"; -import type { ParamsObj } from "../lib/reducers"; -import type { CoerceRow } from "../lib/table"; -import type { InferTypeOfRow } from "../lib/type_builders"; -import type { CamelCase } from "../lib/type_util"; -import type { CallReducerFlags } from "./db_connection_impl"; +import type { ProductType } from '../lib/algebraic_type'; +import type { ParamsObj } from '../lib/reducers'; +import type { CoerceRow } from '../lib/table'; +import type { InferTypeOfRow } from '../lib/type_builders'; +import type { CamelCase } from '../lib/type_util'; +import type { CallReducerFlags } from './db_connection_impl'; export type ReducersView = { - [I in keyof R['reducers'] as CamelCase]: - (params: InferTypeOfRow) => void + [I in keyof R['reducers'] as CamelCase< + R['reducers'][number]['accessorName'] + >]: (params: InferTypeOfRow) => void; }; export type ReducerEventInfo = { @@ -18,7 +19,7 @@ export type ReducerEventInfo = { export type UntypedReducerDef = { name: string; accessorName: string; - params: CoerceRow; + params: CoerceRow; paramsType: ProductType; }; @@ -27,5 +28,7 @@ export type UntypedReducersDef = { }; export type SetReducerFlags = { - [K in keyof R['reducers'] as CamelCase]: (flags: CallReducerFlags) => void; -}; \ No newline at end of file + [K in keyof R['reducers'] as CamelCase]: ( + flags: CallReducerFlags + ) => void; +}; diff --git a/crates/bindings-typescript/src/sdk/set_reducer_flags.ts b/crates/bindings-typescript/src/sdk/set_reducer_flags.ts index a5648b67d36..35fdf3a1c39 100644 --- a/crates/bindings-typescript/src/sdk/set_reducer_flags.ts +++ b/crates/bindings-typescript/src/sdk/set_reducer_flags.ts @@ -1,3 +1,6 @@ -import type { CallReducerFlags } from "./db_connection_impl"; +import type { CallReducerFlags } from './db_connection_impl'; -export type UntypedSetReducerFlags = Record void>; \ No newline at end of file +export type UntypedSetReducerFlags = Record< + string, + (flags: CallReducerFlags) => void +>; diff --git a/crates/bindings-typescript/src/sdk/spacetime_module.ts b/crates/bindings-typescript/src/sdk/spacetime_module.ts index 73f7d5fdca9..bf37b798d2f 100644 --- a/crates/bindings-typescript/src/sdk/spacetime_module.ts +++ b/crates/bindings-typescript/src/sdk/spacetime_module.ts @@ -4,15 +4,21 @@ import type { UntypedReducersDef } from './reducers'; export type RemoteModule< SchemaDef extends UntypedSchemaDef, ReducersDef extends UntypedReducersDef, - CLI extends string = string -> = SchemaDef & ReducersDef & { - versionInfo: { - cliVersion: CLI; + CLI extends string = string, +> = SchemaDef & + ReducersDef & { + versionInfo: { + cliVersion: CLI; + }; }; -} -export type UntypedRemoteModule = RemoteModule; +export type UntypedRemoteModule = RemoteModule< + UntypedSchemaDef, + UntypedReducersDef +>; -export type SchemaDef = RemoteModule['tables']; +export type SchemaDef = + RemoteModule['tables']; -export type ReducersDef = RemoteModule['reducers']; \ No newline at end of file +export type ReducersDef = + RemoteModule['reducers']; diff --git a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts index 842e055b791..851b6baffe0 100644 --- a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts +++ b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts @@ -6,18 +6,11 @@ import type { import { EventEmitter } from './event_emitter'; import type { UntypedRemoteModule } from './spacetime_module'; -export class SubscriptionBuilderImpl< - RemoteModule extends UntypedRemoteModule, -> { - #onApplied?: ( - ctx: SubscriptionEventContextInterface - ) => void = undefined; - #onError?: ( - ctx: ErrorContextInterface - ) => void = undefined; - constructor( - private db: DbConnectionImpl - ) {} +export class SubscriptionBuilderImpl { + #onApplied?: (ctx: SubscriptionEventContextInterface) => void = + undefined; + #onError?: (ctx: ErrorContextInterface) => void = undefined; + constructor(private db: DbConnectionImpl) {} /** * Registers `callback` to run when this query is successfully added to our subscribed set, @@ -35,9 +28,7 @@ export class SubscriptionBuilderImpl< * @returns The current `SubscriptionBuilder` instance. */ onApplied( - cb: ( - ctx: SubscriptionEventContextInterface - ) => void + cb: (ctx: SubscriptionEventContextInterface) => void ): SubscriptionBuilderImpl { this.#onApplied = cb; return this; @@ -128,13 +119,14 @@ export type SubscribeEvent = 'applied' | 'error' | 'end'; export class SubscriptionManager { subscriptions: Map< number, - { handle: SubscriptionHandleImpl; emitter: EventEmitter } + { + handle: SubscriptionHandleImpl; + emitter: EventEmitter; + } > = new Map(); } -export class SubscriptionHandleImpl< - RemoteModule extends UntypedRemoteModule, -> { +export class SubscriptionHandleImpl { #queryId: number; #unsubscribeCalled: boolean = false; #endedState: boolean = false; @@ -145,19 +137,12 @@ export class SubscriptionHandleImpl< constructor( private db: DbConnectionImpl, querySql: string[], - onApplied?: ( - ctx: SubscriptionEventContextInterface - ) => void, - onError?: ( - ctx: ErrorContextInterface, - error: Error - ) => void + onApplied?: (ctx: SubscriptionEventContextInterface) => void, + onError?: (ctx: ErrorContextInterface, error: Error) => void ) { this.#emitter.on( 'applied', - ( - ctx: SubscriptionEventContextInterface - ) => { + (ctx: SubscriptionEventContextInterface) => { this.#activeState = true; if (onApplied) { onApplied(ctx); @@ -166,10 +151,7 @@ export class SubscriptionHandleImpl< ); this.#emitter.on( 'error', - ( - ctx: ErrorContextInterface, - error: Error - ) => { + (ctx: ErrorContextInterface, error: Error) => { this.#activeState = false; this.#endedState = true; if (onError) { @@ -193,9 +175,7 @@ export class SubscriptionHandleImpl< this.db.unregisterSubscription(this.#queryId); this.#emitter.on( 'end', - ( - _ctx: SubscriptionEventContextInterface - ) => { + (_ctx: SubscriptionEventContextInterface) => { this.#endedState = true; this.#activeState = false; } @@ -213,9 +193,7 @@ export class SubscriptionHandleImpl< * @param onEnd - Callback to run upon successful unsubscribe. */ unsubscribeThen( - onEnd: ( - ctx: SubscriptionEventContextInterface - ) => void + onEnd: (ctx: SubscriptionEventContextInterface) => void ): void { if (this.#endedState) { throw new Error('Subscription has already ended'); @@ -227,9 +205,7 @@ export class SubscriptionHandleImpl< this.db.unregisterSubscription(this.#queryId); this.#emitter.on( 'end', - ( - ctx: SubscriptionEventContextInterface - ) => { + (ctx: SubscriptionEventContextInterface) => { this.#endedState = true; this.#activeState = false; onEnd(ctx); diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index 655a17278f9..66e8330dc0b 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -2,9 +2,16 @@ import { EventEmitter } from './event_emitter.ts'; import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; -import type { EventContextInterface, ClientTable, TableDefForTableName } from './index.ts'; +import type { + EventContextInterface, + ClientTable, + TableDefForTableName, +} from './index.ts'; import type { RowType, Table, UntypedTableDef } from '../lib/table.ts'; -import type { ClientTableCore, ClientTableCoreImplementable } from './client_table.ts'; +import type { + ClientTableCore, + ClientTableCoreImplementable, +} from './client_table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; import type { TableNamesOf } from '../lib/schema.ts'; @@ -18,9 +25,7 @@ export type Operation< row: RowType; }; -export type TableUpdate< - TableDef extends UntypedTableDef, -> = { +export type TableUpdate = { tableName: string; operations: Operation>[]; }; @@ -37,8 +42,12 @@ export type PendingCallback = { export class TableCache< RemoteModule extends UntypedRemoteModule, TableName extends TableNamesOf, -> implements ClientTableCoreImplementable { - private rows: Map>, number]>; +> implements ClientTableCoreImplementable +{ + private rows: Map< + ComparablePrimitive, + [RowType>, number] + >; private tableDef: TableDefForTableName; private emitter: EventEmitter<'insert' | 'delete' | 'update'>; @@ -64,8 +73,17 @@ export class TableCache< /** * @returns The values of the rows in the table */ - iter(): IterableIterator>> { - function* generator(rows: Map>, number]>): IterableIterator>> { + iter(): IterableIterator< + RowType> + > { + function* generator( + rows: Map< + ComparablePrimitive, + [RowType>, number] + > + ): IterableIterator< + RowType> + > { for (const [row] of rows.values()) { yield row; } @@ -74,28 +92,40 @@ export class TableCache< } /** - * Allows iteration over the rows in the table + * Allows iteration over the rows in the table * @returns An iterator over the rows in the table */ - [Symbol.iterator](): IterableIterator>> { + [Symbol.iterator](): IterableIterator< + RowType> + > { return this.iter(); } applyOperations = ( - operations: Operation>>[], + operations: Operation< + RowType> + >[], ctx: EventContextInterface ): PendingCallback[] => { const pendingCallbacks: PendingCallback[] = []; // TODO: performance - const hasPrimaryKey = Object.values(this.tableDef.columns).some(col => col.columnMetadata.isPrimaryKey === true); + const hasPrimaryKey = Object.values(this.tableDef.columns).some( + col => col.columnMetadata.isPrimaryKey === true + ); if (hasPrimaryKey) { const insertMap = new Map< ComparablePrimitive, - [Operation>>, number] + [ + Operation>>, + number, + ] >(); const deleteMap = new Map< ComparablePrimitive, - [Operation>>, number] + [ + Operation>>, + number, + ] >(); for (const op of operations) { if (op.type === 'insert') { @@ -205,7 +235,9 @@ export class TableCache< insert = ( ctx: EventContextInterface, - operation: Operation>>, + operation: Operation< + RowType> + >, count: number = 1 ): PendingCallback | undefined => { const [_, previousCount] = this.rows.get(operation.rowId) || [ @@ -228,7 +260,9 @@ export class TableCache< delete = ( ctx: EventContextInterface, - operation: Operation>>, + operation: Operation< + RowType> + >, count: number = 1 ): PendingCallback | undefined => { const [_, previousCount] = this.rows.get(operation.rowId) || [ @@ -272,7 +306,10 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onInsert = ( - cb: (ctx: EventContextInterface, row: RowType>) => void + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void ): void => { this.emitter.on('insert', cb); }; @@ -293,7 +330,10 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onDelete = ( - cb: (ctx: EventContextInterface, row: RowType>) => void + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void ): void => { this.emitter.on('delete', cb); }; @@ -314,7 +354,11 @@ export class TableCache< * @param cb Callback to be called when a new row is inserted */ onUpdate = ( - cb: (ctx: EventContextInterface, oldRow: RowType>, row: RowType>) => void + cb: ( + ctx: EventContextInterface, + oldRow: RowType>, + row: RowType> + ) => void ): void => { this.emitter.on('update', cb); }; @@ -325,7 +369,10 @@ export class TableCache< * @param cb Callback to be removed */ removeOnInsert = ( - cb: (ctx: EventContextInterface, row: RowType>) => void + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void ): void => { this.emitter.off('insert', cb); }; @@ -336,7 +383,10 @@ export class TableCache< * @param cb Callback to be removed */ removeOnDelete = ( - cb: (ctx: EventContextInterface, row: RowType>) => void + cb: ( + ctx: EventContextInterface, + row: RowType> + ) => void ): void => { this.emitter.off('delete', cb); }; @@ -347,7 +397,11 @@ export class TableCache< * @param cb Callback to be removed */ removeOnUpdate = ( - cb: (ctx: EventContextInterface, oldRow: RowType>, row: RowType>) => void + cb: ( + ctx: EventContextInterface, + oldRow: RowType>, + row: RowType> + ) => void ): void => { this.emitter.off('update', cb); }; diff --git a/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts b/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts index 2b1dc2ac25f..58f1861603a 100644 --- a/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts +++ b/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts @@ -30,11 +30,7 @@ class WebsocketTestAdapter { sendToClient(message: Infer): void { const writer = new BinaryWriter(1024); - AlgebraicType.serializeValue( - writer, - ServerMessage.algebraicType, - message - ); + AlgebraicType.serializeValue(writer, ServerMessage.algebraicType, message); const rawBytes = writer.getBuffer(); // The brotli library's `compress` is somehow broken: it returns `null` for some inputs. // See https://github.com/foliojs/brotli.js/issues/36, which is closed but not actually fixed. diff --git a/crates/bindings-typescript/src/server/db_view.ts b/crates/bindings-typescript/src/server/db_view.ts index 43a4a284ed3..25e3978c288 100644 --- a/crates/bindings-typescript/src/server/db_view.ts +++ b/crates/bindings-typescript/src/server/db_view.ts @@ -1,5 +1,5 @@ -import type { UntypedSchemaDef } from "../lib/schema"; -import type { ReadonlyTable, Table } from "../lib/table"; +import type { UntypedSchemaDef } from '../lib/schema'; +import type { ReadonlyTable, Table } from '../lib/table'; /** * A type representing a read-only database view, mapping table names to their corresponding read-only Table handles. @@ -13,4 +13,4 @@ export type ReadonlyDbView = { */ export type DbView = { readonly [Tbl in SchemaDef['tables'][number] as Tbl['accessorName']]: Table; -}; \ No newline at end of file +}; diff --git a/crates/bindings-typescript/src/server/runtime.ts b/crates/bindings-typescript/src/server/runtime.ts index ea442b38308..fd2a9e6a02b 100644 --- a/crates/bindings-typescript/src/server/runtime.ts +++ b/crates/bindings-typescript/src/server/runtime.ts @@ -180,8 +180,8 @@ export const hooks: ModuleHooks = { AlgebraicType.serializeValue( writer, RawModuleDef.algebraicType, - RawModuleDef.create('V9', MODULE_DEF), - ) + RawModuleDef.create('V9', MODULE_DEF) + ); return writer.getBuffer(); }, __call_reducer__(reducerId, sender, connId, timestamp, argsBuf) { @@ -235,7 +235,10 @@ function makeDbView(moduleDef: Infer): DbView { ); } -function makeTableView(typespace: Infer, table: Infer): Table { +function makeTableView( + typespace: Infer, + table: Infer +): Table { const table_id = sys.table_id_from_name(table.name); const rowType = typespace.types[table.productTypeRef]; if (rowType.tag !== 'Product') { @@ -472,7 +475,10 @@ function makeTableView(typespace: Infer, table: Infer, ty: AlgebraicType): number { +function bsatnBaseSize( + typespace: Infer, + ty: AlgebraicType +): number { const assumedArrayLength = 4; while (ty.tag === 'Ref') ty = typespace.types[ty.value]; if (ty.tag === 'Product') { diff --git a/crates/bindings-typescript/test-app/src/App.tsx b/crates/bindings-typescript/test-app/src/App.tsx index 3ef576fb0e1..3bbee2cbf27 100644 --- a/crates/bindings-typescript/test-app/src/App.tsx +++ b/crates/bindings-typescript/test-app/src/App.tsx @@ -1,7 +1,13 @@ import { tables, reducers } from './module_bindings'; import { useEffect } from 'react'; import './App.css'; -import { eq, useReducer, useSpacetimeDB, useTable, where } from '../../src/react'; +import { + eq, + useReducer, + useSpacetimeDB, + useTable, + where, +} from '../../src/react'; function getRandomInt(max: number) { return Math.floor(Math.random() * max); @@ -10,10 +16,9 @@ function getRandomInt(max: number) { function App() { const connection = useSpacetimeDB(); const players = useTable(tables.player, where(eq('name', 'Hello')), { - onInsert: (row) => { - console.log('Player inserted:', rows); + onInsert: row => { + console.log('Player inserted:', row); }, - }); const x = players[0]; const createPlayer = useReducer(reducers.createPlayer); diff --git a/crates/bindings-typescript/tests/serde.test.ts b/crates/bindings-typescript/tests/serde.test.ts index 21cb3d639cb..c740dd2ec8e 100644 --- a/crates/bindings-typescript/tests/serde.test.ts +++ b/crates/bindings-typescript/tests/serde.test.ts @@ -1,5 +1,12 @@ import { describe, expect, test } from 'vitest'; -import { AlgebraicType, BinaryReader, BinaryWriter, ConnectionId, Identity, ScheduleAt } from 'spacetimedb'; +import { + AlgebraicType, + BinaryReader, + BinaryWriter, + ConnectionId, + Identity, + ScheduleAt, +} from 'spacetimedb'; describe('it correctly serializes and deserializes algebraic values', () => { test('when it serializes and deserializes with a product type', () => { From cb16789be729954a7c47b87fb40b35baa0bd1029 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 9 Nov 2025 15:51:00 -0500 Subject: [PATCH 19/23] Removed generation of variant types since they can now be derived from the type builders --- .../src/lib/autogen/algebraic_type_type.ts | 2 - .../lib/autogen/algebraic_type_variants.ts | 38 -- .../src/lib/autogen/index_type_type.ts | 1 - .../src/lib/autogen/index_type_variants.ts | 14 - .../src/lib/autogen/lifecycle_type.ts | 1 - .../src/lib/autogen/lifecycle_variants.ts | 15 - .../lib/autogen/misc_module_export_type.ts | 2 - .../autogen/misc_module_export_variants.ts | 17 - .../autogen/raw_constraint_data_v_9_type.ts | 2 - .../raw_constraint_data_v_9_variants.ts | 17 - .../lib/autogen/raw_index_algorithm_type.ts | 1 - .../autogen/raw_index_algorithm_variants.ts | 15 - .../raw_misc_module_export_v_9_type.ts | 2 - .../raw_misc_module_export_v_9_variants.ts | 24 -- .../src/lib/autogen/raw_module_def_type.ts | 2 - .../lib/autogen/raw_module_def_variants.ts | 19 - .../src/lib/autogen/table_access_type.ts | 1 - .../src/lib/autogen/table_access_variants.ts | 14 - .../src/lib/autogen/table_type_type.ts | 1 - .../src/lib/autogen/table_type_variants.ts | 14 - .../bindings-typescript/src/lib/reducers.ts | 1 - crates/bindings-typescript/src/lib/table.ts | 3 +- .../src/lib/type_builders.ts | 1 + .../src/react/useReducer.ts | 21 +- .../src/react/useSpacetimeDB.ts | 2 - .../src/sdk/client_api/client_message_type.ts | 2 - .../sdk/client_api/client_message_variants.ts | 52 --- .../compressable_query_update_type.ts | 2 - .../compressable_query_update_variants.ts | 19 - .../sdk/client_api/procedure_status_type.ts | 1 - .../client_api/procedure_status_variants.ts | 15 - .../src/sdk/client_api/row_size_hint_type.ts | 1 - .../sdk/client_api/row_size_hint_variants.ts | 14 - .../src/sdk/client_api/server_message_type.ts | 2 - .../sdk/client_api/server_message_variants.ts | 67 ---- .../src/sdk/client_api/update_status_type.ts | 2 - .../sdk/client_api/update_status_variants.ts | 19 - .../src/sdk/client_cache.ts | 2 +- .../src/sdk/db_connection_builder.ts | 1 - .../src/sdk/db_connection_impl.ts | 6 +- .../bindings-typescript/src/sdk/db_context.ts | 1 - .../src/sdk/message_types.ts | 2 +- .../src/sdk/table_cache.ts | 4 +- .../bindings-typescript/test-app/src/App.tsx | 1 - .../test-app/src/module_bindings/index.ts | 16 +- .../test-react-router-app/package.json | 1 + .../test-react-router-app/server/src/lib.rs | 1 + .../module_bindings/clear_counter_reducer.ts | 64 +--- .../client_connected_reducer.ts | 65 +--- .../client_disconnected_reducer.ts | 65 +--- .../src/module_bindings/counter_table.ts | 118 +------ .../src/module_bindings/counter_type.ts | 71 +--- .../increment_counter_reducer.ts | 65 +--- .../src/module_bindings/index.ts | 333 +++++------------- .../src/module_bindings/offline_user_table.ts | 116 +----- .../src/module_bindings/user_table.ts | 116 +----- .../src/module_bindings/user_type.ts | 74 +--- .../src/pages/CounterPage.tsx | 13 +- .../src/pages/UserPage.tsx | 26 +- crates/codegen/src/typescript.rs | 172 ++------- pnpm-lock.yaml | 179 ++++++++-- 61 files changed, 380 insertions(+), 1558 deletions(-) delete mode 100644 crates/bindings-typescript/src/lib/autogen/algebraic_type_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/index_type_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/lifecycle_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/misc_module_export_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/raw_module_def_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/table_access_variants.ts delete mode 100644 crates/bindings-typescript/src/lib/autogen/table_type_variants.ts delete mode 100644 crates/bindings-typescript/src/sdk/client_api/client_message_variants.ts delete mode 100644 crates/bindings-typescript/src/sdk/client_api/compressable_query_update_variants.ts delete mode 100644 crates/bindings-typescript/src/sdk/client_api/procedure_status_variants.ts delete mode 100644 crates/bindings-typescript/src/sdk/client_api/row_size_hint_variants.ts delete mode 100644 crates/bindings-typescript/src/sdk/client_api/server_message_variants.ts delete mode 100644 crates/bindings-typescript/src/sdk/client_api/update_status_variants.ts diff --git a/crates/bindings-typescript/src/lib/autogen/algebraic_type_type.ts b/crates/bindings-typescript/src/lib/autogen/algebraic_type_type.ts index fd16d887075..8ec644f676b 100644 --- a/crates/bindings-typescript/src/lib/autogen/algebraic_type_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/algebraic_type_type.ts @@ -12,8 +12,6 @@ import { import SumType from './sum_type_type'; import ProductType from './product_type_type'; -import * as AlgebraicTypeVariants from './algebraic_type_variants'; - // The tagged union or sum type for the algebraic type `AlgebraicType`. const AlgebraicType: __TypeBuilder<__AlgebraicTypeType, __AlgebraicTypeType> = __t.enum('AlgebraicType', { diff --git a/crates/bindings-typescript/src/lib/autogen/algebraic_type_variants.ts b/crates/bindings-typescript/src/lib/autogen/algebraic_type_variants.ts deleted file mode 100644 index b81b181d802..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/algebraic_type_variants.ts +++ /dev/null @@ -1,38 +0,0 @@ -// 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 '../../index'; -import AlgebraicTypeType from './algebraic_type_type'; -import SumTypeType from './sum_type_type'; -import ProductTypeType from './product_type_type'; - -export type Ref = { tag: 'Ref'; value: number }; -export type Sum = { tag: 'Sum'; value: __Infer }; -export type Product = { - tag: 'Product'; - value: __Infer; -}; -export type Array = { tag: 'Array'; value: __Infer }; -export type String = { tag: 'String' }; -export type Bool = { tag: 'Bool' }; -export type I8 = { tag: 'I8' }; -export type U8 = { tag: 'U8' }; -export type I16 = { tag: 'I16' }; -export type U16 = { tag: 'U16' }; -export type I32 = { tag: 'I32' }; -export type U32 = { tag: 'U32' }; -export type I64 = { tag: 'I64' }; -export type U64 = { tag: 'U64' }; -export type I128 = { tag: 'I128' }; -export type U128 = { tag: 'U128' }; -export type I256 = { tag: 'I256' }; -export type U256 = { tag: 'U256' }; -export type F32 = { tag: 'F32' }; -export type F64 = { tag: 'F64' }; diff --git a/crates/bindings-typescript/src/lib/autogen/index_type_type.ts b/crates/bindings-typescript/src/lib/autogen/index_type_type.ts index 7253194254a..4c2c4d6f3fc 100644 --- a/crates/bindings-typescript/src/lib/autogen/index_type_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/index_type_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../index'; -import * as IndexTypeVariants from './index_type_variants'; // The tagged union or sum type for the algebraic type `IndexType`. const IndexType = __t.enum('IndexType', { diff --git a/crates/bindings-typescript/src/lib/autogen/index_type_variants.ts b/crates/bindings-typescript/src/lib/autogen/index_type_variants.ts deleted file mode 100644 index 37e5ee90972..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/index_type_variants.ts +++ /dev/null @@ -1,14 +0,0 @@ -// 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 '../../index'; - -export type BTree = { tag: 'BTree' }; -export type Hash = { tag: 'Hash' }; diff --git a/crates/bindings-typescript/src/lib/autogen/lifecycle_type.ts b/crates/bindings-typescript/src/lib/autogen/lifecycle_type.ts index 3f80e90dfb4..8de101313f0 100644 --- a/crates/bindings-typescript/src/lib/autogen/lifecycle_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/lifecycle_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../index'; -import * as LifecycleVariants from './lifecycle_variants'; // The tagged union or sum type for the algebraic type `Lifecycle`. const Lifecycle = __t.enum('Lifecycle', { diff --git a/crates/bindings-typescript/src/lib/autogen/lifecycle_variants.ts b/crates/bindings-typescript/src/lib/autogen/lifecycle_variants.ts deleted file mode 100644 index 0570638b19f..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/lifecycle_variants.ts +++ /dev/null @@ -1,15 +0,0 @@ -// 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 '../../index'; - -export type Init = { tag: 'Init' }; -export type OnConnect = { tag: 'OnConnect' }; -export type OnDisconnect = { tag: 'OnDisconnect' }; diff --git a/crates/bindings-typescript/src/lib/autogen/misc_module_export_type.ts b/crates/bindings-typescript/src/lib/autogen/misc_module_export_type.ts index b4d4be33f34..753169f2831 100644 --- a/crates/bindings-typescript/src/lib/autogen/misc_module_export_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/misc_module_export_type.ts @@ -11,8 +11,6 @@ import { } from '../../index'; import TypeAlias from './type_alias_type'; -import * as MiscModuleExportVariants from './misc_module_export_variants'; - // The tagged union or sum type for the algebraic type `MiscModuleExport`. const MiscModuleExport = __t.enum('MiscModuleExport', { get TypeAlias() { diff --git a/crates/bindings-typescript/src/lib/autogen/misc_module_export_variants.ts b/crates/bindings-typescript/src/lib/autogen/misc_module_export_variants.ts deleted file mode 100644 index 99d584fbdca..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/misc_module_export_variants.ts +++ /dev/null @@ -1,17 +0,0 @@ -// 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 '../../index'; -import TypeAliasType from './type_alias_type'; - -export type TypeAlias = { - tag: 'TypeAlias'; - value: __Infer; -}; diff --git a/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_type.ts b/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_type.ts index 314c7014bfb..dafe670e956 100644 --- a/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_type.ts @@ -11,8 +11,6 @@ import { } from '../../index'; import RawUniqueConstraintDataV9 from './raw_unique_constraint_data_v_9_type'; -import * as RawConstraintDataV9Variants from './raw_constraint_data_v_9_variants'; - // The tagged union or sum type for the algebraic type `RawConstraintDataV9`. const RawConstraintDataV9 = __t.enum('RawConstraintDataV9', { get Unique() { diff --git a/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_variants.ts b/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_variants.ts deleted file mode 100644 index 5ace6a528b1..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/raw_constraint_data_v_9_variants.ts +++ /dev/null @@ -1,17 +0,0 @@ -// 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 '../../index'; -import RawUniqueConstraintDataV9Type from './raw_unique_constraint_data_v_9_type'; - -export type Unique = { - tag: 'Unique'; - value: __Infer; -}; diff --git a/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_type.ts b/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_type.ts index e755ee94c7e..f70c8992c68 100644 --- a/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../index'; -import * as RawIndexAlgorithmVariants from './raw_index_algorithm_variants'; // The tagged union or sum type for the algebraic type `RawIndexAlgorithm`. const RawIndexAlgorithm = __t.enum('RawIndexAlgorithm', { diff --git a/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_variants.ts b/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_variants.ts deleted file mode 100644 index 709fb79d6d2..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/raw_index_algorithm_variants.ts +++ /dev/null @@ -1,15 +0,0 @@ -// 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 '../../index'; - -export type BTree = { tag: 'BTree'; value: number[] }; -export type Hash = { tag: 'Hash'; value: number[] }; -export type Direct = { tag: 'Direct'; value: number }; diff --git a/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_type.ts b/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_type.ts index 5f592285551..81612d985f9 100644 --- a/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_type.ts @@ -13,8 +13,6 @@ import RawColumnDefaultValueV9 from './raw_column_default_value_v_9_type'; import RawProcedureDefV9 from './raw_procedure_def_v_9_type'; import RawViewDefV9 from './raw_view_def_v_9_type'; -import * as RawMiscModuleExportV9Variants from './raw_misc_module_export_v_9_variants'; - // The tagged union or sum type for the algebraic type `RawMiscModuleExportV9`. const RawMiscModuleExportV9 = __t.enum('RawMiscModuleExportV9', { get ColumnDefaultValue() { diff --git a/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_variants.ts b/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_variants.ts deleted file mode 100644 index ec0e9e4f899..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/raw_misc_module_export_v_9_variants.ts +++ /dev/null @@ -1,24 +0,0 @@ -// 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 '../../index'; -import RawColumnDefaultValueV9Type from './raw_column_default_value_v_9_type'; -import RawProcedureDefV9Type from './raw_procedure_def_v_9_type'; -import RawViewDefV9Type from './raw_view_def_v_9_type'; - -export type ColumnDefaultValue = { - tag: 'ColumnDefaultValue'; - value: __Infer; -}; -export type Procedure = { - tag: 'Procedure'; - value: __Infer; -}; -export type View = { tag: 'View'; value: __Infer }; diff --git a/crates/bindings-typescript/src/lib/autogen/raw_module_def_type.ts b/crates/bindings-typescript/src/lib/autogen/raw_module_def_type.ts index e808e1528a7..ac92d10fa65 100644 --- a/crates/bindings-typescript/src/lib/autogen/raw_module_def_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/raw_module_def_type.ts @@ -12,8 +12,6 @@ import { import RawModuleDefV8 from './raw_module_def_v_8_type'; import RawModuleDefV9 from './raw_module_def_v_9_type'; -import * as RawModuleDefVariants from './raw_module_def_variants'; - // The tagged union or sum type for the algebraic type `RawModuleDef`. const RawModuleDef = __t.enum('RawModuleDef', { get V8BackCompat() { diff --git a/crates/bindings-typescript/src/lib/autogen/raw_module_def_variants.ts b/crates/bindings-typescript/src/lib/autogen/raw_module_def_variants.ts deleted file mode 100644 index 2f1f1e2d2b0..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/raw_module_def_variants.ts +++ /dev/null @@ -1,19 +0,0 @@ -// 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 '../../index'; -import RawModuleDefV8Type from './raw_module_def_v_8_type'; -import RawModuleDefV9Type from './raw_module_def_v_9_type'; - -export type V8BackCompat = { - tag: 'V8BackCompat'; - value: __Infer; -}; -export type V9 = { tag: 'V9'; value: __Infer }; diff --git a/crates/bindings-typescript/src/lib/autogen/table_access_type.ts b/crates/bindings-typescript/src/lib/autogen/table_access_type.ts index 0d310bf2d23..80d87d447c0 100644 --- a/crates/bindings-typescript/src/lib/autogen/table_access_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/table_access_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../index'; -import * as TableAccessVariants from './table_access_variants'; // The tagged union or sum type for the algebraic type `TableAccess`. const TableAccess = __t.enum('TableAccess', { diff --git a/crates/bindings-typescript/src/lib/autogen/table_access_variants.ts b/crates/bindings-typescript/src/lib/autogen/table_access_variants.ts deleted file mode 100644 index 9567b4f4006..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/table_access_variants.ts +++ /dev/null @@ -1,14 +0,0 @@ -// 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 '../../index'; - -export type Public = { tag: 'Public' }; -export type Private = { tag: 'Private' }; diff --git a/crates/bindings-typescript/src/lib/autogen/table_type_type.ts b/crates/bindings-typescript/src/lib/autogen/table_type_type.ts index ee5cfa6b497..b5d1071151c 100644 --- a/crates/bindings-typescript/src/lib/autogen/table_type_type.ts +++ b/crates/bindings-typescript/src/lib/autogen/table_type_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../index'; -import * as TableTypeVariants from './table_type_variants'; // The tagged union or sum type for the algebraic type `TableType`. const TableType = __t.enum('TableType', { diff --git a/crates/bindings-typescript/src/lib/autogen/table_type_variants.ts b/crates/bindings-typescript/src/lib/autogen/table_type_variants.ts deleted file mode 100644 index ea10b2d5aaa..00000000000 --- a/crates/bindings-typescript/src/lib/autogen/table_type_variants.ts +++ /dev/null @@ -1,14 +0,0 @@ -// 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 '../../index'; - -export type System = { tag: 'System' }; -export type User = { tag: 'User' }; diff --git a/crates/bindings-typescript/src/lib/reducers.ts b/crates/bindings-typescript/src/lib/reducers.ts index da7191d8f76..1550173e1ee 100644 --- a/crates/bindings-typescript/src/lib/reducers.ts +++ b/crates/bindings-typescript/src/lib/reducers.ts @@ -11,7 +11,6 @@ import { RowBuilder, type Infer, type InferTypeOfRow, - type ProductBuilder, type RowObj, type TypeBuilder, } from './type_builders'; diff --git a/crates/bindings-typescript/src/lib/table.ts b/crates/bindings-typescript/src/lib/table.ts index 6d27e10a799..6187f5a4b78 100644 --- a/crates/bindings-typescript/src/lib/table.ts +++ b/crates/bindings-typescript/src/lib/table.ts @@ -286,8 +286,7 @@ export function table>( // add explicit constraints from options.constraints for (const constraintOpts of opts.constraints ?? []) { if (constraintOpts.constraint === 'unique') { - let data: Infer['data']; - data = { + const data: Infer['data'] = { tag: 'Unique', value: { columns: constraintOpts.columns.map(c => colIds.get(c)!) }, }; diff --git a/crates/bindings-typescript/src/lib/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts index 567bae3975f..408467fed9d 100644 --- a/crates/bindings-typescript/src/lib/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -1303,6 +1303,7 @@ export class SumBuilder extends TypeBuilder< }) ); this.variants = variants; + this.typeName = name; } /** diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index 6a16f1a2a38..dc93ae01b58 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -4,15 +4,22 @@ import type { UntypedReducerDef } from '../sdk/reducers'; import { useSpacetimeDB } from './useSpacetimeDB'; import type { Prettify } from '../lib/type_util'; +type IsEmptyObject = [keyof T] extends [never] ? true : false; +type MaybeParams = IsEmptyObject extends true ? [] : [params: T]; + +type ParamsType = MaybeParams +>>; + export function useReducer( reducerDef: ReducerDef -): (params: Prettify>) => void { +): (...params: ParamsType) => void { const { getConnection, isActive } = useSpacetimeDB(); const reducerName = reducerDef.accessorName; // Holds calls made before the connection exists const queueRef = useRef< - Array>> + ParamsType[] >([]); // Flush when we finally have a connection @@ -22,23 +29,25 @@ export function useReducer( return; } const fn = (conn.reducers as any)[reducerName] as ( - p: InferTypeOfRow + p: ParamsType ) => void; if (queueRef.current.length) { const pending = queueRef.current.splice(0); - for (const params of pending) fn(params); + for (const params of pending) { + fn(params); + } } }, [getConnection, reducerName, isActive]); return useCallback( - (params: Prettify>) => { + (...params: ParamsType) => { const conn = getConnection(); if (!conn) { queueRef.current.push(params); return; } const fn = (conn.reducers as any)[reducerName] as ( - p: Prettify> + p: ParamsType ) => void; return fn(params); }, diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index 9298eb3fda4..2a7cbfc828a 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -1,7 +1,5 @@ import { createContext, useContext } from 'react'; -import type { DbConnectionImpl } from '../sdk/db_connection_impl'; import type { ConnectionState } from './connection_state'; -import type { UntypedRemoteModule } from '../sdk/spacetime_module'; export const SpacetimeDBContext = createContext( undefined diff --git a/crates/bindings-typescript/src/sdk/client_api/client_message_type.ts b/crates/bindings-typescript/src/sdk/client_api/client_message_type.ts index a9235ed5572..7024d9d2840 100644 --- a/crates/bindings-typescript/src/sdk/client_api/client_message_type.ts +++ b/crates/bindings-typescript/src/sdk/client_api/client_message_type.ts @@ -18,8 +18,6 @@ import Unsubscribe from './unsubscribe_type'; import UnsubscribeMulti from './unsubscribe_multi_type'; import CallProcedure from './call_procedure_type'; -import * as ClientMessageVariants from './client_message_variants'; - // The tagged union or sum type for the algebraic type `ClientMessage`. const ClientMessage = __t.enum('ClientMessage', { get CallReducer() { diff --git a/crates/bindings-typescript/src/sdk/client_api/client_message_variants.ts b/crates/bindings-typescript/src/sdk/client_api/client_message_variants.ts deleted file mode 100644 index b85719a2956..00000000000 --- a/crates/bindings-typescript/src/sdk/client_api/client_message_variants.ts +++ /dev/null @@ -1,52 +0,0 @@ -// 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 '../../lib/type_builders'; -import CallReducerType from './call_reducer_type'; -import SubscribeType from './subscribe_type'; -import OneOffQueryType from './one_off_query_type'; -import SubscribeSingleType from './subscribe_single_type'; -import SubscribeMultiType from './subscribe_multi_type'; -import UnsubscribeType from './unsubscribe_type'; -import UnsubscribeMultiType from './unsubscribe_multi_type'; -import CallProcedureType from './call_procedure_type'; - -export type CallReducer = { - tag: 'CallReducer'; - value: __Infer; -}; -export type Subscribe = { - tag: 'Subscribe'; - value: __Infer; -}; -export type OneOffQuery = { - tag: 'OneOffQuery'; - value: __Infer; -}; -export type SubscribeSingle = { - tag: 'SubscribeSingle'; - value: __Infer; -}; -export type SubscribeMulti = { - tag: 'SubscribeMulti'; - value: __Infer; -}; -export type Unsubscribe = { - tag: 'Unsubscribe'; - value: __Infer; -}; -export type UnsubscribeMulti = { - tag: 'UnsubscribeMulti'; - value: __Infer; -}; -export type CallProcedure = { - tag: 'CallProcedure'; - value: __Infer; -}; diff --git a/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_type.ts b/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_type.ts index 287eb2364cc..fd07c71773b 100644 --- a/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_type.ts +++ b/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_type.ts @@ -11,8 +11,6 @@ import { } from '../../lib/type_builders'; import QueryUpdate from './query_update_type'; -import * as CompressableQueryUpdateVariants from './compressable_query_update_variants'; - // The tagged union or sum type for the algebraic type `CompressableQueryUpdate`. const CompressableQueryUpdate = __t.enum('CompressableQueryUpdate', { get Uncompressed() { diff --git a/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_variants.ts b/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_variants.ts deleted file mode 100644 index 8246811b00e..00000000000 --- a/crates/bindings-typescript/src/sdk/client_api/compressable_query_update_variants.ts +++ /dev/null @@ -1,19 +0,0 @@ -// 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 '../../lib/type_builders'; -import QueryUpdateType from './query_update_type'; - -export type Uncompressed = { - tag: 'Uncompressed'; - value: __Infer; -}; -export type Brotli = { tag: 'Brotli'; value: Uint8Array }; -export type Gzip = { tag: 'Gzip'; value: Uint8Array }; diff --git a/crates/bindings-typescript/src/sdk/client_api/procedure_status_type.ts b/crates/bindings-typescript/src/sdk/client_api/procedure_status_type.ts index f2750e88a29..29f444f66bd 100644 --- a/crates/bindings-typescript/src/sdk/client_api/procedure_status_type.ts +++ b/crates/bindings-typescript/src/sdk/client_api/procedure_status_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../lib/type_builders'; -import * as ProcedureStatusVariants from './procedure_status_variants'; // The tagged union or sum type for the algebraic type `ProcedureStatus`. const ProcedureStatus = __t.enum('ProcedureStatus', { diff --git a/crates/bindings-typescript/src/sdk/client_api/procedure_status_variants.ts b/crates/bindings-typescript/src/sdk/client_api/procedure_status_variants.ts deleted file mode 100644 index 6652aab16d4..00000000000 --- a/crates/bindings-typescript/src/sdk/client_api/procedure_status_variants.ts +++ /dev/null @@ -1,15 +0,0 @@ -// 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 '../../lib/type_builders'; - -export type Returned = { tag: 'Returned'; value: Uint8Array }; -export type OutOfEnergy = { tag: 'OutOfEnergy' }; -export type InternalError = { tag: 'InternalError'; value: string }; diff --git a/crates/bindings-typescript/src/sdk/client_api/row_size_hint_type.ts b/crates/bindings-typescript/src/sdk/client_api/row_size_hint_type.ts index b5fcd506486..112417b2ac3 100644 --- a/crates/bindings-typescript/src/sdk/client_api/row_size_hint_type.ts +++ b/crates/bindings-typescript/src/sdk/client_api/row_size_hint_type.ts @@ -9,7 +9,6 @@ import { type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, } from '../../lib/type_builders'; -import * as RowSizeHintVariants from './row_size_hint_variants'; // The tagged union or sum type for the algebraic type `RowSizeHint`. const RowSizeHint = __t.enum('RowSizeHint', { diff --git a/crates/bindings-typescript/src/sdk/client_api/row_size_hint_variants.ts b/crates/bindings-typescript/src/sdk/client_api/row_size_hint_variants.ts deleted file mode 100644 index 8c28ef824b9..00000000000 --- a/crates/bindings-typescript/src/sdk/client_api/row_size_hint_variants.ts +++ /dev/null @@ -1,14 +0,0 @@ -// 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 '../../lib/type_builders'; - -export type FixedSize = { tag: 'FixedSize'; value: number }; -export type RowOffsets = { tag: 'RowOffsets'; value: bigint[] }; diff --git a/crates/bindings-typescript/src/sdk/client_api/server_message_type.ts b/crates/bindings-typescript/src/sdk/client_api/server_message_type.ts index b670d6b69bc..cd9e0e2ea23 100644 --- a/crates/bindings-typescript/src/sdk/client_api/server_message_type.ts +++ b/crates/bindings-typescript/src/sdk/client_api/server_message_type.ts @@ -21,8 +21,6 @@ import SubscribeMultiApplied from './subscribe_multi_applied_type'; import UnsubscribeMultiApplied from './unsubscribe_multi_applied_type'; import ProcedureResult from './procedure_result_type'; -import * as ServerMessageVariants from './server_message_variants'; - // The tagged union or sum type for the algebraic type `ServerMessage`. const ServerMessage = __t.enum('ServerMessage', { get InitialSubscription() { diff --git a/crates/bindings-typescript/src/sdk/client_api/server_message_variants.ts b/crates/bindings-typescript/src/sdk/client_api/server_message_variants.ts deleted file mode 100644 index e27eb295f81..00000000000 --- a/crates/bindings-typescript/src/sdk/client_api/server_message_variants.ts +++ /dev/null @@ -1,67 +0,0 @@ -// 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 '../../lib/type_builders'; -import InitialSubscriptionType from './initial_subscription_type'; -import TransactionUpdateType from './transaction_update_type'; -import TransactionUpdateLightType from './transaction_update_light_type'; -import IdentityTokenType from './identity_token_type'; -import OneOffQueryResponseType from './one_off_query_response_type'; -import SubscribeAppliedType from './subscribe_applied_type'; -import UnsubscribeAppliedType from './unsubscribe_applied_type'; -import SubscriptionErrorType from './subscription_error_type'; -import SubscribeMultiAppliedType from './subscribe_multi_applied_type'; -import UnsubscribeMultiAppliedType from './unsubscribe_multi_applied_type'; -import ProcedureResultType from './procedure_result_type'; - -export type InitialSubscription = { - tag: 'InitialSubscription'; - value: __Infer; -}; -export type TransactionUpdate = { - tag: 'TransactionUpdate'; - value: __Infer; -}; -export type TransactionUpdateLight = { - tag: 'TransactionUpdateLight'; - value: __Infer; -}; -export type IdentityToken = { - tag: 'IdentityToken'; - value: __Infer; -}; -export type OneOffQueryResponse = { - tag: 'OneOffQueryResponse'; - value: __Infer; -}; -export type SubscribeApplied = { - tag: 'SubscribeApplied'; - value: __Infer; -}; -export type UnsubscribeApplied = { - tag: 'UnsubscribeApplied'; - value: __Infer; -}; -export type SubscriptionError = { - tag: 'SubscriptionError'; - value: __Infer; -}; -export type SubscribeMultiApplied = { - tag: 'SubscribeMultiApplied'; - value: __Infer; -}; -export type UnsubscribeMultiApplied = { - tag: 'UnsubscribeMultiApplied'; - value: __Infer; -}; -export type ProcedureResult = { - tag: 'ProcedureResult'; - value: __Infer; -}; diff --git a/crates/bindings-typescript/src/sdk/client_api/update_status_type.ts b/crates/bindings-typescript/src/sdk/client_api/update_status_type.ts index 0a666da5719..e5db16fc2e8 100644 --- a/crates/bindings-typescript/src/sdk/client_api/update_status_type.ts +++ b/crates/bindings-typescript/src/sdk/client_api/update_status_type.ts @@ -11,8 +11,6 @@ import { } from '../../lib/type_builders'; import DatabaseUpdate from './database_update_type'; -import * as UpdateStatusVariants from './update_status_variants'; - // The tagged union or sum type for the algebraic type `UpdateStatus`. const UpdateStatus = __t.enum('UpdateStatus', { get Committed() { diff --git a/crates/bindings-typescript/src/sdk/client_api/update_status_variants.ts b/crates/bindings-typescript/src/sdk/client_api/update_status_variants.ts deleted file mode 100644 index e2234e39e52..00000000000 --- a/crates/bindings-typescript/src/sdk/client_api/update_status_variants.ts +++ /dev/null @@ -1,19 +0,0 @@ -// 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 '../../lib/type_builders'; -import DatabaseUpdateType from './database_update_type'; - -export type Committed = { - tag: 'Committed'; - value: __Infer; -}; -export type Failed = { tag: 'Failed'; value: string }; -export type OutOfEnergy = { tag: 'OutOfEnergy' }; diff --git a/crates/bindings-typescript/src/sdk/client_cache.ts b/crates/bindings-typescript/src/sdk/client_cache.ts index afadb578a50..33c7bca6aa7 100644 --- a/crates/bindings-typescript/src/sdk/client_cache.ts +++ b/crates/bindings-typescript/src/sdk/client_cache.ts @@ -114,7 +114,7 @@ export class ClientCache { ): TableCacheForTableName { const name = tableDef.name as N; - let table = this.tables.get(name); + const table = this.tables.get(name); if (table) { return table; } diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index 1842f31fde2..efc32059ae9 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -5,7 +5,6 @@ import type { ErrorContextInterface, Identity, RemoteModuleOf, - SubscriptionEventContextInterface, } from '../'; import { ensureMinimumVersionOrThrow } from './version'; import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index cae78d0564c..5b2a2187631 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -29,7 +29,6 @@ import type { } from './message_types.ts'; import type { ReducerEvent } from './reducer_event.ts'; import { - type RemoteModule, type UntypedRemoteModule, } from './spacetime_module.ts'; import { @@ -52,7 +51,6 @@ import type { ReducerEventInfo, ReducersView, SetReducerFlags, - UntypedReducersDef, } from './reducers.ts'; import type { ClientDbView } from './db_view.ts'; import type { UntypedTableDef } from '../lib/table.ts'; @@ -897,9 +895,9 @@ export class DbConnectionImpl params: object, flags: CallReducerFlags ) { - let writer = new BinaryWriter(1024); + const writer = new BinaryWriter(1024); ProductType.serializeValue(writer, paramsType, params); - let argsBuffer = writer.getBuffer(); + const argsBuffer = writer.getBuffer(); this.callReducer(reducerName, argsBuffer, flags); } diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index a991794594e..230170ac840 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -2,7 +2,6 @@ import type { ClientDbView } from './db_view'; import type { ReducersView, SetReducerFlags, - UntypedReducersDef, } from './reducers'; import type { UntypedRemoteModule } from './spacetime_module'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; diff --git a/crates/bindings-typescript/src/sdk/message_types.ts b/crates/bindings-typescript/src/sdk/message_types.ts index 24e834a1ebe..6a7bdaa4dec 100644 --- a/crates/bindings-typescript/src/sdk/message_types.ts +++ b/crates/bindings-typescript/src/sdk/message_types.ts @@ -2,7 +2,7 @@ import { ConnectionId, type Infer } from '../'; import { Identity } from '../'; import type { TableUpdate } from './table_cache.ts'; import { Timestamp } from '../'; -import type { RowType, Table, UntypedTableDef } from '../lib/table.ts'; +import type { UntypedTableDef } from '../lib/table.ts'; import type UpdateStatus from './client_api/update_status_type.ts'; export type InitialSubscriptionMessage = { diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index 66e8330dc0b..2ff2a1ac194 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -4,12 +4,10 @@ import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; import type { EventContextInterface, - ClientTable, TableDefForTableName, } from './index.ts'; -import type { RowType, Table, UntypedTableDef } from '../lib/table.ts'; +import type { RowType, UntypedTableDef } from '../lib/table.ts'; import type { - ClientTableCore, ClientTableCoreImplementable, } from './client_table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; diff --git a/crates/bindings-typescript/test-app/src/App.tsx b/crates/bindings-typescript/test-app/src/App.tsx index 3bbee2cbf27..720504f78d8 100644 --- a/crates/bindings-typescript/test-app/src/App.tsx +++ b/crates/bindings-typescript/test-app/src/App.tsx @@ -20,7 +20,6 @@ function App() { console.log('Player inserted:', row); }, }); - const x = players[0]; const createPlayer = useReducer(reducers.createPlayer); useEffect(() => { diff --git a/crates/bindings-typescript/test-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-app/src/module_bindings/index.ts index fe0c2f4fbe0..fa5382b6be6 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -28,25 +28,25 @@ import { } from '../../../src/index'; // Import and reexport all reducer arg types -import CreatePlayer from './create_player_reducer.ts'; +import CreatePlayer from './create_player_reducer'; export { CreatePlayer }; // Import and reexport all table handle types -import PlayerRow from './player_table.ts'; +import PlayerRow from './player_table'; export { PlayerRow }; -import UnindexedPlayerRow from './unindexed_player_table.ts'; +import UnindexedPlayerRow from './unindexed_player_table'; export { UnindexedPlayerRow }; -import UserRow from './user_table.ts'; +import UserRow from './user_table'; export { UserRow }; // Import and reexport all types -import Player from './player_type.ts'; +import Player from './player_type'; export { Player }; -import Point from './point_type.ts'; +import Point from './point_type'; export { Point }; -import UnindexedPlayer from './unindexed_player_type.ts'; +import UnindexedPlayer from './unindexed_player_type'; export { UnindexedPlayer }; -import User from './user_type.ts'; +import User from './user_type'; export { User }; const tablesSchema = __schema( diff --git a/crates/bindings-typescript/test-react-router-app/package.json b/crates/bindings-typescript/test-react-router-app/package.json index babf93f8200..235f719db0e 100644 --- a/crates/bindings-typescript/test-react-router-app/package.json +++ b/crates/bindings-typescript/test-react-router-app/package.json @@ -26,6 +26,7 @@ "devDependencies": { "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "@types/react-router-dom": "^5.3.3", "@vitejs/plugin-react": "^4.3.1", "typescript": "^5.2.2", "vite": "^7.1.5" diff --git a/crates/bindings-typescript/test-react-router-app/server/src/lib.rs b/crates/bindings-typescript/test-react-router-app/server/src/lib.rs index 799eb5f91f3..520e68eeaec 100644 --- a/crates/bindings-typescript/test-react-router-app/server/src/lib.rs +++ b/crates/bindings-typescript/test-react-router-app/server/src/lib.rs @@ -59,6 +59,7 @@ fn increment_counter(ctx: &ReducerContext) -> Result<(), String> { fn clear_counter(ctx: &ReducerContext) { for row in ctx.db.counter().iter() { ctx.db.counter().id().delete(row.id); + ctx.db.counter().insert(Counter { id: 0, count: 0 }); } for row in ctx.db.user().iter() { diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/clear_counter_reducer.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/clear_counter_reducer.ts index b05311785e6..2454b459929 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/clear_counter_reducer.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/clear_counter_reducer.ts @@ -4,64 +4,10 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -export type ClearCounter = {}; -let _cached_ClearCounter_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const ClearCounter = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_ClearCounter_type_value) return _cached_ClearCounter_type_value; - _cached_ClearCounter_type_value = __AlgebraicTypeValue.Product({ - elements: [], - }); - _cached_ClearCounter_type_value.value.elements.push(); - return _cached_ClearCounter_type_value; - }, - - serialize(writer: __BinaryWriter, value: ClearCounter): void { - __AlgebraicTypeValue.serializeValue( - writer, - ClearCounter.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): ClearCounter { - return __AlgebraicTypeValue.deserializeValue( - reader, - ClearCounter.getTypeScriptAlgebraicType() - ); - }, -}; - -export default ClearCounter; +export default {}; diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_connected_reducer.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_connected_reducer.ts index ac7a7e2b90d..2454b459929 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_connected_reducer.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_connected_reducer.ts @@ -4,65 +4,10 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -export type ClientConnected = {}; -let _cached_ClientConnected_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const ClientConnected = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_ClientConnected_type_value) - return _cached_ClientConnected_type_value; - _cached_ClientConnected_type_value = __AlgebraicTypeValue.Product({ - elements: [], - }); - _cached_ClientConnected_type_value.value.elements.push(); - return _cached_ClientConnected_type_value; - }, - - serialize(writer: __BinaryWriter, value: ClientConnected): void { - __AlgebraicTypeValue.serializeValue( - writer, - ClientConnected.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): ClientConnected { - return __AlgebraicTypeValue.deserializeValue( - reader, - ClientConnected.getTypeScriptAlgebraicType() - ); - }, -}; - -export default ClientConnected; +export default {}; diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_disconnected_reducer.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_disconnected_reducer.ts index d4369a3e513..2454b459929 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_disconnected_reducer.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/client_disconnected_reducer.ts @@ -4,65 +4,10 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -export type ClientDisconnected = {}; -let _cached_ClientDisconnected_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const ClientDisconnected = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_ClientDisconnected_type_value) - return _cached_ClientDisconnected_type_value; - _cached_ClientDisconnected_type_value = __AlgebraicTypeValue.Product({ - elements: [], - }); - _cached_ClientDisconnected_type_value.value.elements.push(); - return _cached_ClientDisconnected_type_value; - }, - - serialize(writer: __BinaryWriter, value: ClientDisconnected): void { - __AlgebraicTypeValue.serializeValue( - writer, - ClientDisconnected.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): ClientDisconnected { - return __AlgebraicTypeValue.deserializeValue( - reader, - ClientDisconnected.getTypeScriptAlgebraicType() - ); - }, -}; - -export default ClientDisconnected; +export default {}; diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts index 9ffbf83d2c4..d492e387b23 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts @@ -4,115 +4,13 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -import { Counter } from './counter_type'; -import { - type EventContext, - type Reducer, - RemoteReducers, - RemoteTables, -} from '.'; -declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; - -/** - * Table handle for the table `counter`. - * - * Obtain a handle from the [`counter`] property on [`RemoteTables`], - * like `ctx.db.counter`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.counter.on_insert(...)`. - */ -export class CounterTableHandle - implements __TableHandle -{ - // phantom type to track the table name - readonly tableName!: TableName; - tableCache: __TableCache; - - constructor(tableCache: __TableCache) { - this.tableCache = tableCache; - } - - count(): number { - return this.tableCache.count(); - } - - iter(): Iterable { - return this.tableCache.iter(); - } - /** - * Access to the `id` unique index on the table `counter`, - * which allows point queries on the field of the same name - * via the [`CounterIdUnique.find`] method. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.counter.id().find(...)`. - * - * Get a handle on the `id` unique index on the table `counter`. - */ - id = { - // Find the subscribed row whose `id` column value is equal to `colVal`, - // if such a row is present in the client cache. - find: (colVal: number): Counter | undefined => { - for (let row of this.tableCache.iter()) { - if (__deepEqual(row.id, colVal)) { - return row; - } - } - }, - }; - - onInsert = (cb: (ctx: EventContext, row: Counter) => void) => { - return this.tableCache.onInsert(cb); - }; - - removeOnInsert = (cb: (ctx: EventContext, row: Counter) => void) => { - return this.tableCache.removeOnInsert(cb); - }; - - onDelete = (cb: (ctx: EventContext, row: Counter) => void) => { - return this.tableCache.onDelete(cb); - }; - - removeOnDelete = (cb: (ctx: EventContext, row: Counter) => void) => { - return this.tableCache.removeOnDelete(cb); - }; - - // Updates are only defined for tables with primary keys. - onUpdate = ( - cb: (ctx: EventContext, oldRow: Counter, newRow: Counter) => void - ) => { - return this.tableCache.onUpdate(cb); - }; - removeOnUpdate = ( - cb: (ctx: EventContext, onRow: Counter, newRow: Counter) => void - ) => { - return this.tableCache.removeOnUpdate(cb); - }; -} +export default __t.row({ + id: __t.u32(), + count: __t.u32(), +}); diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_type.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_type.ts index 9819fbb63e3..419b8dbfe5b 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_type.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_type.ts @@ -4,68 +4,13 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -export type Counter = { - id: number; - count: number; -}; -let _cached_Counter_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const Counter = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_Counter_type_value) return _cached_Counter_type_value; - _cached_Counter_type_value = __AlgebraicTypeValue.Product({ elements: [] }); - _cached_Counter_type_value.value.elements.push( - { name: 'id', algebraicType: __AlgebraicTypeValue.U32 }, - { name: 'count', algebraicType: __AlgebraicTypeValue.U32 } - ); - return _cached_Counter_type_value; - }, - - serialize(writer: __BinaryWriter, value: Counter): void { - __AlgebraicTypeValue.serializeValue( - writer, - Counter.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): Counter { - return __AlgebraicTypeValue.deserializeValue( - reader, - Counter.getTypeScriptAlgebraicType() - ); - }, -}; - -export default Counter; +export default __t.object('Counter', { + id: __t.u32(), + count: __t.u32(), +}); diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/increment_counter_reducer.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/increment_counter_reducer.ts index 6fe15b551af..2454b459929 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/increment_counter_reducer.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/increment_counter_reducer.ts @@ -4,65 +4,10 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -export type IncrementCounter = {}; -let _cached_IncrementCounter_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const IncrementCounter = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_IncrementCounter_type_value) - return _cached_IncrementCounter_type_value; - _cached_IncrementCounter_type_value = __AlgebraicTypeValue.Product({ - elements: [], - }); - _cached_IncrementCounter_type_value.value.elements.push(); - return _cached_IncrementCounter_type_value; - }, - - serialize(writer: __BinaryWriter, value: IncrementCounter): void { - __AlgebraicTypeValue.serializeValue( - writer, - IncrementCounter.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): IncrementCounter { - return __AlgebraicTypeValue.deserializeValue( - reader, - IncrementCounter.getTypeScriptAlgebraicType() - ); - }, -}; - -export default IncrementCounter; +export default {}; diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts index 07472dc2cf0..fa3f793bdb5 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts @@ -1,298 +1,125 @@ // 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 ../../../src/index cli version 1.6.0 (commit 542d26d7ffecafe93e40ac1a991c2ef2b4e4d0cb). +// This was generated using ../../../src/index cli version 1.6.0 (commit 0b0b06d5f67fecff50eb7b1c79bff5dad686f5d2). /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, DbConnectionBuilder as __DbConnectionBuilder, DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, + TypeBuilder as __TypeBuilder, + convertToAccessorMap as __convertToAccessorMap, + 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 TableHandle as __TableHandle, } from '../../../src/index'; // Import and reexport all reducer arg types -import { ClearCounter } from './clear_counter_reducer.ts'; +import ClearCounter from './clear_counter_reducer'; export { ClearCounter }; -import { ClientConnected } from './client_connected_reducer.ts'; +import ClientConnected from './client_connected_reducer'; export { ClientConnected }; -import { ClientDisconnected } from './client_disconnected_reducer.ts'; +import ClientDisconnected from './client_disconnected_reducer'; export { ClientDisconnected }; -import { IncrementCounter } from './increment_counter_reducer.ts'; +import IncrementCounter from './increment_counter_reducer'; export { IncrementCounter }; // Import and reexport all table handle types -import { CounterTableHandle } from './counter_table.ts'; -export { CounterTableHandle }; -import { OfflineUserTableHandle } from './offline_user_table.ts'; -export { OfflineUserTableHandle }; -import { UserTableHandle } from './user_table.ts'; -export { UserTableHandle }; +import CounterRow from './counter_table'; +export { CounterRow }; +import OfflineUserRow from './offline_user_table'; +export { OfflineUserRow }; +import UserRow from './user_table'; +export { UserRow }; // Import and reexport all types -import { Counter } from './counter_type.ts'; +import Counter from './counter_type'; export { Counter }; -import { User } from './user_type.ts'; +import User from './user_type'; export { User }; -const REMOTE_MODULE = { - tables: { - counter: { - tableName: 'counter' as const, - rowType: Counter.getTypeScriptAlgebraicType(), - primaryKey: 'id', - primaryKeyInfo: { - colName: 'id', - colType: ( - Counter.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product - ).value.elements[0].algebraicType, - }, - }, - offline_user: { - tableName: 'offline_user' as const, - rowType: User.getTypeScriptAlgebraicType(), - primaryKey: 'identity', - primaryKeyInfo: { - colName: 'identity', - colType: ( - User.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product - ).value.elements[0].algebraicType, - }, +const tablesSchema = __schema( + __table( + { + name: 'counter', + indexes: [], }, - user: { - tableName: 'user' as const, - rowType: User.getTypeScriptAlgebraicType(), - primaryKey: 'identity', - primaryKeyInfo: { - colName: 'identity', - colType: ( - User.getTypeScriptAlgebraicType() as __AlgebraicTypeVariants.Product - ).value.elements[0].algebraicType, - }, - }, - }, - reducers: { - clear_counter: { - reducerName: 'clear_counter', - argsType: ClearCounter.getTypeScriptAlgebraicType(), + CounterRow + ), + __table( + { + name: 'offline_user', + indexes: [], }, - client_connected: { - reducerName: 'client_connected', - argsType: ClientConnected.getTypeScriptAlgebraicType(), + UserRow + ), + __table( + { + name: 'user', + indexes: [], }, - client_disconnected: { - reducerName: 'client_disconnected', - argsType: ClientDisconnected.getTypeScriptAlgebraicType(), - }, - increment_counter: { - reducerName: 'increment_counter', - argsType: IncrementCounter.getTypeScriptAlgebraicType(), - }, - }, - versionInfo: { - cliVersion: '1.6.0', - }, - // Constructors which are used by the DbConnectionImpl to - // extract type information from the generated RemoteModule. - // - // NOTE: This is not strictly necessary for `eventContextConstructor` because - // all we do is build a TypeScript object which we could have done inside the - // SDK, but if in the future we wanted to create a class this would be - // necessary because classes have methods, so we'll keep it. - eventContextConstructor: ( - imp: __DbConnectionImpl, - event: __Event - ) => { - return { - ...(imp as DbConnection), - event, - }; - }, - dbViewConstructor: (imp: __DbConnectionImpl) => { - return new RemoteTables(imp); - }, - reducersConstructor: ( - imp: __DbConnectionImpl, - setReducerFlags: SetReducerFlags - ) => { - return new RemoteReducers(imp, setReducerFlags); - }, - setReducerFlagsConstructor: () => { - return new SetReducerFlags(); - }, -}; - -// A type representing all the possible variants of a reducer. -export type Reducer = - | never - | { name: 'ClearCounter'; args: ClearCounter } - | { name: 'ClientConnected'; args: ClientConnected } - | { name: 'ClientDisconnected'; args: ClientDisconnected } - | { name: 'IncrementCounter'; args: IncrementCounter }; - -export class RemoteReducers { - constructor( - private connection: __DbConnectionImpl, - private setCallReducerFlags: SetReducerFlags - ) {} - - clearCounter() { - this.connection.callReducer( - 'clear_counter', - new Uint8Array(0), - this.setCallReducerFlags.clearCounterFlags - ); - } - - onClearCounter(callback: (ctx: ReducerEventContext) => void) { - this.connection.onReducer('clear_counter', callback); - } - - removeOnClearCounter(callback: (ctx: ReducerEventContext) => void) { - this.connection.offReducer('clear_counter', callback); - } - - onClientConnected(callback: (ctx: ReducerEventContext) => void) { - this.connection.onReducer('client_connected', callback); - } - - removeOnClientConnected(callback: (ctx: ReducerEventContext) => void) { - this.connection.offReducer('client_connected', callback); - } - - onClientDisconnected(callback: (ctx: ReducerEventContext) => void) { - this.connection.onReducer('client_disconnected', callback); - } - - removeOnClientDisconnected(callback: (ctx: ReducerEventContext) => void) { - this.connection.offReducer('client_disconnected', callback); - } - - incrementCounter() { - this.connection.callReducer( - 'increment_counter', - new Uint8Array(0), - this.setCallReducerFlags.incrementCounterFlags - ); - } - - onIncrementCounter(callback: (ctx: ReducerEventContext) => void) { - this.connection.onReducer('increment_counter', callback); - } + UserRow + ) +); - removeOnIncrementCounter(callback: (ctx: ReducerEventContext) => void) { - this.connection.offReducer('increment_counter', callback); - } -} - -export class SetReducerFlags { - clearCounterFlags: __CallReducerFlags = 'FullUpdate'; - clearCounter(flags: __CallReducerFlags) { - this.clearCounterFlags = flags; - } - - incrementCounterFlags: __CallReducerFlags = 'FullUpdate'; - incrementCounter(flags: __CallReducerFlags) { - this.incrementCounterFlags = flags; - } -} +const reducersSchema = __reducers( + __reducerSchema('clear_counter', ClearCounter), + __reducerSchema('increment_counter', IncrementCounter) +); -export class RemoteTables { - constructor(private connection: __DbConnectionImpl) {} - - get counter(): CounterTableHandle<'counter'> { - // clientCache is a private property - return new CounterTableHandle( - ( - this.connection as unknown as { clientCache: __ClientCache } - ).clientCache.getOrCreateTable(REMOTE_MODULE.tables.counter) - ); - } +const REMOTE_MODULE = { + versionInfo: { + cliVersion: '1.6.0' as const, + }, + tables: tablesSchema.schemaType.tables, + reducers: reducersSchema.reducersType.reducers, +} satisfies __RemoteModule< + typeof tablesSchema.schemaType, + typeof reducersSchema.reducersType +>; - get offlineUser(): OfflineUserTableHandle<'offline_user'> { - // clientCache is a private property - return new OfflineUserTableHandle( - ( - this.connection as unknown as { clientCache: __ClientCache } - ).clientCache.getOrCreateTable(REMOTE_MODULE.tables.offline_user) - ); - } +export const tables = __convertToAccessorMap(tablesSchema.schemaType.tables); +export const reducers = __convertToAccessorMap( + reducersSchema.reducersType.reducers +); - get user(): UserTableHandle<'user'> { - // clientCache is a private property - return new UserTableHandle( - ( - this.connection as unknown as { clientCache: __ClientCache } - ).clientCache.getOrCreateTable(REMOTE_MODULE.tables.user) - ); - } -} +export type EventContext = __EventContextInterface; +export type ReducerEventContext = __ReducerEventContextInterface< + typeof REMOTE_MODULE +>; +export type SubscriptionEventContext = __SubscriptionEventContextInterface< + typeof REMOTE_MODULE +>; +export type ErrorContext = __ErrorContextInterface; export class SubscriptionBuilder extends __SubscriptionBuilderImpl< - RemoteTables, - RemoteReducers, - SetReducerFlags + typeof REMOTE_MODULE > {} -export class DbConnection extends __DbConnectionImpl< - RemoteTables, - RemoteReducers, - SetReducerFlags -> { - static builder = (): __DbConnectionBuilder< - DbConnection, - ErrorContext, - SubscriptionEventContext - > => { - return new __DbConnectionBuilder< - DbConnection, - ErrorContext, - SubscriptionEventContext - >(REMOTE_MODULE, (imp: __DbConnectionImpl) => imp as DbConnection); +export class DbConnectionBuilder extends __DbConnectionBuilder {} + +export class DbConnection extends __DbConnectionImpl { + static builder = (): DbConnectionBuilder => { + return new DbConnectionBuilder( + REMOTE_MODULE, + (config: __DbConnectionConfig) => + new DbConnection(config) + ); }; subscriptionBuilder = (): SubscriptionBuilder => { return new SubscriptionBuilder(this); }; } - -export type EventContext = __EventContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags, - Reducer ->; -export type ReducerEventContext = __ReducerEventContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags, - Reducer ->; -export type SubscriptionEventContext = __SubscriptionEventContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags ->; -export type ErrorContext = __ErrorContextInterface< - RemoteTables, - RemoteReducers, - SetReducerFlags ->; diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts index cb526e2dee8..7a83afcebf0 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts @@ -4,113 +4,13 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -import { User } from './user_type'; -import { - type EventContext, - type Reducer, - RemoteReducers, - RemoteTables, -} from '.'; -declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; - -/** - * Table handle for the table `offline_user`. - * - * Obtain a handle from the [`offlineUser`] property on [`RemoteTables`], - * like `ctx.db.offlineUser`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.offlineUser.on_insert(...)`. - */ -export class OfflineUserTableHandle - implements __TableHandle -{ - // phantom type to track the table name - readonly tableName!: TableName; - tableCache: __TableCache; - - constructor(tableCache: __TableCache) { - this.tableCache = tableCache; - } - - count(): number { - return this.tableCache.count(); - } - - iter(): Iterable { - return this.tableCache.iter(); - } - /** - * Access to the `identity` unique index on the table `offline_user`, - * which allows point queries on the field of the same name - * via the [`OfflineUserIdentityUnique.find`] method. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.offlineUser.identity().find(...)`. - * - * Get a handle on the `identity` unique index on the table `offline_user`. - */ - identity = { - // Find the subscribed row whose `identity` column value is equal to `colVal`, - // if such a row is present in the client cache. - find: (colVal: __Identity): User | undefined => { - for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, colVal)) { - return row; - } - } - }, - }; - - onInsert = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.onInsert(cb); - }; - - removeOnInsert = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.removeOnInsert(cb); - }; - - onDelete = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.onDelete(cb); - }; - - removeOnDelete = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.removeOnDelete(cb); - }; - - // Updates are only defined for tables with primary keys. - onUpdate = (cb: (ctx: EventContext, oldRow: User, newRow: User) => void) => { - return this.tableCache.onUpdate(cb); - }; - removeOnUpdate = ( - cb: (ctx: EventContext, onRow: User, newRow: User) => void - ) => { - return this.tableCache.removeOnUpdate(cb); - }; -} +export default __t.row({ + identity: __t.identity(), + hasIncrementedCount: __t.u32(), +}); diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts index e8364a92e8f..7a83afcebf0 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts @@ -4,113 +4,13 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -import { User } from './user_type'; -import { - type EventContext, - type Reducer, - RemoteReducers, - RemoteTables, -} from '.'; -declare type __keep = [EventContext, Reducer, RemoteReducers, RemoteTables]; - -/** - * Table handle for the table `user`. - * - * Obtain a handle from the [`user`] property on [`RemoteTables`], - * like `ctx.db.user`. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.user.on_insert(...)`. - */ -export class UserTableHandle - implements __TableHandle -{ - // phantom type to track the table name - readonly tableName!: TableName; - tableCache: __TableCache; - - constructor(tableCache: __TableCache) { - this.tableCache = tableCache; - } - - count(): number { - return this.tableCache.count(); - } - - iter(): Iterable { - return this.tableCache.iter(); - } - /** - * Access to the `identity` unique index on the table `user`, - * which allows point queries on the field of the same name - * via the [`UserIdentityUnique.find`] method. - * - * Users are encouraged not to explicitly reference this type, - * but to directly chain method calls, - * like `ctx.db.user.identity().find(...)`. - * - * Get a handle on the `identity` unique index on the table `user`. - */ - identity = { - // Find the subscribed row whose `identity` column value is equal to `colVal`, - // if such a row is present in the client cache. - find: (colVal: __Identity): User | undefined => { - for (let row of this.tableCache.iter()) { - if (__deepEqual(row.identity, colVal)) { - return row; - } - } - }, - }; - - onInsert = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.onInsert(cb); - }; - - removeOnInsert = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.removeOnInsert(cb); - }; - - onDelete = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.onDelete(cb); - }; - - removeOnDelete = (cb: (ctx: EventContext, row: User) => void) => { - return this.tableCache.removeOnDelete(cb); - }; - - // Updates are only defined for tables with primary keys. - onUpdate = (cb: (ctx: EventContext, oldRow: User, newRow: User) => void) => { - return this.tableCache.onUpdate(cb); - }; - removeOnUpdate = ( - cb: (ctx: EventContext, onRow: User, newRow: User) => void - ) => { - return this.tableCache.removeOnUpdate(cb); - }; -} +export default __t.row({ + identity: __t.identity(), + hasIncrementedCount: __t.u32(), +}); diff --git a/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_type.ts b/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_type.ts index bd12911fa23..ad6fb447e3c 100644 --- a/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_type.ts +++ b/crates/bindings-typescript/test-react-router-app/src/module_bindings/user_type.ts @@ -4,71 +4,13 @@ /* eslint-disable */ /* tslint:disable */ import { - AlgebraicType as __AlgebraicTypeValue, - BinaryReader as __BinaryReader, - BinaryWriter as __BinaryWriter, - ClientCache as __ClientCache, - ConnectionId as __ConnectionId, - DbConnectionBuilder as __DbConnectionBuilder, - DbConnectionImpl as __DbConnectionImpl, - Identity as __Identity, - SubscriptionBuilderImpl as __SubscriptionBuilderImpl, - TableCache as __TableCache, - TimeDuration as __TimeDuration, - Timestamp as __Timestamp, - deepEqual as __deepEqual, - type AlgebraicType as __AlgebraicTypeType, - type AlgebraicTypeVariants as __AlgebraicTypeVariants, - type CallReducerFlags as __CallReducerFlags, - type ErrorContextInterface as __ErrorContextInterface, - type Event as __Event, - type EventContextInterface as __EventContextInterface, - type ReducerEventContextInterface as __ReducerEventContextInterface, - type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, - type TableHandle as __TableHandle, + TypeBuilder as __TypeBuilder, + t as __t, + type AlgebraicTypeType as __AlgebraicTypeType, + type Infer as __Infer, } from '../../../src/index'; -export type User = { - identity: __Identity; - hasIncrementedCount: number; -}; -let _cached_User_type_value: __AlgebraicTypeType | null = null; - -/** - * An object for generated helper functions. - */ -export const User = { - /** - * A function which returns this type represented as an AlgebraicType. - * This function is derived from the AlgebraicType used to generate this type. - */ - getTypeScriptAlgebraicType(): __AlgebraicTypeType { - if (_cached_User_type_value) return _cached_User_type_value; - _cached_User_type_value = __AlgebraicTypeValue.Product({ elements: [] }); - _cached_User_type_value.value.elements.push( - { - name: 'identity', - algebraicType: __AlgebraicTypeValue.createIdentityType(), - }, - { name: 'hasIncrementedCount', algebraicType: __AlgebraicTypeValue.U32 } - ); - return _cached_User_type_value; - }, - - serialize(writer: __BinaryWriter, value: User): void { - __AlgebraicTypeValue.serializeValue( - writer, - User.getTypeScriptAlgebraicType(), - value - ); - }, - - deserialize(reader: __BinaryReader): User { - return __AlgebraicTypeValue.deserializeValue( - reader, - User.getTypeScriptAlgebraicType() - ); - }, -}; - -export default User; +export default __t.object('User', { + identity: __t.identity(), + hasIncrementedCount: __t.u32(), +}); diff --git a/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx b/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx index bc7d5674131..d5787e39620 100644 --- a/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx +++ b/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx @@ -1,9 +1,10 @@ -import { useSpacetimeDB, useTable } from '../../../src/react'; -import { DbConnection, Counter } from '../module_bindings'; +import { useReducer, useTable } from '../../../src/react'; +import { tables, reducers } from '../module_bindings'; export default function CounterPage() { - const stdb = useSpacetimeDB(); - const { rows: counter } = useTable('counter'); + const counter = useTable(tables.counter); + const incrementCounter = useReducer(reducers.incrementCounter); + const clearCounter = useReducer(reducers.clearCounter); console.log('Rendering CounterPage, current counter:', counter); @@ -11,13 +12,13 @@ export default function CounterPage() { <>

Counter

-

Click above to increment the count, click below to clear the count.

-
diff --git a/crates/bindings-typescript/test-react-router-app/src/pages/UserPage.tsx b/crates/bindings-typescript/test-react-router-app/src/pages/UserPage.tsx index 67eccc256e0..b73e004f791 100644 --- a/crates/bindings-typescript/test-react-router-app/src/pages/UserPage.tsx +++ b/crates/bindings-typescript/test-react-router-app/src/pages/UserPage.tsx @@ -1,28 +1,14 @@ -import { useEffect } from 'react'; import { useSpacetimeDB, useTable } from '../../../src/react'; -import { DbConnection, User } from '../module_bindings'; +import { tables, User } from '../module_bindings'; +import { Infer } from '../../../src'; export default function UserPage() { - const stdb = useSpacetimeDB(); - const { rows: users } = useTable('user'); + const connection = useSpacetimeDB(); + const users = useTable(tables.user); - useEffect(() => { - if (!stdb.isActive) return; - - const sub = stdb - .subscriptionBuilder() - .onError((err: any) => console.error('User subscription error:', err)) - .onApplied(() => console.log('User subscription applied')) - .subscribe('SELECT * FROM user'); - - return () => { - sub.unsubscribeThen(() => console.log('User subscription cleaned up')); - }; - }, [stdb.isActive]); - - const identityHex = stdb.identity?.toHexString(); + const identityHex = connection.identity?.toHexString(); const currentUser = users.find( - (u: User) => u.identity.toHexString() === identityHex + (u: Infer) => u.identity.toHexString() === identityHex ); return ( diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 704ccb6069f..d6bd510dd0a 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -1,7 +1,7 @@ use crate::util::{ - is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, iter_types, print_auto_generated_version_comment + is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, iter_types, print_auto_generated_version_comment, }; -use crate::{OutputFile}; +use crate::OutputFile; use super::util::{collect_case, print_auto_generated_file_comment, type_ref_name}; @@ -13,7 +13,9 @@ use convert_case::{Case, Casing}; use spacetimedb_lib::sats::layout::PrimitiveType; use spacetimedb_lib::sats::AlgebraicTypeRef; use spacetimedb_primitives::ColId; -use spacetimedb_schema::def::{BTreeAlgorithm, IndexAlgorithm, ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; +use spacetimedb_schema::def::{ + BTreeAlgorithm, IndexAlgorithm, ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef, +}; use spacetimedb_schema::identifier::Identifier; use spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse, ProductTypeDef}; @@ -46,34 +48,12 @@ impl Lang for TypeScript { } }; - let define_variants_for_sum = |variants: &[(Identifier, AlgebraicTypeUse)]| { - let mut output = CodeIndenter::new(String::new(), INDENT); - let out = &mut output; - - print_file_header(out, false, true); - // Note that the current type is not included in dont_import below. - gen_and_print_imports(module, out, variants, &[], Some("Type")); - writeln!(out); - write_variant_types(module, out, variants); - out.newline(); - OutputFile { - filename: variants_module_name(&typ.name) + ".ts", - code: output.into_inner(), - } - }; - let define_type_for_sum = |variants: &[(Identifier, AlgebraicTypeUse)]| { let mut output = CodeIndenter::new(String::new(), INDENT); let out = &mut output; print_file_header(out, false, true); gen_and_print_imports(module, out, variants, &[typ.ty], None); - writeln!( - out, - "import * as {}Variants from './{}'", - type_name, - variants_module_name(&typ.name) - ); writeln!(out); // For the purpose of bootstrapping AlgebraicType, if the name of the type // is `AlgebraicType`, we need to use an alias. @@ -90,10 +70,7 @@ impl Lang for TypeScript { vec![define_type_for_product(product)] } AlgebraicTypeDef::Sum(sum) => { - vec![ - define_variants_for_sum(&sum.variants), - define_type_for_sum(&sum.variants), - ] + vec![define_type_for_sum(&sum.variants)] } AlgebraicTypeDef::PlainEnum(plain_enum) => { let variants = plain_enum @@ -102,7 +79,7 @@ impl Lang for TypeScript { .cloned() .map(|var| (var, AlgebraicTypeUse::Unit)) .collect::>(); - vec![define_variants_for_sum(&variants), define_type_for_sum(&variants)] + vec![define_type_for_sum(&variants)] } } } @@ -112,7 +89,7 @@ impl Lang for TypeScript { /// table({ /// name: 'player', /// indexes: [ - /// { name: 'this_is_an_index', algorithm: "btree", columns: [ "ownerId" ] } + /// { name: 'this_is_an_index', algorithm: "btree", columns: [ "ownerId" ] } /// ], /// }, t.row({ /// id: t.u32().primaryKey(), @@ -190,7 +167,7 @@ impl Lang for TypeScript { writeln!(out, "// Import and reexport all reducer arg types"); for reducer in iter_reducers(module) { let reducer_name = &reducer.name; - let reducer_module_name = reducer_module_name(reducer_name) + ".ts"; + let reducer_module_name = reducer_module_name(reducer_name); let args_type = reducer_args_type_name(&reducer.name); writeln!(out, "import {args_type} from \"./{reducer_module_name}\";"); writeln!(out, "export {{ {args_type} }};"); @@ -200,7 +177,7 @@ impl Lang for TypeScript { writeln!(out, "// Import and reexport all table handle types"); for table in iter_tables(module) { let table_name = &table.name; - let table_module_name = table_module_name(table_name) + ".ts"; + let table_module_name = table_module_name(table_name); let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); // TODO: This really shouldn't be necessary. We could also have `table()` accept // `__t.object(...)`s. @@ -212,13 +189,13 @@ impl Lang for TypeScript { writeln!(out, "// Import and reexport all types"); for ty in iter_types(module) { let type_name = collect_case(Case::Pascal, ty.name.name_segments()); - let type_module_name = type_module_name(&ty.name) + ".ts"; + let type_module_name = type_module_name(&ty.name); writeln!(out, "import {type_name} from \"./{type_module_name}\";"); writeln!(out, "export {{ {type_name} }};"); } out.newline(); - + writeln!(out); writeln!(out, "const tablesSchema = __schema("); out.indent(1); @@ -271,8 +248,14 @@ impl Lang for TypeScript { out.dedent(1); writeln!(out); - writeln!(out, "export const tables = __convertToAccessorMap(tablesSchema.schemaType.tables);"); - writeln!(out, "export const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);"); + writeln!( + out, + "export const tables = __convertToAccessorMap(tablesSchema.schemaType.tables);" + ); + writeln!( + out, + "export const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);" + ); writeln!(out); out.newline(); @@ -297,7 +280,10 @@ impl Lang for TypeScript { writeln!(out); - writeln!(out, "export class SubscriptionBuilder extends __SubscriptionBuilderImpl<"); + writeln!( + out, + "export class SubscriptionBuilder extends __SubscriptionBuilderImpl<" + ); out.indent(1); writeln!(out, "typeof REMOTE_MODULE"); out.dedent(1); @@ -316,10 +302,7 @@ impl Lang for TypeScript { "export class DbConnection extends __DbConnectionImpl {{" ); out.indent(1); - writeln!( - out, - "static builder = (): DbConnectionBuilder => {{" - ); + writeln!(out, "static builder = (): DbConnectionBuilder => {{"); out.indent(1); writeln!( out, @@ -327,13 +310,10 @@ impl Lang for TypeScript { ); out.dedent(1); writeln!(out, "}};"); - writeln!( - out, - "subscriptionBuilder = (): SubscriptionBuilder => {{" - ); + writeln!(out, "subscriptionBuilder = (): SubscriptionBuilder => {{"); out.indent(1); writeln!(out, "return new SubscriptionBuilder(this);"); - + out.dedent(1); writeln!(out, "}};"); out.dedent(1); @@ -350,7 +330,7 @@ impl Lang for TypeScript { fn print_index_imports(out: &mut Indenter) { // All library imports are prefixed with `__` to avoid // clashing with the names of user generated types. - let mut types = [ + let mut types = [ "TypeBuilder as __TypeBuilder", "type AlgebraicTypeType as __AlgebraicTypeType", "DbConnectionBuilder as __DbConnectionBuilder", @@ -384,7 +364,7 @@ fn print_index_imports(out: &mut Indenter) { fn print_type_builder_imports(out: &mut Indenter) { // All library imports are prefixed with `__` to avoid // clashing with the names of user generated types. - let mut types = [ + let mut types = [ "TypeBuilder as __TypeBuilder", "type AlgebraicTypeType as __AlgebraicTypeType", "type Infer as __Infer", @@ -426,15 +406,11 @@ fn print_lint_suppression(output: &mut Indenter) { /// fooBar: __t.string(), /// }; /// ``` -fn define_body_for_reducer( - module: &ModuleDef, - out: &mut Indenter, - params: &[(Identifier, AlgebraicTypeUse)], -) { +fn define_body_for_reducer(module: &ModuleDef, out: &mut Indenter, params: &[(Identifier, AlgebraicTypeUse)]) { write!(out, "export default {{"); if params.is_empty() { writeln!(out, "}};"); - } else { + } else { writeln!(out); out.with_indent(|out| write_object_type_builder_fields(module, out, params, true).unwrap()); writeln!(out, "}};"); @@ -455,7 +431,6 @@ fn define_body_for_product( name: &str, elements: &[(Identifier, AlgebraicTypeUse)], ) { - write!(out, "export default __t.object(\"{name}\", {{"); if elements.is_empty() { writeln!(out, "}});"); @@ -470,11 +445,7 @@ fn define_body_for_product( fn write_table_opts(module: &ModuleDef, out: &mut Indenter, table: &TableDef) { let type_ref = table.product_type_ref; let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap(); - writeln!( - out, - "name: '{}',", - table.name.deref() - ); + writeln!(out, "name: '{}',", table.name.deref()); writeln!(out, "indexes: ["); out.indent(1); for index_def in iter_indexes(table) { @@ -533,12 +504,7 @@ fn write_object_type_builder_fields( Ok(()) } -fn write_type_builder_field( - module: &ModuleDef, - out: &mut Indenter, - name: &str, - ty: &AlgebraicTypeUse, -) -> fmt::Result { +fn write_type_builder_field(module: &ModuleDef, out: &mut Indenter, name: &str, ty: &AlgebraicTypeUse) -> fmt::Result { // Do we need a getter? (Option/Array only if their inner is a Ref) let needs_getter = match ty { AlgebraicTypeUse::Ref(_) => true, @@ -579,7 +545,7 @@ fn write_type_builder(module: &ModuleDef, out: &mut W, ty: &AlgebraicT write!(out, "__t.option(")?; write_type_builder(module, out, inner_ty)?; write!(out, ")")?; - }, + } AlgebraicTypeUse::Primitive(prim) => match prim { PrimitiveType::Bool => write!(out, "__t.bool()")?, PrimitiveType::I8 => write!(out, "__t.i8()")?, @@ -605,59 +571,14 @@ fn write_type_builder(module: &ModuleDef, out: &mut W, ty: &AlgebraicT write!(out, "__t.array(")?; write_type_builder(module, out, elem_ty)?; write!(out, ")")?; - }, + } AlgebraicTypeUse::Ref(r) => { write!(out, "{}", type_ref_name(module, *r))?; - }, + } } Ok(()) } -fn write_sum_variant_type(module: &ModuleDef, out: &mut Indenter, ident: &Identifier, ty: &AlgebraicTypeUse) { - let name = ident.deref().to_case(Case::Pascal); - write!(out, "export type {name} = "); - - // If the contained type is the unit type, i.e. this variant has no members, - // write only the tag. - // ``` - // { tag: "Foo" } - // ``` - write!(out, "{{ "); - write!(out, "tag: \"{name}\""); - - // If the contained type is not the unit type, write the tag and the value. - // ``` - // { tag: "Bar", value: Infer } - // { tag: "Bar", value: number } - // { tag: "Bar", value: string } - // ``` - // Note you could alternatively do: - // ``` - // { tag: "Bar" } & Infer - // ``` - // for non-primitive types but that doesn't extend to primitives. - // Another alternative would be to name the value field the same as the tag field, but lowercased - // ``` - // { tag: "Bar", bar: Infer } - // { tag: "Bar", bar: number } - // { tag: "Bar", bar: string } - // ``` - // but this is a departure from our previous convention and is not much different. - if !matches!(ty, AlgebraicTypeUse::Unit) { - write!(out, ", value: "); - write_type(module, out, ty, None, Some("Type")).unwrap(); - } - - writeln!(out, " }};"); -} - -fn write_variant_types(module: &ModuleDef, out: &mut Indenter, variants: &[(Identifier, AlgebraicTypeUse)]) { - // Write all the variant types. - for (ident, ty) in variants { - write_sum_variant_type(module, out, ident, ty); - } -} - /// e.g. /// ```ts /// // The tagged union or sum type for the algebraic type `Option`. @@ -694,34 +615,18 @@ fn type_module_name(type_name: &ScopedTypeName) -> String { collect_case(Case::Snake, type_name.name_segments()) + "_type" } -fn variants_module_name(type_name: &ScopedTypeName) -> String { - collect_case(Case::Snake, type_name.name_segments()) + "_variants" -} - fn table_module_name(table_name: &Identifier) -> String { table_name.deref().to_case(Case::Snake) + "_table" } -fn table_method_name(table_name: &Identifier) -> String { - table_name.deref().to_case(Case::Camel) -} - fn reducer_args_type_name(reducer_name: &Identifier) -> String { reducer_name.deref().to_case(Case::Pascal) } -fn reducer_variant_name(reducer_name: &Identifier) -> String { - reducer_name.deref().to_case(Case::Pascal) -} - fn reducer_module_name(reducer_name: &Identifier) -> String { reducer_name.deref().to_case(Case::Snake) + "_reducer" } -fn reducer_function_name(reducer: &ReducerDef) -> String { - reducer.name.deref().to_case(Case::Camel) -} - pub fn type_name(module: &ModuleDef, ty: &AlgebraicTypeUse) -> String { let mut s = String::new(); write_type(module, &mut s, ty, None, None).unwrap(); @@ -827,10 +732,7 @@ fn print_imports(module: &ModuleDef, out: &mut Indenter, imports: Imports, suffi let module_name = type_ref_module_name(module, typeref); let type_name = type_ref_name(module, typeref); if let Some(suffix) = suffix { - writeln!( - out, - "import {type_name}{suffix} from \"./{module_name}\";" - ); + writeln!(out, "import {type_name}{suffix} from \"./{module_name}\";"); } else { writeln!(out, "import {type_name} from \"./{module_name}\";"); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd66da9016c..5a173665ccb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,8 +57,8 @@ importers: specifier: ^3.3.3 version: 3.6.2 react: - specifier: ^18.0.0 || ^19.0.0-0 || ^19.0.0 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.2.0 undici: specifier: ^6.19.2 version: 6.21.3 @@ -204,6 +204,37 @@ 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) + crates/bindings-typescript/test-react-router-app: + dependencies: + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-router-dom: + specifier: ^7.9.4 + version: 7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + devDependencies: + '@types/react': + specifier: ^18.3.3 + version: 18.3.23 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.7(@types/react@18.3.23) + '@types/react-router-dom': + specifier: ^5.3.3 + version: 5.3.3 + '@vitejs/plugin-react': + specifier: ^4.3.1 + version: 4.7.0(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(terser@5.43.1)(tsx@4.20.4)) + typescript: + specifier: ^5.2.2 + version: 5.9.3 + vite: + 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) + docs: dependencies: '@docusaurus/core': @@ -211,7 +242,7 @@ importers: version: 3.9.1(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) '@docusaurus/plugin-content-docs': specifier: ^3.9.2 - version: 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) + version: 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) '@docusaurus/preset-classic': specifier: 3.9.1 version: 3.9.1(@algolia/client-search@5.39.0)(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(search-insights@2.17.3)(typescript@5.6.3) @@ -4522,6 +4553,10 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + copy-webpack-plugin@11.0.0: resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} engines: {node: '>= 14.15.0'} @@ -7511,11 +7546,28 @@ packages: peerDependencies: react: '>=15' + react-router-dom@7.9.5: + resolution: {integrity: sha512-mkEmq/K8tKN63Ae2M7Xgz3c9l9YNbY+NHH6NNeUmLA3kDkhKXRsNb/ZpxaEunvGo2/3YXdk5EJU3Hxp3ocaBPw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + react-router@5.3.4: resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==} peerDependencies: react: '>=15' + react-router@7.9.5: + resolution: {integrity: sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -7827,6 +7879,9 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -9131,10 +9186,10 @@ snapshots: '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) '@babel/helpers': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 convert-source-map: 2.0.0 debug: 4.4.3 gensync: 1.0.0-beta.2 @@ -9145,8 +9200,8 @@ snapshots: '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 @@ -9206,7 +9261,7 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.4 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color @@ -9215,7 +9270,7 @@ snapshots: '@babel/core': 7.28.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -9267,7 +9322,7 @@ snapshots: '@babel/helpers@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@babel/parser@7.28.3': dependencies: @@ -9862,8 +9917,8 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@babel/traverse@7.28.3': dependencies: @@ -10711,6 +10766,46 @@ snapshots: - webpack-cli '@docusaurus/plugin-content-docs@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3)': + dependencies: + '@docusaurus/core': 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) + '@docusaurus/logger': 3.9.2 + '@docusaurus/mdx-loader': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/module-type-aliases': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/types': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/utils': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/utils-common': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/utils-validation': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@types/react-router-config': 5.0.11 + combine-promises: 1.2.0 + fs-extra: 11.3.2 + js-yaml: 4.1.0 + lodash: 4.17.21 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + schema-dts: 1.1.5 + tslib: 2.8.1 + utility-types: 3.11.0 + webpack: 5.102.0 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-content-docs@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3)': dependencies: '@docusaurus/core': 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) '@docusaurus/logger': 3.9.2 @@ -11095,7 +11190,7 @@ snapshots: dependencies: '@docusaurus/mdx-loader': 3.9.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@docusaurus/module-type-aliases': 3.9.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@docusaurus/plugin-content-docs': 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) + '@docusaurus/plugin-content-docs': 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) '@docusaurus/utils': 3.9.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@docusaurus/utils-common': 3.9.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@types/history': 4.7.11 @@ -11115,7 +11210,7 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/theme-common@3.9.2(@docusaurus/plugin-content-docs@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@docusaurus/theme-common@3.9.2(@docusaurus/plugin-content-docs@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(debug@4.4.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@docusaurus/mdx-loader': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@docusaurus/module-type-aliases': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -11139,6 +11234,30 @@ snapshots: - uglify-js - webpack-cli + '@docusaurus/theme-common@3.9.2(@docusaurus/plugin-content-docs@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@docusaurus/mdx-loader': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/module-type-aliases': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/plugin-content-docs': 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.6.3) + '@docusaurus/utils': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@docusaurus/utils-common': 3.9.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@types/history': 4.7.11 + '@types/react': 18.3.23 + '@types/react-router-config': 5.0.11 + clsx: 2.1.1 + parse-numeric-range: 1.3.0 + prism-react-renderer: 2.4.1(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + tslib: 2.8.1 + utility-types: 3.11.0 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - supports-color + - uglify-js + - webpack-cli + '@docusaurus/theme-search-algolia@3.9.1(@algolia/client-search@5.39.0)(@mdx-js/react@3.1.1(@types/react@19.2.0)(react@19.2.0))(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(search-insights@2.17.3)(typescript@5.6.3)': dependencies: '@docsearch/react': 4.2.0(@algolia/client-search@5.39.0)(@types/react@19.2.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(search-insights@2.17.3) @@ -13175,24 +13294,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/body-parser@1.19.6': dependencies: @@ -15280,6 +15399,8 @@ snapshots: cookie@0.7.1: {} + cookie@1.0.2: {} + copy-webpack-plugin@11.0.0(webpack@5.102.0): dependencies: fast-glob: 3.3.3 @@ -18813,6 +18934,12 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 + react-router-dom@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router@5.3.4(react@19.2.0): dependencies: '@babel/runtime': 7.28.4 @@ -18826,6 +18953,14 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 + react-router@7.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.0.2 + react: 18.3.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-style-singleton@2.2.3(@types/react@19.2.0)(react@19.2.0): dependencies: get-nonce: 1.0.1 @@ -19262,6 +19397,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-cookie-parser@2.7.2: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 From d398d25d044a837e6e517713ef267b15453dfcaa Mon Sep 17 00:00:00 2001 From: = Date: Sun, 9 Nov 2025 16:26:12 -0500 Subject: [PATCH 20/23] pnpm format --- crates/bindings-typescript/src/lib/indexes.ts | 2 +- crates/bindings-typescript/src/lib/schema.ts | 7 ++++++- crates/bindings-typescript/src/lib/util.ts | 7 +++++-- crates/bindings-typescript/src/react/useReducer.ts | 10 ++++------ .../bindings-typescript/src/sdk/db_connection_impl.ts | 4 +--- crates/bindings-typescript/src/sdk/db_context.ts | 5 +---- crates/bindings-typescript/src/sdk/table_cache.ts | 9 ++------- crates/bindings-typescript/src/server/runtime.ts | 7 ++++++- .../test-react-router-app/src/pages/CounterPage.tsx | 4 +--- 9 files changed, 27 insertions(+), 28 deletions(-) diff --git a/crates/bindings-typescript/src/lib/indexes.ts b/crates/bindings-typescript/src/lib/indexes.ts index 9ac54cdd83c..75e0b0a0b8d 100644 --- a/crates/bindings-typescript/src/lib/indexes.ts +++ b/crates/bindings-typescript/src/lib/indexes.ts @@ -97,7 +97,7 @@ export interface UniqueIndex< > extends ReadonlyUniqueIndex { delete(colVal: IndexVal): boolean; update(colVal: RowType): RowType; -}; +} /** * A type representing a read-only ranged index on a database table. diff --git a/crates/bindings-typescript/src/lib/schema.ts b/crates/bindings-typescript/src/lib/schema.ts index d5bc6fdbe45..367a0018929 100644 --- a/crates/bindings-typescript/src/lib/schema.ts +++ b/crates/bindings-typescript/src/lib/schema.ts @@ -33,7 +33,12 @@ import type RawScopedTypeNameV9 from './autogen/raw_scoped_type_name_v_9_type'; import type { CamelCase } from './type_util'; import type { TableSchema } from './table_schema'; import { toCamelCase } from './util'; -import { defineView, type AnonymousViewFn, type ViewFn, type ViewReturnTypeBuilder } from './views'; +import { + defineView, + type AnonymousViewFn, + type ViewFn, + type ViewReturnTypeBuilder, +} from './views'; export type TableNamesOf = S['tables'][number]['name']; diff --git a/crates/bindings-typescript/src/lib/util.ts b/crates/bindings-typescript/src/lib/util.ts index 83822e6c760..c13a90b0e53 100644 --- a/crates/bindings-typescript/src/lib/util.ts +++ b/crates/bindings-typescript/src/lib/util.ts @@ -113,7 +113,10 @@ import type { AlgebraicType } from './algebraic_type'; import type Typespace from './autogen/typespace_type'; import type { Infer } from './type_builders'; -export function bsatnBaseSize(typespace: Infer, ty: AlgebraicType): number { +export function bsatnBaseSize( + typespace: Infer, + ty: AlgebraicType +): number { const assumedArrayLength = 4; while (ty.tag === 'Ref') ty = typespace.types[ty.value]; if (ty.tag === 'Product') { @@ -152,4 +155,4 @@ export function bsatnBaseSize(typespace: Infer, ty: AlgebraicT I256: 32, U256: 32, }[ty.tag]; -} \ No newline at end of file +} diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index dc93ae01b58..742f456ae31 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -7,9 +7,9 @@ import type { Prettify } from '../lib/type_util'; type IsEmptyObject = [keyof T] extends [never] ? true : false; type MaybeParams = IsEmptyObject extends true ? [] : [params: T]; -type ParamsType = MaybeParams ->>; +type ParamsType = MaybeParams< + Prettify> +>; export function useReducer( reducerDef: ReducerDef @@ -18,9 +18,7 @@ export function useReducer( const reducerName = reducerDef.accessorName; // Holds calls made before the connection exists - const queueRef = useRef< - ParamsType[] - >([]); + const queueRef = useRef[]>([]); // Flush when we finally have a connection useEffect(() => { diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index ddfaac3b0b1..1e250548738 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -28,9 +28,7 @@ import type { UnsubscribeAppliedMessage, } from './message_types.ts'; import type { ReducerEvent } from './reducer_event.ts'; -import { - type UntypedRemoteModule, -} from './spacetime_module.ts'; +import { type UntypedRemoteModule } from './spacetime_module.ts'; import { TableCache, type Operation, diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index 230170ac840..f34bf4a95ed 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,8 +1,5 @@ import type { ClientDbView } from './db_view'; -import type { - ReducersView, - SetReducerFlags, -} from './reducers'; +import type { ReducersView, SetReducerFlags } from './reducers'; import type { UntypedRemoteModule } from './spacetime_module'; import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index 2ff2a1ac194..abc4fb147d5 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -2,14 +2,9 @@ import { EventEmitter } from './event_emitter.ts'; import { stdbLogger } from './logger.ts'; import type { ComparablePrimitive } from '../'; -import type { - EventContextInterface, - TableDefForTableName, -} from './index.ts'; +import type { EventContextInterface, TableDefForTableName } from './index.ts'; import type { RowType, UntypedTableDef } from '../lib/table.ts'; -import type { - ClientTableCoreImplementable, -} from './client_table.ts'; +import type { ClientTableCoreImplementable } from './client_table.ts'; import type { UntypedRemoteModule } from './spacetime_module.ts'; import type { TableNamesOf } from '../lib/schema.ts'; diff --git a/crates/bindings-typescript/src/server/runtime.ts b/crates/bindings-typescript/src/server/runtime.ts index a6a02308d23..92b50768052 100644 --- a/crates/bindings-typescript/src/server/runtime.ts +++ b/crates/bindings-typescript/src/server/runtime.ts @@ -32,7 +32,12 @@ import type { DbView } from './db_view'; import { toCamelCase } from '../lib/util'; import type { Infer } from '../lib/type_builders'; import { bsatnBaseSize } from '../lib/util'; -import { ANON_VIEWS, VIEWS, type AnonymousViewCtx, type ViewCtx } from '../lib/views'; +import { + ANON_VIEWS, + VIEWS, + type AnonymousViewCtx, + type ViewCtx, +} from '../lib/views'; const { freeze } = Object; diff --git a/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx b/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx index d5787e39620..034ba900866 100644 --- a/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx +++ b/crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx @@ -18,9 +18,7 @@ export default function CounterPage() {

Click above to increment the count, click below to clear the count.

- + ); From c61fae717081900187abc1ad39ac841af540346c Mon Sep 17 00:00:00 2001 From: = Date: Sun, 9 Nov 2025 16:26:27 -0500 Subject: [PATCH 21/23] cargo fmt --- crates/codegen/src/typescript.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index ba1872a7be5..c3aa847b7e4 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -99,7 +99,12 @@ impl Lang for TypeScript { /// location: pointType, /// })) /// ``` - fn generate_table_file_from_schema(&self, module: &ModuleDef, table: &TableDef, _schema: TableSchema) -> OutputFile { + fn generate_table_file_from_schema( + &self, + module: &ModuleDef, + table: &TableDef, + _schema: TableSchema, + ) -> OutputFile { let mut output = CodeIndenter::new(String::new(), INDENT); let out = &mut output; From 64b3404f9829fa9825cb1b460c912621d493a8d4 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 9 Nov 2025 16:28:38 -0500 Subject: [PATCH 22/23] misconfigured import --- crates/bindings-typescript/src/server/runtime.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/bindings-typescript/src/server/runtime.ts b/crates/bindings-typescript/src/server/runtime.ts index 92b50768052..a6a02308d23 100644 --- a/crates/bindings-typescript/src/server/runtime.ts +++ b/crates/bindings-typescript/src/server/runtime.ts @@ -32,12 +32,7 @@ import type { DbView } from './db_view'; import { toCamelCase } from '../lib/util'; import type { Infer } from '../lib/type_builders'; import { bsatnBaseSize } from '../lib/util'; -import { - ANON_VIEWS, - VIEWS, - type AnonymousViewCtx, - type ViewCtx, -} from '../lib/views'; +import { ANON_VIEWS, VIEWS, type AnonymousViewCtx, type ViewCtx } from '../lib/views'; const { freeze } = Object; From 31243a71da6b5b921f89507411e20fa9f2e575ee Mon Sep 17 00:00:00 2001 From: = Date: Sun, 9 Nov 2025 16:29:52 -0500 Subject: [PATCH 23/23] Fixed broken type tests --- crates/bindings-typescript/src/server/schema.test-d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bindings-typescript/src/server/schema.test-d.ts b/crates/bindings-typescript/src/server/schema.test-d.ts index 8ffc6f50656..c1846ca1716 100644 --- a/crates/bindings-typescript/src/server/schema.test-d.ts +++ b/crates/bindings-typescript/src/server/schema.test-d.ts @@ -1,6 +1,6 @@ -import { schema } from './schema'; -import { table } from './table'; -import t from './type_builders'; +import { schema } from '../lib/schema'; +import { table } from '../lib/table'; +import t from '../lib/type_builders'; const person = table( {