diff --git a/Cargo.toml b/Cargo.toml index 3d13bc7ac40..ba53dcce679 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -288,7 +288,7 @@ rand_xoshiro = "0.7" rayon = "1.8" rayon-core = "1.11.0" regex = "1" -reqwest = { version = "0.12", features = ["stream", "json", "gzip", "brotli"] } +reqwest = { version = "0.12", features = ["stream", "json", "gzip", "brotli", "native-tls-alpn"] } rolldown = { git = "https://github.com/rolldown/rolldown.git", tag = "v1.0.0-rc.3" } rolldown_common = { git = "https://github.com/rolldown/rolldown.git", tag = "v1.0.0-rc.3" } rolldown_error = { git = "https://github.com/rolldown/rolldown.git", tag = "v1.0.0-rc.3" } diff --git a/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts b/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts index 8fbf73d58d4..82ddf0cb52f 100644 --- a/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts +++ b/crates/bindings-typescript/case-conversion-test-client/src/module_bindings/index.ts @@ -229,10 +229,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< /** Builder class to configure a new database connection to the remote SpacetimeDB instance. */ export class DbConnectionBuilder extends __DbConnectionBuilder {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/crates/bindings-typescript/src/angular/connection_state.ts b/crates/bindings-typescript/src/angular/connection_state.ts index e57f2524e72..3b9aa624ffd 100644 --- a/crates/bindings-typescript/src/angular/connection_state.ts +++ b/crates/bindings-typescript/src/angular/connection_state.ts @@ -1,7 +1,7 @@ import { InjectionToken, type WritableSignal } from '@angular/core'; -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.ts'; +import type { Identity } from '../lib/identity.ts'; +import type { DbConnectionImpl } from '../sdk/db_connection_impl.ts'; export interface ConnectionState { isActive: boolean; diff --git a/crates/bindings-typescript/src/angular/injectors/inject-reducer.ts b/crates/bindings-typescript/src/angular/injectors/inject-reducer.ts index 7afa0f9367e..ffb5961b287 100644 --- a/crates/bindings-typescript/src/angular/injectors/inject-reducer.ts +++ b/crates/bindings-typescript/src/angular/injectors/inject-reducer.ts @@ -1,7 +1,7 @@ import { assertInInjectionContext, inject, effect } from '@angular/core'; -import { SPACETIMEDB_CONNECTION } from '../connection_state'; -import type { ParamsType } from '../../sdk'; -import type { UntypedReducerDef } from '../../sdk/reducers'; +import { SPACETIMEDB_CONNECTION } from '../connection_state.ts'; +import type { ParamsType } from '../../sdk/index.ts'; +import type { UntypedReducerDef } from '../../sdk/reducers.ts'; export function injectReducer( reducerDef: ReducerDef diff --git a/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb-connected.ts b/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb-connected.ts index 6cbfa53f80d..f41a0c1ea15 100644 --- a/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb-connected.ts +++ b/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb-connected.ts @@ -4,7 +4,7 @@ import { computed, type Signal, } from '@angular/core'; -import { SPACETIMEDB_CONNECTION } from '../connection_state'; +import { SPACETIMEDB_CONNECTION } from '../connection_state.ts'; export function injectSpacetimeDBConnected(): Signal { assertInInjectionContext(injectSpacetimeDBConnected); diff --git a/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb.ts b/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb.ts index c8dfa689cef..ba5a4b821ce 100644 --- a/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb.ts +++ b/crates/bindings-typescript/src/angular/injectors/inject-spacetimedb.ts @@ -2,7 +2,7 @@ import { assertInInjectionContext, inject, type Signal } from '@angular/core'; import { SPACETIMEDB_CONNECTION, type ConnectionState, -} from '../connection_state'; +} from '../connection_state.ts'; export function injectSpacetimeDB(): Signal { assertInInjectionContext(injectSpacetimeDB); diff --git a/crates/bindings-typescript/src/angular/injectors/inject-table.ts b/crates/bindings-typescript/src/angular/injectors/inject-table.ts index ea46cde33fc..652a3422b9b 100644 --- a/crates/bindings-typescript/src/angular/injectors/inject-table.ts +++ b/crates/bindings-typescript/src/angular/injectors/inject-table.ts @@ -5,9 +5,9 @@ import { effect, type Signal, } from '@angular/core'; -import type { RowType, UntypedTableDef } from '../../lib/table'; -import type { Prettify } from '../../lib/type_util'; -import { SPACETIMEDB_CONNECTION } from '../connection_state'; +import type { RowType, UntypedTableDef } from '../../lib/table.ts'; +import type { Prettify } from '../../lib/type_util.ts'; +import { SPACETIMEDB_CONNECTION } from '../connection_state.ts'; import { type Query, type BooleanExpr, @@ -15,9 +15,9 @@ import { evaluateBooleanExpr, getQueryAccessorName, getQueryWhereClause, -} from '../../lib/query'; -import type { EventContextInterface } from '../../sdk'; -import type { UntypedRemoteModule } from '../../sdk/spacetime_module'; +} from '../../lib/query.ts'; +import type { EventContextInterface } from '../../sdk/index.ts'; +import type { UntypedRemoteModule } from '../../sdk/spacetime_module.ts'; export type RowTypeDef = Prettify< RowType diff --git a/crates/bindings-typescript/src/angular/providers/provide-spacetimedb.ts b/crates/bindings-typescript/src/angular/providers/provide-spacetimedb.ts index c847a44d4d7..40a61971fec 100644 --- a/crates/bindings-typescript/src/angular/providers/provide-spacetimedb.ts +++ b/crates/bindings-typescript/src/angular/providers/provide-spacetimedb.ts @@ -9,12 +9,12 @@ import type { DbConnectionImpl, ErrorContextInterface, RemoteModuleOf, -} from '../../sdk/db_connection_impl'; +} from '../../sdk/db_connection_impl.ts'; import { SPACETIMEDB_CONNECTION, type ConnectionState, -} from '../connection_state'; -import { ConnectionId } from '../../lib/connection_id'; +} from '../connection_state.ts'; +import { ConnectionId } from '../../lib/connection_id.ts'; let connRef: DbConnectionImpl | null = null; diff --git a/crates/bindings-typescript/src/index.ts b/crates/bindings-typescript/src/index.ts index 41674612e40..a83646301b1 100644 --- a/crates/bindings-typescript/src/index.ts +++ b/crates/bindings-typescript/src/index.ts @@ -1,16 +1,16 @@ -export * from './lib/connection_id'; -export * from './lib/errors'; -export * from './lib/algebraic_type'; -export * from './lib/algebraic_value'; -export { default as BinaryReader } from './lib/binary_reader'; -export { default as BinaryWriter } from './lib/binary_writer'; -export * from './lib/schedule_at'; -export * from './lib/time_duration'; -export * from './lib/timestamp'; -export * from './lib/uuid'; -export * from './lib/util'; -export * from './lib/identity'; -export * from './lib/option'; -export * from './lib/result'; -export * from './lib/query'; -export * from './sdk'; +export * from './lib/connection_id.ts'; +export * from './lib/errors.ts'; +export * from './lib/algebraic_type.ts'; +export * from './lib/algebraic_value.ts'; +export { default as BinaryReader } from './lib/binary_reader.ts'; +export { default as BinaryWriter } from './lib/binary_writer.ts'; +export * from './lib/schedule_at.ts'; +export * from './lib/time_duration.ts'; +export * from './lib/timestamp.ts'; +export * from './lib/uuid.ts'; +export * from './lib/util.ts'; +export * from './lib/identity.ts'; +export * from './lib/option.ts'; +export * from './lib/result.ts'; +export * from './lib/query.ts'; +export * from './sdk/index.ts'; diff --git a/crates/bindings-typescript/src/lib/algebraic_type.ts b/crates/bindings-typescript/src/lib/algebraic_type.ts index c325797af75..57098961fb3 100644 --- a/crates/bindings-typescript/src/lib/algebraic_type.ts +++ b/crates/bindings-typescript/src/lib/algebraic_type.ts @@ -1,12 +1,12 @@ -import { TimeDuration } from './time_duration'; -import { Timestamp } from './timestamp'; -import { Uuid } from './uuid'; -import { ConnectionId } from './connection_id'; -import BinaryReader from './binary_reader'; -import BinaryWriter from './binary_writer'; -import { Identity } from './identity'; -import * as AlgebraicTypeVariants from './algebraic_type_variants'; -import { hasOwn } from './util'; +import { TimeDuration } from './time_duration.ts'; +import { Timestamp } from './timestamp.ts'; +import { Uuid } from './uuid.ts'; +import { ConnectionId } from './connection_id.ts'; +import BinaryReader from './binary_reader.ts'; +import BinaryWriter from './binary_writer.ts'; +import { Identity } from './identity.ts'; +import * as AlgebraicTypeVariants from './algebraic_type_variants.ts'; +import { hasOwn } from './util.ts'; type TypespaceType = { types: AlgebraicTypeType[]; diff --git a/crates/bindings-typescript/src/lib/algebraic_type_variants.ts b/crates/bindings-typescript/src/lib/algebraic_type_variants.ts index 5890d53c148..d68c883d2c1 100644 --- a/crates/bindings-typescript/src/lib/algebraic_type_variants.ts +++ b/crates/bindings-typescript/src/lib/algebraic_type_variants.ts @@ -2,7 +2,7 @@ import type { AlgebraicTypeType, ProductTypeType, SumTypeType, -} from './algebraic_type'; +} from './algebraic_type.ts'; export type Ref = { tag: 'Ref'; value: number }; export type Sum = { tag: 'Sum'; value: SumTypeType }; diff --git a/crates/bindings-typescript/src/lib/algebraic_value.ts b/crates/bindings-typescript/src/lib/algebraic_value.ts index 558f48a2a01..e2122d00587 100644 --- a/crates/bindings-typescript/src/lib/algebraic_value.ts +++ b/crates/bindings-typescript/src/lib/algebraic_value.ts @@ -1,4 +1,4 @@ -import BinaryReader from './binary_reader'; +import BinaryReader from './binary_reader.ts'; export interface ParseableType { deserialize: (reader: BinaryReader) => T; diff --git a/crates/bindings-typescript/src/lib/autogen/types.ts b/crates/bindings-typescript/src/lib/autogen/types.ts index c27e9a43260..46a2a1f4261 100644 --- a/crates/bindings-typescript/src/lib/autogen/types.ts +++ b/crates/bindings-typescript/src/lib/autogen/types.ts @@ -8,7 +8,7 @@ import { t as __t, type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, -} from '../../lib/type_builders'; +} from '../../lib/type_builders.ts'; // The tagged union or sum type for the algebraic type `AlgebraicType`. export const AlgebraicType: __TypeBuilder< diff --git a/crates/bindings-typescript/src/lib/connection_id.ts b/crates/bindings-typescript/src/lib/connection_id.ts index a57e6bba1a6..862f87465de 100644 --- a/crates/bindings-typescript/src/lib/connection_id.ts +++ b/crates/bindings-typescript/src/lib/connection_id.ts @@ -1,10 +1,10 @@ -import { AlgebraicType } from './algebraic_type'; +import { AlgebraicType } from './algebraic_type.ts'; import { coerceToBigInt, hexStringToU128, u128ToHexString, u128ToUint8Array, -} from './util'; +} from './util.ts'; export type ConnectionIdAlgebraicType = { tag: 'Product'; diff --git a/crates/bindings-typescript/src/lib/constraints.ts b/crates/bindings-typescript/src/lib/constraints.ts index f779b49d309..eccace200f2 100644 --- a/crates/bindings-typescript/src/lib/constraints.ts +++ b/crates/bindings-typescript/src/lib/constraints.ts @@ -1,5 +1,5 @@ -import type { table, UntypedTableDef } from './table'; -import type { ColumnMetadata } from './type_builders'; +import type { table, UntypedTableDef } from './table.ts'; +import type { ColumnMetadata } from './type_builders.ts'; /** * A helper type to determine if all columns in an index are unique. @@ -44,5 +44,5 @@ export type ColumnIsUnique> = M extends * of constraint definitions. */ export type ConstraintOpts = { - name?: string; + name?: string | undefined; } & { constraint: 'unique'; columns: [AllowedCol] }; diff --git a/crates/bindings-typescript/src/lib/environment.ts b/crates/bindings-typescript/src/lib/environment.ts index d065d5bb0aa..c246c3a1a8b 100644 --- a/crates/bindings-typescript/src/lib/environment.ts +++ b/crates/bindings-typescript/src/lib/environment.ts @@ -3,7 +3,7 @@ import type { StringBuilder, TypeBuilder, t, -} from './type_builders'; +} from './type_builders.ts'; /** Only strings and simple, payload-free enums constrain environment strings. */ export type EnvironmentString = diff --git a/crates/bindings-typescript/src/lib/filter.ts b/crates/bindings-typescript/src/lib/filter.ts index 869f36d239d..d9dde9de43f 100644 --- a/crates/bindings-typescript/src/lib/filter.ts +++ b/crates/bindings-typescript/src/lib/filter.ts @@ -1,6 +1,6 @@ -import type { RowType, UntypedTableDef } from './table'; -import { Timestamp } from './timestamp'; -import { Uuid } from './uuid'; +import type { RowType, UntypedTableDef } from './table.ts'; +import { Timestamp } from './timestamp.ts'; +import { Uuid } from './uuid.ts'; export type Value = string | number | boolean | Uuid | Timestamp; diff --git a/crates/bindings-typescript/src/lib/identity.ts b/crates/bindings-typescript/src/lib/identity.ts index a1ada196fd9..640bee22f39 100644 --- a/crates/bindings-typescript/src/lib/identity.ts +++ b/crates/bindings-typescript/src/lib/identity.ts @@ -1,10 +1,10 @@ -import { AlgebraicType } from './algebraic_type'; +import { AlgebraicType } from './algebraic_type.ts'; import { coerceToBigInt, hexStringToU256, u256ToHexString, u256ToUint8Array, -} from './util'; +} from './util.ts'; export type IdentityAlgebraicType = { tag: 'Product'; diff --git a/crates/bindings-typescript/src/lib/indexes.ts b/crates/bindings-typescript/src/lib/indexes.ts index 0b507ff3bae..5bd1a164025 100644 --- a/crates/bindings-typescript/src/lib/indexes.ts +++ b/crates/bindings-typescript/src/lib/indexes.ts @@ -1,8 +1,8 @@ -import type { RowType, table, UntypedTableDef } from './table'; -import type { ColumnMetadata, IndexTypes } from './type_builders'; -import type { CollapseTuple, Prettify } from './type_util'; -import { Range } from '../server/range'; -import type { ColumnIsUnique } from './constraints'; +import type { RowType, table, UntypedTableDef } from './table.ts'; +import type { ColumnMetadata, IndexTypes } from './type_builders.ts'; +import type { CollapseTuple, Prettify } from './type_util.ts'; +import { Range } from '../server/range.ts'; +import type { ColumnIsUnique } from './constraints.ts'; /** * Index helper type used *inside* {@link table} to enforce that only @@ -10,7 +10,7 @@ import type { ColumnIsUnique } from './constraints'; */ export type IndexOpts = { accessor: string; - name?: string; + name?: string | undefined; } & ( | { algorithm: 'btree'; columns: readonly AllowedCol[] } | { algorithm: 'hash'; columns: readonly AllowedCol[] } diff --git a/crates/bindings-typescript/src/lib/option.ts b/crates/bindings-typescript/src/lib/option.ts index 40e11d2a9b2..bae7def2949 100644 --- a/crates/bindings-typescript/src/lib/option.ts +++ b/crates/bindings-typescript/src/lib/option.ts @@ -1,4 +1,4 @@ -import { AlgebraicType } from './algebraic_type'; +import { AlgebraicType } from './algebraic_type.ts'; export type OptionAlgebraicType = { tag: 'Sum'; diff --git a/crates/bindings-typescript/src/lib/query.ts b/crates/bindings-typescript/src/lib/query.ts index bb93b0e6ce3..2ddaa8a702e 100644 --- a/crates/bindings-typescript/src/lib/query.ts +++ b/crates/bindings-typescript/src/lib/query.ts @@ -1,19 +1,19 @@ -import { ConnectionId } from './connection_id'; -import { Identity } from './identity'; -import type { ColumnIndex, IndexColumns, IndexOpts } from './indexes'; -import type { UntypedSchemaDef } from './schema'; -import type { UntypedTableDef } from './table'; -import type { UntypedTableSchema } from './table_schema'; -import { Timestamp } from './timestamp'; +import { ConnectionId } from './connection_id.ts'; +import { Identity } from './identity.ts'; +import type { ColumnIndex, IndexColumns, IndexOpts } from './indexes.ts'; +import type { UntypedSchemaDef } from './schema.ts'; +import type { UntypedTableDef } from './table.ts'; +import type { UntypedTableSchema } from './table_schema.ts'; +import { Timestamp } from './timestamp.ts'; import type { ColumnBuilder, ColumnMetadata, RowBuilder, TypeBuilder, -} from './type_builders'; -import type { Values } from './type_util'; -import type { Bool as SatsBool } from './algebraic_type_variants'; -import { Uuid } from './uuid'; +} from './type_builders.ts'; +import type { Values } from './type_util.ts'; +import type { Bool as SatsBool } from './algebraic_type_variants.ts'; +import { Uuid } from './uuid.ts'; /** * Helper to get the set of table names. diff --git a/crates/bindings-typescript/src/lib/reducer_schema.ts b/crates/bindings-typescript/src/lib/reducer_schema.ts index 810aa810860..2b4628e9ffe 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/types'; -import type { ParamsObj } from './reducers'; -import type { RowBuilder, RowObj } from './type_builders'; -import type { CamelCase } from './type_util'; +import type { ProductType } from './algebraic_type.ts'; +import type { RawReducerDefV9 } from './autogen/types.ts'; +import type { ParamsObj } from './reducers.ts'; +import type { RowBuilder, RowObj } from './type_builders.ts'; +import type { CamelCase } from './type_util.ts'; /** * Represents a handle to a database reducer, including its name and argument type. diff --git a/crates/bindings-typescript/src/lib/reducers.ts b/crates/bindings-typescript/src/lib/reducers.ts index f2d470152ec..e7068c166a0 100644 --- a/crates/bindings-typescript/src/lib/reducers.ts +++ b/crates/bindings-typescript/src/lib/reducers.ts @@ -1,15 +1,15 @@ -import type { EnvironmentFor } from './environment'; -import type { DbView } from '../server/db_view'; -import type { Random } from '../server/rng'; -import type { ConnectionId } from './connection_id'; -import type { Identity } from './identity'; -import { type UntypedSchemaDef } from './schema'; -import { type Timestamp } from './timestamp'; +import type { EnvironmentFor } from './environment.ts'; +import type { DbView } from '../server/db_view.ts'; +import type { Random } from '../server/rng.ts'; +import type { ConnectionId } from './connection_id.ts'; +import type { Identity } from './identity.ts'; +import { type UntypedSchemaDef } from './schema.ts'; +import { type Timestamp } from './timestamp.ts'; import { ColumnBuilder, type InferTypeOfRow, type TypeBuilder, -} from './type_builders'; +} from './type_builders.ts'; import { Uuid } from './uuid.ts'; /** diff --git a/crates/bindings-typescript/src/lib/result.ts b/crates/bindings-typescript/src/lib/result.ts index 5ea0d682048..366a72e2962 100644 --- a/crates/bindings-typescript/src/lib/result.ts +++ b/crates/bindings-typescript/src/lib/result.ts @@ -1,4 +1,4 @@ -import { AlgebraicType } from './algebraic_type'; +import { AlgebraicType } from './algebraic_type.ts'; export type ResultAlgebraicType< T extends AlgebraicType = AlgebraicType, diff --git a/crates/bindings-typescript/src/lib/schedule_at.ts b/crates/bindings-typescript/src/lib/schedule_at.ts index 61d263b8a35..03bdd87ae6f 100644 --- a/crates/bindings-typescript/src/lib/schedule_at.ts +++ b/crates/bindings-typescript/src/lib/schedule_at.ts @@ -1,6 +1,9 @@ -import { AlgebraicType } from './algebraic_type'; -import { TimeDuration, type TimeDurationAlgebraicType } from './time_duration'; -import { Timestamp, type TimestampAlgebraicType } from './timestamp'; +import { AlgebraicType } from './algebraic_type.ts'; +import { + TimeDuration, + type TimeDurationAlgebraicType, +} from './time_duration.ts'; +import { Timestamp, type TimestampAlgebraicType } from './timestamp.ts'; export type ScheduleAtAlgebraicType = { tag: 'Sum'; diff --git a/crates/bindings-typescript/src/lib/schema.ts b/crates/bindings-typescript/src/lib/schema.ts index 67b263d8397..4ed45bc7f48 100644 --- a/crates/bindings-typescript/src/lib/schema.ts +++ b/crates/bindings-typescript/src/lib/schema.ts @@ -4,7 +4,7 @@ import { SumType, type AlgebraicTypeType, type AlgebraicTypeVariants, -} from './algebraic_type'; +} from './algebraic_type.ts'; import type { CaseConversionPolicy, RawSubmoduleV10, @@ -12,10 +12,10 @@ import type { RawModuleDefV10Section, RawScopedTypeNameV10, RawTableDefV10, -} from './autogen/types'; -import type { UntypedIndex } from './indexes'; -import type { UntypedTableDef } from './table'; -import type { UntypedTableSchema } from './table_schema'; +} from './autogen/types.ts'; +import type { UntypedIndex } from './indexes.ts'; +import type { UntypedTableDef } from './table.ts'; +import type { UntypedTableSchema } from './table_schema.ts'; import { ArrayBuilder, OptionBuilder, @@ -31,8 +31,8 @@ import { type InferSpacetimeTypeOfTypeBuilder, type RowObj, type VariantsObj, -} from './type_builders'; -import type { Values } from './type_util'; +} from './type_builders.ts'; +import type { Values } from './type_util.ts'; export type TableNamesOf = Values< S['tables'] diff --git a/crates/bindings-typescript/src/lib/table.ts b/crates/bindings-typescript/src/lib/table.ts index cb15705989f..e153114a7d1 100644 --- a/crates/bindings-typescript/src/lib/table.ts +++ b/crates/bindings-typescript/src/lib/table.ts @@ -1,5 +1,5 @@ -import type { ProcedureExport, ReducerExport, t } from '../server'; -import type { errors } from '../server/errors'; +import type { ProcedureExport, ReducerExport, t } from '../server/index.ts'; +import type { errors } from '../server/errors.ts'; import { ExplicitNameEntry, RawColumnDefaultValueV10, @@ -8,9 +8,9 @@ import { RawIndexDefV10, RawSequenceDefV10, RawTableDefV10, -} from './autogen/types'; -import BinaryWriter from './binary_writer'; -import type { AllUnique, ConstraintOpts } from './constraints'; +} from './autogen/types.ts'; +import BinaryWriter from './binary_writer.ts'; +import type { AllUnique, ConstraintOpts } from './constraints.ts'; import type { ColumnIndex, IndexColumns, @@ -18,9 +18,9 @@ import type { IndexOpts, ReadonlyIndexes, UntypedIndex, -} from './indexes'; -import ScheduleAt from './schedule_at'; -import type { TableSchema, TableSchedule } from './table_schema'; +} from './indexes.ts'; +import ScheduleAt from './schedule_at.ts'; +import type { TableSchema, TableSchedule } from './table_schema.ts'; import { RowBuilder, type ColumnBuilder, @@ -28,13 +28,13 @@ import { type InferTypeOfRow, type RowObj, type TypeBuilder, -} from './type_builders'; +} from './type_builders.ts'; import type { InvalidColumnMetadata, Prettify, ValidateColumnMetadata, -} from './type_util'; -import { toPascalCase } from './util'; +} from './type_util.ts'; +import { toPascalCase } from './util.ts'; export type AlgebraicTypeRef = number; type ColId = number; diff --git a/crates/bindings-typescript/src/lib/table_schema.ts b/crates/bindings-typescript/src/lib/table_schema.ts index f66a3eba226..85cd2f1eb85 100644 --- a/crates/bindings-typescript/src/lib/table_schema.ts +++ b/crates/bindings-typescript/src/lib/table_schema.ts @@ -1,10 +1,10 @@ -import type { ProductType } from './algebraic_type'; -import type { RawScheduleDefV10, RawTableDefV10 } from './autogen/types'; -import type { IndexOpts } from './indexes'; -import type { ModuleContext } from './schema'; -import type { ColumnBuilder, RowBuilder } from './type_builders'; -import type { HasExactlyOneKnownKey } from './type_util'; -import type { ProcedureExport, ReducerExport } from '../server'; +import type { ProductType } from './algebraic_type.ts'; +import type { RawScheduleDefV10, RawTableDefV10 } from './autogen/types.ts'; +import type { IndexOpts } from './indexes.ts'; +import type { ModuleContext } from './schema.ts'; +import type { ColumnBuilder, RowBuilder } from './type_builders.ts'; +import type { HasExactlyOneKnownKey } from './type_util.ts'; +import type { ProcedureExport, ReducerExport } from '../server/index.ts'; /** * Internal erased form of a scheduled reducer/procedure export. diff --git a/crates/bindings-typescript/src/lib/time_duration.ts b/crates/bindings-typescript/src/lib/time_duration.ts index 26830e7b830..eef182ab5c1 100644 --- a/crates/bindings-typescript/src/lib/time_duration.ts +++ b/crates/bindings-typescript/src/lib/time_duration.ts @@ -1,5 +1,5 @@ -import { AlgebraicType } from './algebraic_type'; -import { coerceToBigInt } from './util'; +import { AlgebraicType } from './algebraic_type.ts'; +import { coerceToBigInt } from './util.ts'; export type TimeDurationAlgebraicType = { tag: 'Product'; diff --git a/crates/bindings-typescript/src/lib/timestamp.ts b/crates/bindings-typescript/src/lib/timestamp.ts index 918f03b3d6b..d3fcac304b2 100644 --- a/crates/bindings-typescript/src/lib/timestamp.ts +++ b/crates/bindings-typescript/src/lib/timestamp.ts @@ -1,6 +1,6 @@ -import { AlgebraicType } from './algebraic_type'; -import { TimeDuration } from './time_duration'; -import { coerceToBigInt } from './util'; +import { AlgebraicType } from './algebraic_type.ts'; +import { TimeDuration } from './time_duration.ts'; +import { coerceToBigInt } from './util.ts'; export type TimestampAlgebraicType = { tag: 'Product'; diff --git a/crates/bindings-typescript/src/lib/type_builders.test-d.ts b/crates/bindings-typescript/src/lib/type_builders.test-d.ts index bae3089e145..3e59256b2f9 100644 --- a/crates/bindings-typescript/src/lib/type_builders.test-d.ts +++ b/crates/bindings-typescript/src/lib/type_builders.test-d.ts @@ -1,5 +1,5 @@ -import { t } from '../server'; -import { type AlgebraicTypeVariants } from '..'; +import { t } from '../server/index.ts'; +import { type AlgebraicTypeVariants } from '../index.ts'; import type { I32ColumnBuilder, I64ColumnBuilder, @@ -7,7 +7,7 @@ import type { InferTypeOfTypeBuilder, TypeBuilder, U8ColumnBuilder, -} from './type_builders'; +} from './type_builders.ts'; type MustBeNever = [T] extends [never] ? true diff --git a/crates/bindings-typescript/src/lib/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts index 41d2da11015..26572a9b040 100644 --- a/crates/bindings-typescript/src/lib/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -1,19 +1,25 @@ -import { AlgebraicType, type AlgebraicTypeVariants } from './algebraic_type'; -import type BinaryReader from './binary_reader'; -import type BinaryWriter from './binary_writer'; -import { ConnectionId, type ConnectionIdAlgebraicType } from './connection_id'; -import { Identity, type IdentityAlgebraicType } from './identity'; -import { Option, type OptionAlgebraicType } from './option'; -import { Result, type ResultAlgebraicType } from './result'; -import ScheduleAt, { type ScheduleAtAlgebraicType } from './schedule_at'; -import type { CoerceRow } from './table'; -import { TimeDuration, type TimeDurationAlgebraicType } from './time_duration'; -import { Timestamp, type TimestampAlgebraicType } from './timestamp'; -import { set, type Prettify, type SetField } from './type_util'; -import { Uuid, type UuidAlgebraicType } from './uuid'; +import { AlgebraicType, type AlgebraicTypeVariants } from './algebraic_type.ts'; +import type BinaryReader from './binary_reader.ts'; +import type BinaryWriter from './binary_writer.ts'; +import { + ConnectionId, + type ConnectionIdAlgebraicType, +} from './connection_id.ts'; +import { Identity, type IdentityAlgebraicType } from './identity.ts'; +import { Option, type OptionAlgebraicType } from './option.ts'; +import { Result, type ResultAlgebraicType } from './result.ts'; +import ScheduleAt, { type ScheduleAtAlgebraicType } from './schedule_at.ts'; +import type { CoerceRow } from './table.ts'; +import { + TimeDuration, + type TimeDurationAlgebraicType, +} from './time_duration.ts'; +import { Timestamp, type TimestampAlgebraicType } from './timestamp.ts'; +import { set, type Prettify, type SetField } from './type_util.ts'; +import { Uuid, type UuidAlgebraicType } from './uuid.ts'; // Used in codegen files -export { type AlgebraicTypeType } from './algebraic_type'; +export { type AlgebraicTypeType } from './algebraic_type.ts'; /** * Helper type to extract the TypeScript type from a TypeBuilder diff --git a/crates/bindings-typescript/src/lib/type_util.ts b/crates/bindings-typescript/src/lib/type_util.ts index 942b76a1424..337317f1134 100644 --- a/crates/bindings-typescript/src/lib/type_util.ts +++ b/crates/bindings-typescript/src/lib/type_util.ts @@ -1,8 +1,8 @@ -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'; +import type { ConnectionId } from './connection_id.ts'; +import type { Identity } from './identity.ts'; +import type { ScheduleAt } from './schedule_at.ts'; +import type { TimeDuration } from './time_duration.ts'; +import type { Timestamp } from './timestamp.ts'; type DoNotPrettify = | Identity diff --git a/crates/bindings-typescript/src/lib/util.ts b/crates/bindings-typescript/src/lib/util.ts index 143d54bdfe0..489a64d5379 100644 --- a/crates/bindings-typescript/src/lib/util.ts +++ b/crates/bindings-typescript/src/lib/util.ts @@ -1,10 +1,10 @@ -import type { AlgebraicType } from './algebraic_type'; -import type { Typespace } from './autogen/types'; -import BinaryReader from './binary_reader'; -import BinaryWriter from './binary_writer'; -import type { ParamsObj } from './reducers'; -import type { ColumnBuilder, TypeBuilder } from './type_builders'; -import type { CamelCase, SnakeCase } from './type_util'; +import type { AlgebraicType } from './algebraic_type.ts'; +import type { Typespace } from './autogen/types.ts'; +import BinaryReader from './binary_reader.ts'; +import BinaryWriter from './binary_writer.ts'; +import type { ParamsObj } from './reducers.ts'; +import type { ColumnBuilder, TypeBuilder } from './type_builders.ts'; +import type { CamelCase, SnakeCase } from './type_util.ts'; export function deepEqual(obj1: any, obj2: any): boolean { // If both are strictly equal (covers primitives and reference equality), return true diff --git a/crates/bindings-typescript/src/lib/uuid.ts b/crates/bindings-typescript/src/lib/uuid.ts index 0d5d2fe0967..5dd2a22817e 100644 --- a/crates/bindings-typescript/src/lib/uuid.ts +++ b/crates/bindings-typescript/src/lib/uuid.ts @@ -1,6 +1,6 @@ -import { Timestamp } from './timestamp'; +import { Timestamp } from './timestamp.ts'; import { AlgebraicType } from './algebraic_type.ts'; -import { coerceToBigInt, u128ToHexString } from './util'; +import { coerceToBigInt, u128ToHexString } from './util.ts'; export type UuidAlgebraicType = { tag: 'Product'; diff --git a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts index a574e21d4a8..96c1c61dd5f 100644 --- a/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/react/SpacetimeDBProvider.ts @@ -1,15 +1,15 @@ import { DbConnectionBuilder, type DbConnectionImpl, -} from '../sdk/db_connection_impl'; +} from '../sdk/db_connection_impl.ts'; import * as React from 'react'; -import { SpacetimeDBContext } from './useSpacetimeDB'; -import type { ConnectionState } from './connection_state'; -import { ConnectionId } from '../lib/connection_id'; +import { SpacetimeDBContext } from './useSpacetimeDB.ts'; +import type { ConnectionState } from './connection_state.ts'; +import { ConnectionId } from '../lib/connection_id.ts'; import { ConnectionManager, type ConnectionState as ManagerConnectionState, -} from '../sdk/connection_manager'; +} from '../sdk/connection_manager.ts'; export interface SpacetimeDBProviderProps< DbConnection extends DbConnectionImpl, @@ -64,9 +64,16 @@ export function SpacetimeDBProvider< [key] ); + const reconnect = React.useCallback( + (builder: DbConnectionBuilder) => { + ConnectionManager.rebuild(key, builder); + }, + [key] + ); + const contextValue = React.useMemo( - () => ({ ...state, getConnection }), - [state, getConnection] + () => ({ ...state, getConnection, reconnect }), + [state, getConnection, reconnect] ); React.useEffect(() => { diff --git a/crates/bindings-typescript/src/react/connection_state.ts b/crates/bindings-typescript/src/react/connection_state.ts index 5ad565f9be6..e2900be5071 100644 --- a/crates/bindings-typescript/src/react/connection_state.ts +++ b/crates/bindings-typescript/src/react/connection_state.ts @@ -1,6 +1,16 @@ -import type { DbConnectionImpl } from '../sdk/db_connection_impl'; -import type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager'; +import type { + DbConnectionBuilder, + DbConnectionImpl, +} from '../sdk/db_connection_impl.ts'; +import type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager.ts'; export type ConnectionState = ManagerConnectionState & { getConnection(): DbConnectionImpl | null; + /** + * Tear down the current connection and reconnect using a fresh builder, for + * example to switch identity after sign-in or sign-out. The builder should + * carry the new token and the same uri + database name. Hooks re-bind to the + * new connection automatically. + */ + reconnect(builder: DbConnectionBuilder): void; }; diff --git a/crates/bindings-typescript/src/react/useProcedure.ts b/crates/bindings-typescript/src/react/useProcedure.ts index 1f688335f34..906fd0fce3c 100644 --- a/crates/bindings-typescript/src/react/useProcedure.ts +++ b/crates/bindings-typescript/src/react/useProcedure.ts @@ -1,10 +1,10 @@ import { useCallback, useEffect, useRef } from 'react'; -import type { UntypedProcedureDef } from '../sdk/procedures'; -import { useSpacetimeDB } from './useSpacetimeDB'; +import type { UntypedProcedureDef } from '../sdk/procedures.ts'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; import type { ProcedureParamsType, ProcedureReturnType, -} from '../sdk/type_utils'; +} from '../sdk/type_utils.ts'; export function useProcedure( procedureDef: ProcedureDef diff --git a/crates/bindings-typescript/src/react/useReducer.ts b/crates/bindings-typescript/src/react/useReducer.ts index 8e0ab3177c2..37762107d16 100644 --- a/crates/bindings-typescript/src/react/useReducer.ts +++ b/crates/bindings-typescript/src/react/useReducer.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef } from 'react'; -import type { UntypedReducerDef } from '../sdk/reducers'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import type { ParamsType } from '../sdk'; +import type { UntypedReducerDef } from '../sdk/reducers.ts'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import type { ParamsType } from '../sdk/index.ts'; export function useReducer( reducerDef: ReducerDef diff --git a/crates/bindings-typescript/src/react/useSpacetimeDB.ts b/crates/bindings-typescript/src/react/useSpacetimeDB.ts index 2a7cbfc828a..db7183a21fd 100644 --- a/crates/bindings-typescript/src/react/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/react/useSpacetimeDB.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import type { ConnectionState } from './connection_state'; +import type { ConnectionState } from './connection_state.ts'; export const SpacetimeDBContext = createContext( undefined diff --git a/crates/bindings-typescript/src/react/useTable.ts b/crates/bindings-typescript/src/react/useTable.ts index 313627cead4..e6248582eaf 100644 --- a/crates/bindings-typescript/src/react/useTable.ts +++ b/crates/bindings-typescript/src/react/useTable.ts @@ -5,12 +5,12 @@ import { useState, useSyncExternalStore, } from 'react'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import { type EventContextInterface } from '../sdk/db_connection_impl'; -import type { ConnectionState } from './connection_state'; -import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -import type { RowType, UntypedTableDef } from '../lib/table'; -import type { Prettify } from '../lib/type_util'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import { type EventContextInterface } from '../sdk/db_connection_impl.ts'; +import type { ConnectionState } from './connection_state.ts'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module.ts'; +import type { RowType, UntypedTableDef } from '../lib/table.ts'; +import type { Prettify } from '../lib/type_util.ts'; import { type Query, toSql, @@ -18,7 +18,7 @@ import { evaluateBooleanExpr, getQueryAccessorName, getQueryWhereClause, -} from '../lib/query'; +} from '../lib/query.ts'; export interface UseTableCallbacks { onInsert?: (row: RowType) => void; diff --git a/crates/bindings-typescript/src/sdk/client_api/index.ts b/crates/bindings-typescript/src/sdk/client_api/index.ts index 85be7b0c365..234a6dd5676 100644 --- a/crates/bindings-typescript/src/sdk/client_api/index.ts +++ b/crates/bindings-typescript/src/sdk/client_api/index.ts @@ -31,7 +31,7 @@ import { type RemoteModule as __RemoteModule, type SubscriptionEventContextInterface as __SubscriptionEventContextInterface, type SubscriptionHandleImpl as __SubscriptionHandleImpl, -} from '../../index'; +} from '../../index.ts'; // Import all reducer arg schemas diff --git a/crates/bindings-typescript/src/sdk/client_api/types.ts b/crates/bindings-typescript/src/sdk/client_api/types.ts index 709a114da28..57c29127e3c 100644 --- a/crates/bindings-typescript/src/sdk/client_api/types.ts +++ b/crates/bindings-typescript/src/sdk/client_api/types.ts @@ -8,7 +8,7 @@ import { t as __t, type AlgebraicTypeType as __AlgebraicTypeType, type Infer as __Infer, -} from '../../lib/type_builders'; +} from '../../lib/type_builders.ts'; export const BsatnRowList = __t.object('BsatnRowList', { get sizeHint() { diff --git a/crates/bindings-typescript/src/sdk/client_api/types/procedures.ts b/crates/bindings-typescript/src/sdk/client_api/types/procedures.ts index 8726cd8baf0..2f27124fb99 100644 --- a/crates/bindings-typescript/src/sdk/client_api/types/procedures.ts +++ b/crates/bindings-typescript/src/sdk/client_api/types/procedures.ts @@ -3,6 +3,6 @@ /* eslint-disable */ /* tslint:disable */ -import { type Infer as __Infer } from '../../../lib/type_builders'; +import { type Infer as __Infer } from '../../../lib/type_builders.ts'; // Import all procedure arg schemas diff --git a/crates/bindings-typescript/src/sdk/client_api/types/reducers.ts b/crates/bindings-typescript/src/sdk/client_api/types/reducers.ts index 183aff65fa7..02064193157 100644 --- a/crates/bindings-typescript/src/sdk/client_api/types/reducers.ts +++ b/crates/bindings-typescript/src/sdk/client_api/types/reducers.ts @@ -3,6 +3,6 @@ /* eslint-disable */ /* tslint:disable */ -import { type Infer as __Infer } from '../../../lib/type_builders'; +import { type Infer as __Infer } from '../../../lib/type_builders.ts'; // Import all reducer arg schemas diff --git a/crates/bindings-typescript/src/sdk/client_table.ts b/crates/bindings-typescript/src/sdk/client_table.ts index b6cd2c1a56d..4cd643179b4 100644 --- a/crates/bindings-typescript/src/sdk/client_table.ts +++ b/crates/bindings-typescript/src/sdk/client_table.ts @@ -1,16 +1,16 @@ -import type { ReadonlyIndexes } from '../lib/indexes'; -import type { TableNamesOf } from '../lib/schema'; +import type { ReadonlyIndexes } from '../lib/indexes.ts'; +import type { TableNamesOf } from '../lib/schema.ts'; 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'; +} from '../lib/table.ts'; +import type { ColumnBuilder } from '../lib/type_builders.ts'; +import type { Prettify } from '../lib/type_util.ts'; +import type { TableDefForTableName } from './client_cache.ts'; +import type { EventContextInterface } from './event_context.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; export type ClientTablePrimaryKeyMethods< RemoteModule extends UntypedRemoteModule, diff --git a/crates/bindings-typescript/src/sdk/connection_manager.ts b/crates/bindings-typescript/src/sdk/connection_manager.ts index 2cf34ea2c36..afbd5f57bd2 100644 --- a/crates/bindings-typescript/src/sdk/connection_manager.ts +++ b/crates/bindings-typescript/src/sdk/connection_manager.ts @@ -31,9 +31,9 @@ import type { DbConnectionBuilder, DbConnectionImpl, ErrorContextInterface, -} from './db_connection_impl'; -import type { Identity } from '../lib/identity'; -import { ConnectionId } from '../lib/connection_id'; +} from './db_connection_impl.ts'; +import type { Identity } from '../lib/identity.ts'; +import { ConnectionId } from '../lib/connection_id.ts'; /** Represents the current state of a managed connection. */ export type ConnectionState = { @@ -315,6 +315,10 @@ class ConnectionManagerImpl { * * Pass `resumeSession: false` when the caller is deliberately changing * identity — see {@link rebuild} — so the builder's own token wins. + * + * A builder with a token provider is never overwritten: the provider is + * called for every attempt and already decides the identity, and resuming + * its last token would reuse a short-lived credential after it expired. */ #buildManagedConnection>( managed: ManagedConnection, @@ -322,7 +326,7 @@ class ConnectionManagerImpl { { resumeSession = true }: { resumeSession?: boolean } = {} ): T { managed.builder = builder; - if (resumeSession && managed.state.token) { + if (resumeSession && managed.state.token && !builder.hasTokenProvider()) { builder.withToken(managed.state.token); } const connection = builder.build(); diff --git a/crates/bindings-typescript/src/sdk/db_connection_builder.ts b/crates/bindings-typescript/src/sdk/db_connection_builder.ts index 282cab02d37..c03dfdc77e3 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_builder.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_builder.ts @@ -1,14 +1,18 @@ -import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl'; -import { EventEmitter } from './event_emitter'; +import { + DbConnectionImpl, + type ConnectionEvent, + type TokenProvider, +} from './db_connection_impl.ts'; +import { EventEmitter } from './event_emitter.ts'; import type { DbConnectionConfig, ErrorContextInterface, Identity, RemoteModuleOf, -} from '../'; -import { ensureMinimumVersionOrThrow } from './version'; -import { WebsocketDecompressAdapter } from './websocket_decompress_adapter'; -import type { WebSocketFactory } from './ws'; +} from '../index.ts'; +import { ensureMinimumVersionOrThrow } from './version.ts'; +import { WebsocketDecompressAdapter } from './websocket_decompress_adapter.ts'; +import type { WebSocketFactory } from './ws.ts'; /** * The database client connection to a SpacetimeDB server. @@ -22,7 +26,7 @@ export class DbConnectionBuilder> { #uri?: URL; #nameOrAddress?: string; #identity?: Identity; - #token?: string; + #token?: string | TokenProvider; #emitter: EventEmitter = new EventEmitter(); #compression: 'gzip' | 'brotli' | 'none' = 'gzip'; #lightMode: boolean = false; @@ -76,13 +80,28 @@ export class DbConnectionBuilder> { * is optional. You can store the token returned by the `onConnect` callback * to use in future connections. * + * Pass a function instead of a string for short-lived tokens, such as OIDC + * session JWTs. It is called, and may return a promise, on every connection + * attempt, including automatic reconnects by the framework providers, so + * each attempt authenticates with a fresh token. Returning `undefined` + * connects anonymously. + * * @returns The `DbConnectionBuilder` instance. */ - withToken(token?: string): this { + withToken(token?: string | TokenProvider): this { this.#token = token; return this; } + /** + * Whether the token was set as a {@link TokenProvider}. The + * `ConnectionManager` does not resume a session's token over a provider, + * which already decides the identity for each attempt. + */ + hasTokenProvider(): boolean { + return typeof this.#token === 'function'; + } + withWSFn(createWSFn: WebSocketFactory): this { this.#createWSFn = createWSFn; return this; diff --git a/crates/bindings-typescript/src/sdk/db_connection_impl.ts b/crates/bindings-typescript/src/sdk/db_connection_impl.ts index 12dfca7afbf..b8121206822 100644 --- a/crates/bindings-typescript/src/sdk/db_connection_impl.ts +++ b/crates/bindings-typescript/src/sdk/db_connection_impl.ts @@ -1,5 +1,5 @@ -import { ConnectionId, ProductBuilder, ProductType } from '../'; -import { AlgebraicType, type ComparablePrimitive } from '../'; +import { ConnectionId, ProductBuilder, ProductType } from '../index.ts'; +import { AlgebraicType, type ComparablePrimitive } from '../index.ts'; import BinaryReader from '../lib/binary_reader.ts'; import BinaryWriter from '../lib/binary_writer.ts'; import { @@ -10,7 +10,7 @@ import { ServerMessage, TableUpdateRows, UnsubscribeFlags, -} from './client_api/types'; +} from './client_api/types.ts'; import { ClientCache } from './client_cache.ts'; import { DbConnectionBuilder } from './db_connection_builder.ts'; import { INTERNAL_REMOTE_MODULE } from './internal.ts'; @@ -28,14 +28,14 @@ import type { Identity, InferTypeOfParams, Serializer, -} from '../'; +} from '../index.ts'; import type { ProcedureResultMessage, ReducerResultMessage, } from './message_types.ts'; import type { ReducerEvent } from './reducer_event.ts'; import { type UntypedRemoteModule } from './spacetime_module.ts'; -import { makeFromBuilder, type NamespacedQueryBuilder } from '../lib/query'; +import { makeFromBuilder, type NamespacedQueryBuilder } from '../lib/query.ts'; import { type TableCache, type Operation, @@ -57,7 +57,7 @@ import type { } from './reducers.ts'; import type { ClientDbView } from './db_view.ts'; import type { RowType, UntypedTableDef } from '../lib/table.ts'; -import type { UntypedSchemaDef } from '../lib/schema'; +import type { UntypedSchemaDef } from '../lib/schema.ts'; import type { ProceduresView } from './procedures.ts'; import type { Values } from '../lib/type_util.ts'; import type { TransactionUpdate } from './client_api/types.ts'; @@ -69,7 +69,7 @@ import { V2_WS_PROTOCOL, V3_WS_PROTOCOL, type NegotiatedWsProtocol, -} from './websocket_protocols'; +} from './websocket_protocols.ts'; import { countClientMessagesForV3Frame, encodeClientMessagesV3, @@ -98,11 +98,20 @@ export type { export type ConnectionEvent = 'connect' | 'disconnect' | 'connectError'; +/** + * Supplies the auth token for one connection attempt. See + * {@link DbConnectionBuilder.withToken}. + */ +export type TokenProvider = () => + | string + | undefined + | Promise; + export type DbConnectionConfig = { uri: URL; nameOrAddress: string; identity?: Identity; - token?: string; + token?: string | TokenProvider; emitter: EventEmitter; createWSFn: WebSocketFactory; compression: 'gzip' | 'brotli' | 'none'; @@ -327,7 +336,6 @@ export class DbConnectionImpl } this.identity = identity; - this.token = token; this.#remoteModule = remoteModule; this.#emitter = emitter; @@ -388,15 +396,23 @@ export class DbConnectionImpl this.reducers = this.#makeReducers(remoteModule); this.procedures = this.#makeProcedures(remoteModule); - this.wsPromise = createWSFn({ - url, - nameOrAddress, - wsProtocol: [...PREFERRED_WS_PROTOCOLS], - authToken: token, - compression: compression, - lightMode: lightMode, - confirmedReads: confirmedReads, - }) + // A token provider is asked for a fresh token on every attempt, so + // short-lived credentials such as OIDC session JWTs survive reconnects. + // Resolving it inside the chain routes a throw or rejection to + // `onConnectError` rather than out of `build()`. A plain string token is + // assigned synchronously, as before. + this.wsPromise = (async () => { + this.token = typeof token === 'function' ? await token() : token; + return createWSFn({ + url, + nameOrAddress, + wsProtocol: [...PREFERRED_WS_PROTOCOLS], + authToken: this.token, + compression: compression, + lightMode: lightMode, + confirmedReads: confirmedReads, + }); + })() .then(v => { this.ws = v; @@ -423,7 +439,7 @@ export class DbConnectionImpl }) .catch(e => { stdbLogger('error', 'Error connecting to SpacetimeDB WS'); - this.#emitter.emit('connectError', this, e); + this.#emitter.emit('connectError', this, toError(e)); return undefined; }); @@ -1160,6 +1176,16 @@ export class DbConnectionImpl this.#processMessage(data); } } + } catch (e) { + // A message that fails to apply (undecodable, or a callback threw) + // leaves the client cache inconsistent, and rethrowing from a WebSocket + // listener crashes Node hosts. Fail the connection instead: drop what is + // queued and close, so `onDisconnect` receives the error. + stdbLogger('error', 'Failed to process a server message', e); + this.#connectionError = toError(e); + this.#inboundQueueOffset = this.#inboundQueue.length; + this.isActive = false; + this.ws?.close(); } finally { if (this.#inboundQueueOffset >= this.#inboundQueue.length) { this.#inboundQueue.length = 0; diff --git a/crates/bindings-typescript/src/sdk/db_context.ts b/crates/bindings-typescript/src/sdk/db_context.ts index 3ac91225fd2..1e1ed2d5d68 100644 --- a/crates/bindings-typescript/src/sdk/db_context.ts +++ b/crates/bindings-typescript/src/sdk/db_context.ts @@ -1,7 +1,7 @@ -import type { ClientDbView } from './db_view'; -import type { ReducersView } from './reducers'; -import type { UntypedRemoteModule } from './spacetime_module'; -import type { SubscriptionBuilderImpl } from './subscription_builder_impl'; +import type { ClientDbView } from './db_view.ts'; +import type { ReducersView } from './reducers.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; +import type { SubscriptionBuilderImpl } from './subscription_builder_impl.ts'; /** * Interface representing a database context. diff --git a/crates/bindings-typescript/src/sdk/db_view.ts b/crates/bindings-typescript/src/sdk/db_view.ts index baedb4dbe2d..cfa93e827b8 100644 --- a/crates/bindings-typescript/src/sdk/db_view.ts +++ b/crates/bindings-typescript/src/sdk/db_view.ts @@ -1,6 +1,6 @@ -import type { UntypedRemoteModule } from './spacetime_module'; -import type { ClientTable } from './client_table'; -import type { Values } from '../lib/type_util'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; +import type { ClientTable } from './client_table.ts'; +import type { Values } from '../lib/type_util.ts'; /** * 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/event.ts b/crates/bindings-typescript/src/sdk/event.ts index a22c3873608..17a606eff8d 100644 --- a/crates/bindings-typescript/src/sdk/event.ts +++ b/crates/bindings-typescript/src/sdk/event.ts @@ -1,5 +1,5 @@ -import type { ReducerEvent } from './reducer_event'; -import type { ReducerEventInfo } from './reducers'; +import type { ReducerEvent } from './reducer_event.ts'; +import type { ReducerEventInfo } from './reducers.ts'; type WithId = { /** diff --git a/crates/bindings-typescript/src/sdk/event_context.ts b/crates/bindings-typescript/src/sdk/event_context.ts index 9efc0100166..fc0d1570beb 100644 --- a/crates/bindings-typescript/src/sdk/event_context.ts +++ b/crates/bindings-typescript/src/sdk/event_context.ts @@ -1,5 +1,5 @@ import type { InferTypeOfParams } from '../lib/type_builders.ts'; -import type { DbContext } from './db_context'; +import type { DbContext } from './db_context.ts'; import type { Event } from './event.ts'; import type { ReducerEvent } from './reducer_event.ts'; import type { ReducerEventInfo } from './reducers.ts'; diff --git a/crates/bindings-typescript/src/sdk/logger.ts b/crates/bindings-typescript/src/sdk/logger.ts index 7374e8b3510..9179a19b623 100644 --- a/crates/bindings-typescript/src/sdk/logger.ts +++ b/crates/bindings-typescript/src/sdk/logger.ts @@ -1,5 +1,5 @@ import { stringify as ssStringify } from 'safe-stable-stringify'; -import { u128ToHexString, u256ToHexString } from '../lib/util'; +import { u128ToHexString, u256ToHexString } from '../lib/util.ts'; export type LogLevel = 'info' | 'warn' | 'error' | 'debug' | 'trace'; const LogLevelIdentifierIcon = { diff --git a/crates/bindings-typescript/src/sdk/message_types.ts b/crates/bindings-typescript/src/sdk/message_types.ts index 319f8ac605a..5304b937320 100644 --- a/crates/bindings-typescript/src/sdk/message_types.ts +++ b/crates/bindings-typescript/src/sdk/message_types.ts @@ -1,6 +1,6 @@ import type { TableUpdate } from './table_cache.ts'; import type { UntypedTableDef } from '../lib/table.ts'; -import type { ReducerOutcome } from './client_api/types'; +import type { ReducerOutcome } from './client_api/types.ts'; export type TransactionUpdateMessage = { tag: 'TransactionUpdate'; diff --git a/crates/bindings-typescript/src/sdk/procedures.ts b/crates/bindings-typescript/src/sdk/procedures.ts index df95a8e40c9..5ce830fe3df 100644 --- a/crates/bindings-typescript/src/sdk/procedures.ts +++ b/crates/bindings-typescript/src/sdk/procedures.ts @@ -1,12 +1,12 @@ -import type { ParamsObj } from '../lib/reducers'; +import type { ParamsObj } from '../lib/reducers.ts'; import type { Infer, InferTypeOfParams, TypeBuilder, -} from '../lib/type_builders'; -import type { CamelCase } from '../lib/type_util'; -import { coerceParams, toCamelCase, type CoerceParams } from '../lib/util'; -import type { UntypedRemoteModule } from './spacetime_module'; +} from '../lib/type_builders.ts'; +import type { CamelCase } from '../lib/type_util.ts'; +import { coerceParams, toCamelCase, type CoerceParams } from '../lib/util.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; // Utility: detect 'any' type IfAny = 0 extends 1 & T ? Y : N; diff --git a/crates/bindings-typescript/src/sdk/reducer_event.ts b/crates/bindings-typescript/src/sdk/reducer_event.ts index 18a39077410..eb138c0befc 100644 --- a/crates/bindings-typescript/src/sdk/reducer_event.ts +++ b/crates/bindings-typescript/src/sdk/reducer_event.ts @@ -1,5 +1,5 @@ -import { Timestamp } from '../'; -import type { ReducerOutcome } from './client_api/types'; +import { Timestamp } from '../index.ts'; +import type { ReducerOutcome } from './client_api/types.ts'; import type { ReducerEventInfo } from './reducers.ts'; export type ReducerEvent = { diff --git a/crates/bindings-typescript/src/sdk/reducers.ts b/crates/bindings-typescript/src/sdk/reducers.ts index 1111e817f57..89800a4b9c2 100644 --- a/crates/bindings-typescript/src/sdk/reducers.ts +++ b/crates/bindings-typescript/src/sdk/reducers.ts @@ -1,11 +1,11 @@ -import type { ProductType } from '../lib/algebraic_type'; -import type { ReducerSchema } from '../lib/reducer_schema'; -import type { ParamsObj } from '../lib/reducers'; -import { RowBuilder, type InferTypeOfParams } from '../lib/type_builders'; -import type { CamelCase } from '../lib/type_util'; -import { toCamelCase } from '../lib/util'; -import type { SubscriptionEventContextInterface } from './event_context'; -import type { UntypedRemoteModule } from './spacetime_module'; +import type { ProductType } from '../lib/algebraic_type.ts'; +import type { ReducerSchema } from '../lib/reducer_schema.ts'; +import type { ParamsObj } from '../lib/reducers.ts'; +import { RowBuilder, type InferTypeOfParams } from '../lib/type_builders.ts'; +import type { CamelCase } from '../lib/type_util.ts'; +import { toCamelCase } from '../lib/util.ts'; +import type { SubscriptionEventContextInterface } from './event_context.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; export type SubscriptionEventCallback< RemoteModule extends UntypedRemoteModule, diff --git a/crates/bindings-typescript/src/sdk/schema.ts b/crates/bindings-typescript/src/sdk/schema.ts index d561076ebec..9747a8e4ff5 100644 --- a/crates/bindings-typescript/src/sdk/schema.ts +++ b/crates/bindings-typescript/src/sdk/schema.ts @@ -3,8 +3,8 @@ import { tablesToSchema, type TablesToSchema, type UntypedSchemaDef, -} from '../lib/schema'; -import type { UntypedTableSchema } from '../lib/table_schema'; +} from '../lib/schema.ts'; +import type { UntypedTableSchema } from '../lib/table_schema.ts'; class Tables { constructor(readonly schemaType: S) {} diff --git a/crates/bindings-typescript/src/sdk/spacetime_module.ts b/crates/bindings-typescript/src/sdk/spacetime_module.ts index aa19b38cd32..fbf00217add 100644 --- a/crates/bindings-typescript/src/sdk/spacetime_module.ts +++ b/crates/bindings-typescript/src/sdk/spacetime_module.ts @@ -1,6 +1,6 @@ -import type { UntypedProceduresDef } from './procedures'; -import type { UntypedSchemaDef } from '../lib/schema'; -import type { UntypedReducersDef } from './reducers'; +import type { UntypedProceduresDef } from './procedures.ts'; +import type { UntypedSchemaDef } from '../lib/schema.ts'; +import type { UntypedReducersDef } from './reducers.ts'; export type RemoteModule< SchemaDef extends UntypedSchemaDef, diff --git a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts index f7e271b4e85..e8b62135ca2 100644 --- a/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts +++ b/crates/bindings-typescript/src/sdk/subscription_builder_impl.ts @@ -1,18 +1,18 @@ -import type { DbConnectionImpl } from './db_connection_impl'; -import { INTERNAL_REMOTE_MODULE } from './internal'; +import type { DbConnectionImpl } from './db_connection_impl.ts'; +import { INTERNAL_REMOTE_MODULE } from './internal.ts'; import type { ErrorContextInterface, SubscriptionEventContextInterface, -} from './event_context'; -import { EventEmitter } from './event_emitter'; -import type { UntypedRemoteModule } from './spacetime_module'; +} from './event_context.ts'; +import { EventEmitter } from './event_emitter.ts'; +import type { UntypedRemoteModule } from './spacetime_module.ts'; import { isRowTypedQuery, toSql, type NamespacedQueryBuilder, type RowTypedQuery, -} from '../lib/query'; -import type { UntypedSchemaDef } from '../lib/schema'; +} from '../lib/query.ts'; +import type { UntypedSchemaDef } from '../lib/schema.ts'; export class SubscriptionBuilderImpl { #onApplied?: (ctx: SubscriptionEventContextInterface) => void = diff --git a/crates/bindings-typescript/src/sdk/table_cache.ts b/crates/bindings-typescript/src/sdk/table_cache.ts index 543d3fb78ed..c7099715555 100644 --- a/crates/bindings-typescript/src/sdk/table_cache.ts +++ b/crates/bindings-typescript/src/sdk/table_cache.ts @@ -1,7 +1,7 @@ import { EventEmitter } from './event_emitter.ts'; import { stdbLogger } from './logger.ts'; -import { deepEqual, type ComparablePrimitive } from '../'; +import { deepEqual, type ComparablePrimitive } from '../index.ts'; import type { EventContextInterface, TableDefForTableName } from './index.ts'; import type { RowType, TableIndexes, UntypedTableDef } from '../lib/table.ts'; import type { ClientTableCoreImplementable } from './client_table.ts'; diff --git a/crates/bindings-typescript/src/sdk/type_utils.ts b/crates/bindings-typescript/src/sdk/type_utils.ts index 99ae49ca3b1..f4353206e39 100644 --- a/crates/bindings-typescript/src/sdk/type_utils.ts +++ b/crates/bindings-typescript/src/sdk/type_utils.ts @@ -1,7 +1,7 @@ -import type { Infer, InferTypeOfParams } from '.'; -import type { Prettify } from '../lib/type_util'; -import type { UntypedProcedureDef } from './procedures'; -import type { UntypedReducerDef } from './reducers'; +import type { Infer, InferTypeOfParams } from './index.ts'; +import type { Prettify } from '../lib/type_util.ts'; +import type { UntypedProcedureDef } from './procedures.ts'; +import type { UntypedReducerDef } from './reducers.ts'; export type IsEmptyObject = [keyof T] extends [never] ? true : false; export type MaybeParams = IsEmptyObject extends true ? [] : [params: T]; diff --git a/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts b/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts index c3c474b960a..c31086283fd 100644 --- a/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts +++ b/crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts @@ -1,5 +1,9 @@ -import { decompress } from './decompress'; -import { openWebSocket, type WebSocketAdapter, type WebSocketArgs } from './ws'; +import { decompress } from './decompress.ts'; +import { + openWebSocket, + type WebSocketAdapter, + type WebSocketArgs, +} from './ws.ts'; export class WebsocketDecompressAdapter implements WebSocketAdapter { get protocol(): string { diff --git a/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts b/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts index 257f9cb2806..899318a1d0f 100644 --- a/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts +++ b/crates/bindings-typescript/src/sdk/websocket_test_adapter.ts @@ -1,8 +1,11 @@ import BinaryReader from '../lib/binary_reader.ts'; import BinaryWriter from '../lib/binary_writer.ts'; -import { ClientMessage, ServerMessage } from './client_api/types'; -import type { WebSocketAdapter, WebSocketFactory } from './ws'; -import { PREFERRED_WS_PROTOCOLS, V3_WS_PROTOCOL } from './websocket_protocols'; +import { ClientMessage, ServerMessage } from './client_api/types.ts'; +import type { WebSocketAdapter, WebSocketFactory } from './ws.ts'; +import { + PREFERRED_WS_PROTOCOLS, + V3_WS_PROTOCOL, +} from './websocket_protocols.ts'; import { decodeClientMessagesV3, encodeServerMessagesV3, diff --git a/crates/bindings-typescript/src/sdk/websocket_v3_frames.ts b/crates/bindings-typescript/src/sdk/websocket_v3_frames.ts index e6613101ceb..17c8e20267b 100644 --- a/crates/bindings-typescript/src/sdk/websocket_v3_frames.ts +++ b/crates/bindings-typescript/src/sdk/websocket_v3_frames.ts @@ -1,6 +1,6 @@ import BinaryReader from '../lib/binary_reader.ts'; import BinaryWriter from '../lib/binary_writer.ts'; -import { ClientMessage, ServerMessage } from './client_api/types'; +import { ClientMessage, ServerMessage } from './client_api/types.ts'; // v3 is only a transport framing convention. The payload is one or more // already-encoded v2 websocket messages concatenated back-to-back, so these diff --git a/crates/bindings-typescript/src/sdk/ws.ts b/crates/bindings-typescript/src/sdk/ws.ts index 99d1b688ece..166edb03e04 100644 --- a/crates/bindings-typescript/src/sdk/ws.ts +++ b/crates/bindings-typescript/src/sdk/ws.ts @@ -1,4 +1,4 @@ -import { stdbLogger } from './logger'; +import { stdbLogger } from './logger.ts'; async function resolveWS(): Promise { // Browser or Node >= 22 (or any env that exposes global WebSocket) diff --git a/crates/bindings-typescript/src/server/console.ts b/crates/bindings-typescript/src/server/console.ts index 5094030ce33..fc1a11e10ec 100644 --- a/crates/bindings-typescript/src/server/console.ts +++ b/crates/bindings-typescript/src/server/console.ts @@ -1,5 +1,5 @@ import type { u32 } from 'spacetime:sys@2.0'; -import { sys } from './runtime'; +import { sys } from './runtime.ts'; import inspect from 'object-inspect'; const fmtLog = (...data: any[]) => diff --git a/crates/bindings-typescript/src/server/db_view.ts b/crates/bindings-typescript/src/server/db_view.ts index 847a74f957b..e6cf9cb3897 100644 --- a/crates/bindings-typescript/src/server/db_view.ts +++ b/crates/bindings-typescript/src/server/db_view.ts @@ -1,6 +1,6 @@ -import type { UntypedSchemaDef } from '../lib/schema'; -import type { ReadonlyTable, Table } from '../lib/table'; -import type { Values } from '../lib/type_util'; +import type { UntypedSchemaDef } from '../lib/schema.ts'; +import type { ReadonlyTable, Table } from '../lib/table.ts'; +import type { Values } from '../lib/type_util.ts'; /** * A type representing a read-only database view, mapping table names to their corresponding read-only Table handles. diff --git a/crates/bindings-typescript/src/server/environment.ts b/crates/bindings-typescript/src/server/environment.ts index 93daa9ce767..4dc6185e825 100644 --- a/crates/bindings-typescript/src/server/environment.ts +++ b/crates/bindings-typescript/src/server/environment.ts @@ -1,11 +1,11 @@ import { env_get } from 'spacetime:sys@2.2'; -import type { Environment, EnvironmentSchema } from '../lib/environment'; +import type { Environment, EnvironmentSchema } from '../lib/environment.ts'; import type { EnvironmentDeclaration, EnvVarType, AlgebraicType, -} from '../lib/autogen/types'; -import { OptionBuilder, StringBuilder } from '../lib/type_builders'; +} from '../lib/autogen/types.ts'; +import { OptionBuilder, StringBuilder } from '../lib/type_builders.ts'; // These UTF-8 byte/count limits match spacetimedb_lib::environment. const MAX_ENV_KEY_BYTES = 256; @@ -31,7 +31,7 @@ export type { Environment, EnvironmentFor, EnvironmentSchema, -} from '../lib/environment'; +} from '../lib/environment.ts'; /** Produce metadata only. No environment value is embedded in the artifact. */ export function environmentDeclarations( diff --git a/crates/bindings-typescript/src/server/errors.ts b/crates/bindings-typescript/src/server/errors.ts index 397512e50ce..0f153cffad1 100644 --- a/crates/bindings-typescript/src/server/errors.ts +++ b/crates/bindings-typescript/src/server/errors.ts @@ -1,4 +1,4 @@ -import { SenderError } from '../lib/errors'; +import { SenderError } from '../lib/errors.ts'; /** * Base class for all Spacetime host errors (i.e. errors that may be thrown diff --git a/crates/bindings-typescript/src/server/http.test-d.ts b/crates/bindings-typescript/src/server/http.test-d.ts index 80299ef6a85..9d45a36b7e5 100644 --- a/crates/bindings-typescript/src/server/http.test-d.ts +++ b/crates/bindings-typescript/src/server/http.test-d.ts @@ -1,12 +1,12 @@ -import { table } from '../lib/table'; -import t from '../lib/type_builders'; +import { table } from '../lib/table.ts'; +import t from '../lib/type_builders.ts'; import { type HandlerContext, Request, SyncResponse, Router, schema, -} from './index'; +} from './index.ts'; const person = table( {}, diff --git a/crates/bindings-typescript/src/server/http.ts b/crates/bindings-typescript/src/server/http.ts index 62f68906f1b..2fafb1982c0 100644 --- a/crates/bindings-typescript/src/server/http.ts +++ b/crates/bindings-typescript/src/server/http.ts @@ -11,4 +11,4 @@ export { type HandlerFn, type HttpHandlerExport, type HttpHandlerOpts, -} from './http_handlers'; +} from './http_handlers.ts'; diff --git a/crates/bindings-typescript/src/server/http_handlers.ts b/crates/bindings-typescript/src/server/http_handlers.ts index b8823577f4e..ad6311d0b2f 100644 --- a/crates/bindings-typescript/src/server/http_handlers.ts +++ b/crates/bindings-typescript/src/server/http_handlers.ts @@ -1,22 +1,22 @@ -import type { EnvironmentFor } from '../lib/environment'; -import type { Identity } from '../lib/identity'; +import type { EnvironmentFor } from '../lib/environment.ts'; +import type { Identity } from '../lib/identity.ts'; import type { HttpMethod, HttpVersion, MethodOrAny, -} from '../lib/autogen/types'; -import type { UntypedSchemaDef } from '../lib/schema'; -import type { Timestamp } from '../lib/timestamp'; -import type { Uuid } from '../lib/uuid'; -import type { TransactionCtx } from './procedures'; -import type { HttpClient } from './http_internal'; -import type { Random } from './rng'; +} from '../lib/autogen/types.ts'; +import type { UntypedSchemaDef } from '../lib/schema.ts'; +import type { Timestamp } from '../lib/timestamp.ts'; +import type { Uuid } from '../lib/uuid.ts'; +import type { TransactionCtx } from './procedures.ts'; +import type { HttpClient } from './http_internal.ts'; +import type { Random } from './rng.ts'; import { exportContext, registerExport, type ModuleExport, type SchemaInner, -} from './schema'; +} from './schema.ts'; import { Headers, makeResponse, @@ -26,7 +26,7 @@ import { type BodyInit, type HeadersInit, type ResponseInit, -} from './http_shared'; +} from './http_shared.ts'; export { Headers }; export { SyncResponse }; diff --git a/crates/bindings-typescript/src/server/http_internal.ts b/crates/bindings-typescript/src/server/http_internal.ts index 1ba1f31a5bf..141f8ae0df7 100644 --- a/crates/bindings-typescript/src/server/http_internal.ts +++ b/crates/bindings-typescript/src/server/http_internal.ts @@ -1,9 +1,9 @@ -import BinaryReader from '../lib/binary_reader'; -import BinaryWriter from '../lib/binary_writer'; +import BinaryReader from '../lib/binary_reader.ts'; +import BinaryWriter from '../lib/binary_writer.ts'; import status from 'statuses'; -import { HttpRequest, HttpResponse } from '../lib/autogen/types'; -import type { TimeDuration } from '../lib/time_duration'; -import { bsatnBaseSize } from '../lib/util'; +import { HttpRequest, HttpResponse } from '../lib/autogen/types.ts'; +import type { TimeDuration } from '../lib/time_duration.ts'; +import { bsatnBaseSize } from '../lib/util.ts'; import { type BodyInit, type HeadersInit, @@ -13,8 +13,8 @@ import { serializeHeaders, serializeMethod, SyncResponse, -} from './http_shared'; -import { sys } from './runtime'; +} from './http_shared.ts'; +import { sys } from './runtime.ts'; export { Headers }; diff --git a/crates/bindings-typescript/src/server/http_shared.ts b/crates/bindings-typescript/src/server/http_shared.ts index ea422b7ecd1..f85e2b85dca 100644 --- a/crates/bindings-typescript/src/server/http_shared.ts +++ b/crates/bindings-typescript/src/server/http_shared.ts @@ -3,7 +3,7 @@ import type { HttpHeaders, HttpMethod, HttpVersion, -} from '../lib/autogen/types'; +} from '../lib/autogen/types.ts'; export { Headers }; diff --git a/crates/bindings-typescript/src/server/index.ts b/crates/bindings-typescript/src/server/index.ts index c3a3d3c1458..a337070cad6 100644 --- a/crates/bindings-typescript/src/server/index.ts +++ b/crates/bindings-typescript/src/server/index.ts @@ -1,28 +1,28 @@ -export * from '../lib/type_builders'; +export * from '../lib/type_builders.ts'; export { schema, type InferSchema, type ModuleExport, type ModuleSettings, type SubmoduleMount, -} from './schema'; -export { CaseConversionPolicy } from '../lib/autogen/types'; -export { table } from '../lib/table'; -export { SenderError, SpacetimeHostError, errors } from './errors'; -export type { Reducer, ReducerCtx, JwtClaims, AuthCtx } from '../lib/reducers'; -export type { ReducerExport } from './reducers'; -export { type DbView } from './db_view'; -export * from './query'; +} from './schema.ts'; +export { CaseConversionPolicy } from '../lib/autogen/types.ts'; +export { table } from '../lib/table.ts'; +export { SenderError, SpacetimeHostError, errors } from './errors.ts'; +export type { Reducer, ReducerCtx, JwtClaims, AuthCtx } from '../lib/reducers.ts'; +export type { ReducerExport } from './reducers.ts'; +export { type DbView } from './db_view.ts'; +export * from './query.ts'; export type { ProcedureCtx, TransactionCtx, ProcedureExport, -} from './procedures'; -export { toCamelCase } from '../lib/util'; -export type { Uuid } from '../lib/uuid'; -export type { Random } from './rng'; -export type { ViewExport, ViewCtx, AnonymousViewCtx } from './views'; -export { Range, type Bound } from './range'; +} from './procedures.ts'; +export { toCamelCase } from '../lib/util.ts'; +export type { Uuid } from '../lib/uuid.ts'; +export type { Random } from './rng.ts'; +export type { ViewExport, ViewCtx, AnonymousViewCtx } from './views.ts'; +export { Range, type Bound } from './range.ts'; export { Headers, Request, @@ -32,10 +32,10 @@ export { type HeadersInit, type RequestInit, type ResponseInit, -} from './http'; -export type { HandlerContext, HttpHandlerExport } from './http'; -export { ScheduleAt } from '../lib/schedule_at'; +} from './http.ts'; +export type { HandlerContext, HttpHandlerExport } from './http.ts'; +export { ScheduleAt } from '../lib/schedule_at.ts'; -export type { Environment } from './environment'; +export type { Environment } from './environment.ts'; -import './polyfills'; // Ensure polyfills are loaded +import './polyfills.ts'; // Ensure polyfills are loaded diff --git a/crates/bindings-typescript/src/server/polyfills.ts b/crates/bindings-typescript/src/server/polyfills.ts index 3e88b119de4..d02579fd088 100644 --- a/crates/bindings-typescript/src/server/polyfills.ts +++ b/crates/bindings-typescript/src/server/polyfills.ts @@ -1,4 +1,4 @@ import 'url-polyfill'; -import { console } from './console'; +import { console } from './console.ts'; globalThis.console = console; diff --git a/crates/bindings-typescript/src/server/procedures.ts b/crates/bindings-typescript/src/server/procedures.ts index f5b8e2aa1e2..8bfe6a6fc02 100644 --- a/crates/bindings-typescript/src/server/procedures.ts +++ b/crates/bindings-typescript/src/server/procedures.ts @@ -1,30 +1,30 @@ -import { environment, type EnvironmentFor } from './environment'; +import { environment, type EnvironmentFor } from './environment.ts'; import { AlgebraicType, ProductType, type Deserializer, type Serializer, -} from '../lib/algebraic_type'; -import { FunctionVisibility } from '../lib/autogen/types'; -import BinaryReader from '../lib/binary_reader'; -import BinaryWriter from '../lib/binary_writer'; -import type { ConnectionId } from '../lib/connection_id'; -import { Identity } from '../lib/identity'; -import type { ParamsObj, ReducerCtx } from '../lib/reducers'; -import { type UntypedSchemaDef } from '../lib/schema'; -import type { ScheduleTableForParams } from '../lib/table_schema'; -import { Timestamp } from '../lib/timestamp'; +} from '../lib/algebraic_type.ts'; +import { FunctionVisibility } from '../lib/autogen/types.ts'; +import BinaryReader from '../lib/binary_reader.ts'; +import BinaryWriter from '../lib/binary_writer.ts'; +import type { ConnectionId } from '../lib/connection_id.ts'; +import { Identity } from '../lib/identity.ts'; +import type { ParamsObj, ReducerCtx } from '../lib/reducers.ts'; +import { type UntypedSchemaDef } from '../lib/schema.ts'; +import type { ScheduleTableForParams } from '../lib/table_schema.ts'; +import { Timestamp } from '../lib/timestamp.ts'; import { type Infer, type InferTypeOfRow, type t, type TypeBuilder, -} from '../lib/type_builders'; -import { bsatnBaseSize } from '../lib/util'; -import { Uuid } from '../lib/uuid'; -import { httpClient, type HttpClient } from './http_internal'; -import type { DbView } from './db_view'; -import { makeRandom, type Random } from './rng'; +} from '../lib/type_builders.ts'; +import { bsatnBaseSize } from '../lib/util.ts'; +import { Uuid } from '../lib/uuid.ts'; +import { httpClient, type HttpClient } from './http_internal.ts'; +import type { DbView } from './db_view.ts'; +import { makeRandom, type Random } from './rng.ts'; import { assignTxAliasViews, buildProcedureAliasCtxMap, @@ -32,14 +32,14 @@ import { ReducerCtxImpl, runWithTx, sys, -} from './runtime'; +} from './runtime.ts'; import { exportContext, registerExport, type SubmoduleDispatchInfo, type ModuleExport, type SchemaInner, -} from './schema'; +} from './schema.ts'; export type ProcedureExport< S extends UntypedSchemaDef, diff --git a/crates/bindings-typescript/src/server/query.ts b/crates/bindings-typescript/src/server/query.ts index afeafcdfe03..71487a58b88 100644 --- a/crates/bindings-typescript/src/server/query.ts +++ b/crates/bindings-typescript/src/server/query.ts @@ -1 +1 @@ -export * from '../lib/query'; +export * from '../lib/query.ts'; diff --git a/crates/bindings-typescript/src/server/reducers.ts b/crates/bindings-typescript/src/server/reducers.ts index ea5f770faf8..3a981901d41 100644 --- a/crates/bindings-typescript/src/server/reducers.ts +++ b/crates/bindings-typescript/src/server/reducers.ts @@ -1,16 +1,16 @@ -import { AlgebraicType } from '../lib/algebraic_type'; -import { FunctionVisibility, type Lifecycle } from '../lib/autogen/types'; -import type { ParamsObj, Reducer } from '../lib/reducers'; -import { type UntypedSchemaDef } from '../lib/schema'; -import type { ScheduleTableForParams } from '../lib/table_schema'; -import { RowBuilder, type RowObj } from '../lib/type_builders'; -import { toPascalCase } from '../lib/util'; +import { AlgebraicType } from '../lib/algebraic_type.ts'; +import { FunctionVisibility, type Lifecycle } from '../lib/autogen/types.ts'; +import type { ParamsObj, Reducer } from '../lib/reducers.ts'; +import { type UntypedSchemaDef } from '../lib/schema.ts'; +import type { ScheduleTableForParams } from '../lib/table_schema.ts'; +import { RowBuilder, type RowObj } from '../lib/type_builders.ts'; +import { toPascalCase } from '../lib/util.ts'; import { exportContext, registerExport, type ModuleExport, type SchemaInner, -} from './schema'; +} from './schema.ts'; export interface ReducerExport< S extends UntypedSchemaDef, diff --git a/crates/bindings-typescript/src/server/rng.ts b/crates/bindings-typescript/src/server/rng.ts index 2bde81d64ad..0b619112620 100644 --- a/crates/bindings-typescript/src/server/rng.ts +++ b/crates/bindings-typescript/src/server/rng.ts @@ -2,7 +2,7 @@ import type { RandomGenerator } from 'pure-rand'; import { unsafeUniformBigIntDistribution } from 'pure-rand/distribution/UnsafeUniformBigIntDistribution'; import { unsafeUniformIntDistribution } from 'pure-rand/distribution/UnsafeUniformIntDistribution'; import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; -import type { Timestamp } from '../lib/timestamp'; +import type { Timestamp } from '../lib/timestamp.ts'; type IntArray = | Int8Array diff --git a/crates/bindings-typescript/src/server/runtime.ts b/crates/bindings-typescript/src/server/runtime.ts index 70c6eb81401..be0ded8c500 100644 --- a/crates/bindings-typescript/src/server/runtime.ts +++ b/crates/bindings-typescript/src/server/runtime.ts @@ -1,4 +1,4 @@ -import { environment, type EnvironmentFor } from './environment'; +import { environment, type EnvironmentFor } from './environment.ts'; import * as _syscalls2_0 from 'spacetime:sys@2.0'; import * as _syscalls2_1 from 'spacetime:sys@2.1'; @@ -7,7 +7,7 @@ import { AlgebraicType, ProductType, type Deserializer, -} from '../lib/algebraic_type'; +} from '../lib/algebraic_type.ts'; import { RawModuleDef, ViewResultHeader, @@ -15,68 +15,68 @@ import { type RawReducerDefV10, type RawTableDefV10, type Typespace, -} from '../lib/autogen/types'; -import { ConnectionId } from '../lib/connection_id'; -import { Identity } from '../lib/identity'; -import { Timestamp } from '../lib/timestamp'; -import { Uuid } from '../lib/uuid'; -import BinaryReader from '../lib/binary_reader'; -import BinaryWriter, { ResizableBuffer } from '../lib/binary_writer'; +} from '../lib/autogen/types.ts'; +import { ConnectionId } from '../lib/connection_id.ts'; +import { Identity } from '../lib/identity.ts'; +import { Timestamp } from '../lib/timestamp.ts'; +import { Uuid } from '../lib/uuid.ts'; +import BinaryReader from '../lib/binary_reader.ts'; +import BinaryWriter, { ResizableBuffer } from '../lib/binary_writer.ts'; import { type Index, type IndexVal, type PointIndex, type RangedIndex, type UniqueIndex, -} from '../lib/indexes'; -import { callProcedure } from './procedures'; -import type { Procedures } from './procedures'; -import type { Reducers } from './reducers'; +} from '../lib/indexes.ts'; +import { callProcedure } from './procedures.ts'; +import type { Procedures } from './procedures.ts'; +import type { Reducers } from './reducers.ts'; import { type HandlerContext, Request, SyncResponse, makeRequest, -} from './http_handlers'; -import { httpClient } from './http_internal'; +} from './http_handlers.ts'; +import { httpClient } from './http_internal.ts'; import { deserializeHeaders, deserializeMethod, serializeHeaders, -} from './http_shared'; +} from './http_shared.ts'; import { type AliasViews, type AuthCtx, type JsonObject, type JwtClaims, type ReducerCtx as IReducerCtx, -} from '../lib/reducers'; -import { type UntypedSchemaDef } from '../lib/schema'; +} from '../lib/reducers.ts'; +import { type UntypedSchemaDef } from '../lib/schema.ts'; import { type RowType, type Table, type TableMethods, type UntypedTableDef, -} from '../lib/table'; -import { bsatnBaseSize, hasOwn } from '../lib/util'; +} from '../lib/table.ts'; +import { bsatnBaseSize, hasOwn } from '../lib/util.ts'; import { type AnonymousViewCtx, type AnonViews, type ViewCtx, type Views, -} from './views'; +} from './views.ts'; import { isRowTypedQuery, makeQueryBuilder, toSql, type QueryBuilder, -} from './query'; -import type { DbView, ReadonlyDbView } from './db_view'; -import { getErrorConstructor, SenderError } from './errors'; -import { Range, type Bound } from './range'; -import { makeRandom, type Random } from './rng'; -import type { SubmoduleDispatchInfo, SchemaInner } from './schema'; -import { HttpRequest, HttpResponse } from '../lib/autogen/types'; +} from './query.ts'; +import type { DbView, ReadonlyDbView } from './db_view.ts'; +import { getErrorConstructor, SenderError } from './errors.ts'; +import { Range, type Bound } from './range.ts'; +import { makeRandom, type Random } from './rng.ts'; +import type { SubmoduleDispatchInfo, SchemaInner } from './schema.ts'; +import { HttpRequest, HttpResponse } from '../lib/autogen/types.ts'; const { freeze } = Object; diff --git a/crates/bindings-typescript/src/server/schema.test-d.ts b/crates/bindings-typescript/src/server/schema.test-d.ts index 6d142fe2741..7f21a2acb29 100644 --- a/crates/bindings-typescript/src/server/schema.test-d.ts +++ b/crates/bindings-typescript/src/server/schema.test-d.ts @@ -1,7 +1,7 @@ -import { registerExport, schema } from './schema'; -import type { ProcedureExport } from './procedures'; -import { table } from '../lib/table'; -import t from '../lib/type_builders'; +import { registerExport, schema } from './schema.ts'; +import type { ProcedureExport } from './procedures.ts'; +import { table } from '../lib/table.ts'; +import t from '../lib/type_builders.ts'; const person = table( { diff --git a/crates/bindings-typescript/src/server/schema.ts b/crates/bindings-typescript/src/server/schema.ts index e1e0b7b26a5..3a3bc035237 100644 --- a/crates/bindings-typescript/src/server/schema.ts +++ b/crates/bindings-typescript/src/server/schema.ts @@ -1,4 +1,7 @@ -import { environmentDeclarations, type EnvironmentSchema } from './environment'; +import { + environmentDeclarations, + type EnvironmentSchema, +} from './environment.ts'; import { moduleHooks, type ModuleDefaultExport } from 'spacetime:sys@2.0'; import { CaseConversionPolicy, @@ -9,22 +12,22 @@ import { type RawReducerDefV10, type RawTableDefV10, type Typespace, -} from '../lib/autogen/types'; +} from '../lib/autogen/types.ts'; import { type ParamsAsObject, type ParamsObj, type Reducer, type ReducerCtx, -} from '../lib/reducers'; +} from '../lib/reducers.ts'; import { ModuleContext, tableToSchema, type TablesToSchema, type UntypedSchemaDef, -} from '../lib/schema'; -import type { UntypedTableSchema } from '../lib/table_schema'; -import { TypeBuilder, type ColumnBuilder } from '../lib/type_builders'; -import { hasOwn } from '../lib/util'; +} from '../lib/schema.ts'; +import type { UntypedTableSchema } from '../lib/table_schema.ts'; +import { TypeBuilder, type ColumnBuilder } from '../lib/type_builders.ts'; +import { hasOwn } from '../lib/util.ts'; import { Router, type HandlerFn, @@ -32,7 +35,7 @@ import { type HttpHandlerOpts, makeHttpHandlerExport, makeHttpRouterExport, -} from './http_handlers'; +} from './http_handlers.ts'; import { makeProcedureExport, type ProcedureExport, @@ -40,15 +43,15 @@ import { type ProcedureOpts, type ProcedureOptsWithOptionalName, type Procedures, -} from './procedures'; +} from './procedures.ts'; import { makeReducerExport, type ReducerExport, type ReducerOpts, type ReducerOptsWithOptionalName, type Reducers, -} from './reducers'; -import { makeHooks } from './runtime'; +} from './reducers.ts'; +import { makeHooks } from './runtime.ts'; import { makeAnonViewExport, @@ -61,8 +64,8 @@ import { type ViewReturnTypeBuilder, type ValidateViewPrimaryKey, type Views, -} from './views'; -import type { UntypedTableDef } from '../lib/table'; +} from './views.ts'; +import type { UntypedTableDef } from '../lib/table.ts'; /** * Internal erased form of a scheduled reducer/procedure export. diff --git a/crates/bindings-typescript/src/server/view.test-d.ts b/crates/bindings-typescript/src/server/view.test-d.ts index de91adfcb83..d99a6e50c66 100644 --- a/crates/bindings-typescript/src/server/view.test-d.ts +++ b/crates/bindings-typescript/src/server/view.test-d.ts @@ -1,6 +1,6 @@ -import { schema } from './schema'; -import { table } from '../lib/table'; -import t from '../lib/type_builders'; +import { schema } from './schema.ts'; +import { table } from '../lib/table.ts'; +import t from '../lib/type_builders.ts'; const person = table( { diff --git a/crates/bindings-typescript/src/server/views.ts b/crates/bindings-typescript/src/server/views.ts index f089de2f0c1..03120e88828 100644 --- a/crates/bindings-typescript/src/server/views.ts +++ b/crates/bindings-typescript/src/server/views.ts @@ -1,15 +1,15 @@ -import type { EnvironmentFor } from '../lib/environment'; +import type { EnvironmentFor } from '../lib/environment.ts'; import { AlgebraicType, ProductType, type AlgebraicTypeVariants, type Deserializer, type Serializer, -} from '../lib/algebraic_type'; -import type { Identity } from '../lib/identity'; -import type { OptionAlgebraicType } from '../lib/option'; -import type { ParamsObj } from '../lib/reducers'; -import { type UntypedSchemaDef } from '../lib/schema'; +} from '../lib/algebraic_type.ts'; +import type { Identity } from '../lib/identity.ts'; +import type { OptionAlgebraicType } from '../lib/option.ts'; +import type { ParamsObj } from '../lib/reducers.ts'; +import { type UntypedSchemaDef } from '../lib/schema.ts'; import { ArrayBuilder, OptionBuilder, @@ -21,17 +21,17 @@ import { type InferTypeOfRow, type RowObj, type TypeBuilder, -} from '../lib/type_builders'; -import type { IsUnion } from '../lib/type_util'; -import { bsatnBaseSize, toPascalCase } from '../lib/util'; -import type { ReadonlyDbView } from './db_view'; -import { type QueryBuilder, type RowTypedQuery } from './query'; +} from '../lib/type_builders.ts'; +import type { IsUnion } from '../lib/type_util.ts'; +import { bsatnBaseSize, toPascalCase } from '../lib/util.ts'; +import type { ReadonlyDbView } from './db_view.ts'; +import { type QueryBuilder, type RowTypedQuery } from './query.ts'; import { exportContext, registerExport, type ModuleExport, type SchemaInner, -} from './schema'; +} from './schema.ts'; export type ViewExport = ViewFn & ModuleExport; diff --git a/crates/bindings-typescript/src/solid/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/solid/SpacetimeDBProvider.ts index 84752691d5a..346edce5cef 100644 --- a/crates/bindings-typescript/src/solid/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/solid/SpacetimeDBProvider.ts @@ -1,16 +1,16 @@ import { DbConnectionBuilder, type DbConnectionImpl, -} from '../sdk/db_connection_impl'; +} from '../sdk/db_connection_impl.ts'; import { onCleanup, createMemo, createComputed } from 'solid-js'; import { createStore } from 'solid-js/store'; -import { SpacetimeDBContext } from './useSpacetimeDB'; -import type { ConnectionState } from './connection_state'; -import { ConnectionId } from '../lib/connection_id'; +import { SpacetimeDBContext } from './useSpacetimeDB.ts'; +import type { ConnectionState } from './connection_state.ts'; +import { ConnectionId } from '../lib/connection_id.ts'; import { ConnectionManager, type ConnectionState as ManagerConnectionState, -} from '../sdk/connection_manager'; +} from '../sdk/connection_manager.ts'; export interface SpacetimeDBProviderProps< DbConnection extends DbConnectionImpl, diff --git a/crates/bindings-typescript/src/solid/connection_state.ts b/crates/bindings-typescript/src/solid/connection_state.ts index 5ad565f9be6..f1df5fe9dbb 100644 --- a/crates/bindings-typescript/src/solid/connection_state.ts +++ b/crates/bindings-typescript/src/solid/connection_state.ts @@ -1,5 +1,5 @@ -import type { DbConnectionImpl } from '../sdk/db_connection_impl'; -import type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager'; +import type { DbConnectionImpl } from '../sdk/db_connection_impl.ts'; +import type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager.ts'; export type ConnectionState = ManagerConnectionState & { getConnection(): DbConnectionImpl | null; diff --git a/crates/bindings-typescript/src/solid/useProcedure.ts b/crates/bindings-typescript/src/solid/useProcedure.ts index a5db86dc434..ebfe4f38090 100644 --- a/crates/bindings-typescript/src/solid/useProcedure.ts +++ b/crates/bindings-typescript/src/solid/useProcedure.ts @@ -1,10 +1,10 @@ import { createEffect } from 'solid-js'; -import type { UntypedProcedureDef } from '../sdk/procedures'; -import { useSpacetimeDB } from './useSpacetimeDB'; +import type { UntypedProcedureDef } from '../sdk/procedures.ts'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; import type { ProcedureParamsType, ProcedureReturnType, -} from '../sdk/type_utils'; +} from '../sdk/type_utils.ts'; export function useProcedure( procedureDef: ProcedureDef diff --git a/crates/bindings-typescript/src/solid/useReducer.ts b/crates/bindings-typescript/src/solid/useReducer.ts index c9a6968f4b1..c74333387c4 100644 --- a/crates/bindings-typescript/src/solid/useReducer.ts +++ b/crates/bindings-typescript/src/solid/useReducer.ts @@ -1,7 +1,7 @@ import { createEffect } from 'solid-js'; -import type { UntypedReducerDef } from '../sdk/reducers'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import type { ParamsType } from '../sdk'; +import type { UntypedReducerDef } from '../sdk/reducers.ts'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import type { ParamsType } from '../sdk/index.ts'; export function useReducer( reducerDef: ReducerDef diff --git a/crates/bindings-typescript/src/solid/useSpacetimeDB.ts b/crates/bindings-typescript/src/solid/useSpacetimeDB.ts index 4eed7c2d41c..d9665de159e 100644 --- a/crates/bindings-typescript/src/solid/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/solid/useSpacetimeDB.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'solid-js'; -import type { ConnectionState } from './connection_state'; +import type { ConnectionState } from './connection_state.ts'; export const SpacetimeDBContext = createContext( undefined diff --git a/crates/bindings-typescript/src/solid/useTable.ts b/crates/bindings-typescript/src/solid/useTable.ts index 70e4064b971..b0fa5274fc0 100644 --- a/crates/bindings-typescript/src/solid/useTable.ts +++ b/crates/bindings-typescript/src/solid/useTable.ts @@ -1,9 +1,9 @@ import { createSignal, onCleanup, createMemo, createComputed } from 'solid-js'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import { type EventContextInterface } from '../sdk/db_connection_impl'; -import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -import type { RowType, UntypedTableDef } from '../lib/table'; -import type { Prettify } from '../lib/type_util'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import { type EventContextInterface } from '../sdk/db_connection_impl.ts'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module.ts'; +import type { RowType, UntypedTableDef } from '../lib/table.ts'; +import type { Prettify } from '../lib/type_util.ts'; import { type Query, type BooleanExpr, @@ -11,7 +11,7 @@ import { evaluateBooleanExpr, getQueryAccessorName, getQueryWhereClause, -} from '../lib/query'; +} from '../lib/query.ts'; import { createStore, reconcile } from 'solid-js/store'; export interface UseTableCallbacks { @@ -69,7 +69,7 @@ export function useTable( const whereExpr = createMemo(() => getQueryWhereClause(q())); const querySql = createMemo(() => toSql(q())); - let connectionState: import('./connection_state').ConnectionState; + let connectionState: import('./connection_state.ts').ConnectionState; try { connectionState = useSpacetimeDB(); } catch { diff --git a/crates/bindings-typescript/src/svelte/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/svelte/SpacetimeDBProvider.ts index ea7521384c9..20480c500ef 100644 --- a/crates/bindings-typescript/src/svelte/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/svelte/SpacetimeDBProvider.ts @@ -3,16 +3,16 @@ import { writable, type Writable } from 'svelte/store'; import { DbConnectionBuilder, type DbConnectionImpl, -} from '../sdk/db_connection_impl'; -import { ConnectionId } from '../lib/connection_id'; +} from '../sdk/db_connection_impl.ts'; +import { ConnectionId } from '../lib/connection_id.ts'; import { ConnectionManager, type ConnectionState as ManagerConnectionState, -} from '../sdk/connection_manager'; +} from '../sdk/connection_manager.ts'; import { SPACETIMEDB_CONTEXT_KEY, type ConnectionState, -} from './connection_state'; +} from './connection_state.ts'; /** * Establish a SpacetimeDB connection for the current component subtree and make diff --git a/crates/bindings-typescript/src/svelte/connection_state.ts b/crates/bindings-typescript/src/svelte/connection_state.ts index 502ec666d4b..03a81ecd862 100644 --- a/crates/bindings-typescript/src/svelte/connection_state.ts +++ b/crates/bindings-typescript/src/svelte/connection_state.ts @@ -1,8 +1,8 @@ import type { DbConnectionBuilder, DbConnectionImpl, -} from '../sdk/db_connection_impl'; -import type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager'; +} from '../sdk/db_connection_impl.ts'; +import type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager.ts'; export const SPACETIMEDB_CONTEXT_KEY = Symbol('spacetimedb'); diff --git a/crates/bindings-typescript/src/svelte/useReducer.ts b/crates/bindings-typescript/src/svelte/useReducer.ts index 236cc8c5faa..81f7e9516fd 100644 --- a/crates/bindings-typescript/src/svelte/useReducer.ts +++ b/crates/bindings-typescript/src/svelte/useReducer.ts @@ -1,9 +1,9 @@ import { onDestroy } from 'svelte'; import { get } from 'svelte/store'; -import type { InferTypeOfParams } from '../lib/type_builders'; -import type { UntypedReducerDef } from '../sdk/reducers'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import type { Prettify } from '../lib/type_util'; +import type { InferTypeOfParams } from '../lib/type_builders.ts'; +import type { UntypedReducerDef } from '../sdk/reducers.ts'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import type { Prettify } from '../lib/type_util.ts'; type IsEmptyObject = [keyof T] extends [never] ? true : false; type MaybeParams = IsEmptyObject extends true ? [] : [params: T]; diff --git a/crates/bindings-typescript/src/svelte/useSpacetimeDB.ts b/crates/bindings-typescript/src/svelte/useSpacetimeDB.ts index 899c23c4f5f..af74990e85c 100644 --- a/crates/bindings-typescript/src/svelte/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/svelte/useSpacetimeDB.ts @@ -3,7 +3,7 @@ import type { Writable } from 'svelte/store'; import { SPACETIMEDB_CONTEXT_KEY, type ConnectionState, -} from './connection_state'; +} from './connection_state.ts'; // Throws an error if used outside of a SpacetimeDBProvider export function useSpacetimeDB(): Writable { diff --git a/crates/bindings-typescript/src/svelte/useTable.ts b/crates/bindings-typescript/src/svelte/useTable.ts index c2e15602e9b..4394e6bd2a8 100644 --- a/crates/bindings-typescript/src/svelte/useTable.ts +++ b/crates/bindings-typescript/src/svelte/useTable.ts @@ -1,10 +1,10 @@ import { onDestroy } from 'svelte'; import { writable, get, type Readable } from 'svelte/store'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import type { EventContextInterface } from '../sdk/db_connection_impl'; -import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -import type { RowType, UntypedTableDef } from '../lib/table'; -import type { Prettify } from '../lib/type_util'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import type { EventContextInterface } from '../sdk/db_connection_impl.ts'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module.ts'; +import type { RowType, UntypedTableDef } from '../lib/table.ts'; +import type { Prettify } from '../lib/type_util.ts'; import { type BooleanExpr, evaluateBooleanExpr, @@ -12,7 +12,7 @@ import { getQueryWhereClause, type Query, toSql, -} from '../lib/query'; +} from '../lib/query.ts'; export interface UseTableCallbacks { onInsert?: (row: RowType) => void; diff --git a/crates/bindings-typescript/src/tanstack/SpacetimeDBQueryClient.ts b/crates/bindings-typescript/src/tanstack/SpacetimeDBQueryClient.ts index 4bce2812e52..e5303aad41d 100644 --- a/crates/bindings-typescript/src/tanstack/SpacetimeDBQueryClient.ts +++ b/crates/bindings-typescript/src/tanstack/SpacetimeDBQueryClient.ts @@ -10,7 +10,7 @@ import { evaluateBooleanExpr, getQueryAccessorName, getQueryWhereClause, -} from '../lib/query'; +} from '../lib/query.ts'; type QueryInput = Query; diff --git a/crates/bindings-typescript/src/tanstack/hooks.ts b/crates/bindings-typescript/src/tanstack/hooks.ts index f8eb2c8decd..e31d4aedcf9 100644 --- a/crates/bindings-typescript/src/tanstack/hooks.ts +++ b/crates/bindings-typescript/src/tanstack/hooks.ts @@ -5,9 +5,9 @@ import type { UseSuspenseQueryOptions, UseSuspenseQueryResult, } from '@tanstack/react-query'; -import type { UntypedTableDef, RowType } from '../lib/table'; -import type { Query } from '../lib/query'; -import { spacetimeDBQuery } from './SpacetimeDBQueryClient'; +import type { UntypedTableDef, RowType } from '../lib/table.ts'; +import type { Query } from '../lib/query.ts'; +import { spacetimeDBQuery } from './SpacetimeDBQueryClient.ts'; export type UseSpacetimeDBQueryResult = [ T[], diff --git a/crates/bindings-typescript/src/tanstack/index.ts b/crates/bindings-typescript/src/tanstack/index.ts index e74fcf71cfb..f90660992d9 100644 --- a/crates/bindings-typescript/src/tanstack/index.ts +++ b/crates/bindings-typescript/src/tanstack/index.ts @@ -3,14 +3,14 @@ export { spacetimeDBQuery, type SpacetimeDBQueryOptions, type SpacetimeDBQueryOptionsSkipped, -} from './SpacetimeDBQueryClient'; +} from './SpacetimeDBQueryClient.ts'; export { useSpacetimeDBQuery, useSpacetimeDBSuspenseQuery, type UseSpacetimeDBQueryResult, type UseSpacetimeDBSuspenseQueryResult, -} from './hooks'; -export * from '../react/SpacetimeDBProvider'; -export { useSpacetimeDB } from '../react/useSpacetimeDB'; -export { useReducer } from '../react/useReducer'; -export { useProcedure } from '../react/useProcedure'; +} from './hooks.ts'; +export * from '../react/SpacetimeDBProvider.ts'; +export { useSpacetimeDB } from '../react/useSpacetimeDB.ts'; +export { useReducer } from '../react/useReducer.ts'; +export { useProcedure } from '../react/useProcedure.ts'; diff --git a/crates/bindings-typescript/src/vue/SpacetimeDBProvider.ts b/crates/bindings-typescript/src/vue/SpacetimeDBProvider.ts index fffd3091d43..979f1a48f85 100644 --- a/crates/bindings-typescript/src/vue/SpacetimeDBProvider.ts +++ b/crates/bindings-typescript/src/vue/SpacetimeDBProvider.ts @@ -12,12 +12,12 @@ import { type DbConnectionImpl, type ErrorContextInterface, type RemoteModuleOf, -} from '../sdk/db_connection_impl'; -import { ConnectionId } from '../lib/connection_id'; +} from '../sdk/db_connection_impl.ts'; +import { ConnectionId } from '../lib/connection_id.ts'; import { SPACETIMEDB_INJECTION_KEY, type ConnectionState, -} from './connection_state'; +} from './connection_state.ts'; export interface SpacetimeDBProviderProps< DbConnection extends DbConnectionImpl, diff --git a/crates/bindings-typescript/src/vue/connection_state.ts b/crates/bindings-typescript/src/vue/connection_state.ts index aded21a0c52..05b10c2a1d3 100644 --- a/crates/bindings-typescript/src/vue/connection_state.ts +++ b/crates/bindings-typescript/src/vue/connection_state.ts @@ -1,7 +1,7 @@ import type { InjectionKey } from 'vue'; -import type { ConnectionId } from '../lib/connection_id'; -import type { Identity } from '../lib/identity'; -import type { DbConnectionImpl } from '../sdk/db_connection_impl'; +import type { ConnectionId } from '../lib/connection_id.ts'; +import type { Identity } from '../lib/identity.ts'; +import type { DbConnectionImpl } from '../sdk/db_connection_impl.ts'; export interface ConnectionState { isActive: boolean; diff --git a/crates/bindings-typescript/src/vue/useProcedure.ts b/crates/bindings-typescript/src/vue/useProcedure.ts index 385b0c96ae8..f4025d3735f 100644 --- a/crates/bindings-typescript/src/vue/useProcedure.ts +++ b/crates/bindings-typescript/src/vue/useProcedure.ts @@ -1,10 +1,10 @@ import { shallowRef, watch, onUnmounted } from 'vue'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import type { UntypedProcedureDef } from '../sdk/procedures'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import type { UntypedProcedureDef } from '../sdk/procedures.ts'; import type { ProcedureParamsType, ProcedureReturnType, -} from '../sdk/type_utils'; +} from '../sdk/type_utils.ts'; export function useProcedure( procedureDef: ProcedureDef diff --git a/crates/bindings-typescript/src/vue/useReducer.ts b/crates/bindings-typescript/src/vue/useReducer.ts index b64ef2324ed..0c4a814da47 100644 --- a/crates/bindings-typescript/src/vue/useReducer.ts +++ b/crates/bindings-typescript/src/vue/useReducer.ts @@ -1,7 +1,7 @@ import { shallowRef, watch, onUnmounted } from 'vue'; -import { useSpacetimeDB } from './useSpacetimeDB'; -import type { UntypedReducerDef } from '../sdk/reducers'; -import type { ParamsType } from '../sdk'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; +import type { UntypedReducerDef } from '../sdk/reducers.ts'; +import type { ParamsType } from '../sdk/index.ts'; export function useReducer( reducerDef: ReducerDef diff --git a/crates/bindings-typescript/src/vue/useSpacetimeDB.ts b/crates/bindings-typescript/src/vue/useSpacetimeDB.ts index 1d80fa8f3a3..01e0ea69b6e 100644 --- a/crates/bindings-typescript/src/vue/useSpacetimeDB.ts +++ b/crates/bindings-typescript/src/vue/useSpacetimeDB.ts @@ -2,7 +2,7 @@ import { inject } from 'vue'; import { SPACETIMEDB_INJECTION_KEY, type ConnectionState, -} from './connection_state'; +} from './connection_state.ts'; export function useSpacetimeDB(): ConnectionState { const context = inject(SPACETIMEDB_INJECTION_KEY); diff --git a/crates/bindings-typescript/src/vue/useTable.ts b/crates/bindings-typescript/src/vue/useTable.ts index c0f6fa63848..cd52f3d38cb 100644 --- a/crates/bindings-typescript/src/vue/useTable.ts +++ b/crates/bindings-typescript/src/vue/useTable.ts @@ -7,12 +7,12 @@ import { type DeepReadonly, type Ref, } from 'vue'; -import { useSpacetimeDB } from './useSpacetimeDB'; +import { useSpacetimeDB } from './useSpacetimeDB.ts'; -import type { EventContextInterface } from '../sdk/db_connection_impl'; -import type { UntypedRemoteModule } from '../sdk/spacetime_module'; -import type { RowType, UntypedTableDef } from '../lib/table'; -import type { Prettify } from '../lib/type_util'; +import type { EventContextInterface } from '../sdk/db_connection_impl.ts'; +import type { UntypedRemoteModule } from '../sdk/spacetime_module.ts'; +import type { RowType, UntypedTableDef } from '../lib/table.ts'; +import type { Prettify } from '../lib/type_util.ts'; import { type Query, type BooleanExpr, @@ -20,7 +20,7 @@ import { evaluateBooleanExpr, getQueryAccessorName, getQueryWhereClause, -} from '../lib/query'; +} from '../lib/query.ts'; export interface UseTableCallbacks { onInsert?: (row: RowType) => void; 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 5a5cc759b17..cf33870746c 100644 --- a/crates/bindings-typescript/test-app/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-app/src/module_bindings/index.ts @@ -249,10 +249,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< /** Builder class to configure a new database connection to the remote SpacetimeDB instance. */ export class DbConnectionBuilder extends __DbConnectionBuilder {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; 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 5a7a64d1e2b..dae611869c6 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 @@ -227,10 +227,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< /** Builder class to configure a new database connection to the remote SpacetimeDB instance. */ export class DbConnectionBuilder extends __DbConnectionBuilder {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts b/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts index 5a7a64d1e2b..dae611869c6 100644 --- a/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts +++ b/crates/bindings-typescript/test-solid-router/src/module_bindings/index.ts @@ -227,10 +227,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< /** Builder class to configure a new database connection to the remote SpacetimeDB instance. */ export class DbConnectionBuilder extends __DbConnectionBuilder {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/crates/bindings-typescript/tests/connection_manager_liveness.test.ts b/crates/bindings-typescript/tests/connection_manager_liveness.test.ts index 5c2e1e75c98..fabb9436824 100644 --- a/crates/bindings-typescript/tests/connection_manager_liveness.test.ts +++ b/crates/bindings-typescript/tests/connection_manager_liveness.test.ts @@ -99,6 +99,10 @@ class MockBuilder { return this; } + hasTokenProvider(): boolean { + return false; + } + build(): MockConnection { const connection = new MockConnection(); connection.token = this.token; diff --git a/crates/bindings-typescript/tests/connection_manager_reconnect.test.ts b/crates/bindings-typescript/tests/connection_manager_reconnect.test.ts index ee981ccf2f9..806119974eb 100644 --- a/crates/bindings-typescript/tests/connection_manager_reconnect.test.ts +++ b/crates/bindings-typescript/tests/connection_manager_reconnect.test.ts @@ -1,10 +1,14 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; -import { ConnectionId } from '../src'; +import { ConnectionId, type TokenProvider } from '../src'; +import { ServerMessage } from '../src/sdk/client_api/types.ts'; import { CONNECTION_MANAGER_RECONNECT_MAX_DELAY_MS, connectionManagerReconnectDelayMs, ConnectionManager, } from '../src/sdk/connection_manager.ts'; +import WebsocketTestAdapter from '../src/sdk/websocket_test_adapter.ts'; +import { DbConnection } from '../test-app/src/module_bindings/index.ts'; +import { anIdentity } from './utils.ts'; type ErrorContextInterface = { isActive: boolean; @@ -144,6 +148,10 @@ class MockBuilder { return this; } + hasTokenProvider(): boolean { + return false; + } + build(): MockConnection { const connection = new MockConnection(this.token); this.buildCount += 1; @@ -809,3 +817,83 @@ describe('ConnectionManager session continuity across rebuilds', () => { ConnectionManager.release(key); }); }); + +// These use a real `DbConnection` because the provider is resolved inside it, +// per connection attempt. +describe('ConnectionManager with a token provider', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.runOnlyPendingTimers(); + vi.useRealTimers(); + }); + + function providerBuilder(provider: TokenProvider) { + const sockets: WebsocketTestAdapter[] = []; + const authTokens: (string | undefined)[] = []; + const builder = DbConnection.builder() + .withUri('ws://127.0.0.1:1234') + .withDatabaseName('db') + .withToken(provider) + .withWSFn(args => { + authTokens.push(args.authToken); + const socket = new WebsocketTestAdapter(); + sockets.push(socket); + return socket.openWebSocket(args); + }); + return { builder, sockets, authTokens }; + } + + test('asks the provider again on reconnect instead of resuming its last token', async () => { + const key = nextKey(); + let issued = 0; + const { builder, sockets, authTokens } = providerBuilder( + async () => `jwt-${++issued}` + ); + + ConnectionManager.retain(key, builder); + await vi.advanceTimersByTimeAsync(0); + sockets[0].acceptConnection(); + sockets[0].sendToClient( + ServerMessage.InitialConnection({ + identity: anIdentity, + token: 'jwt-1', + connectionId: ConnectionId.random(), + }) + ); + expect(ConnectionManager.getSnapshot(key)?.token).toBe('jwt-1'); + + // By now the short-lived token may have expired; the rebuild must not + // resume it. + sockets[0].close(); + await vi.advanceTimersByTimeAsync(connectionManagerReconnectDelayMs(0)); + + expect(authTokens).toEqual(['jwt-1', 'jwt-2']); + + ConnectionManager.release(key); + }); + + test('retries after the provider rejects', async () => { + const key = nextKey(); + const provider = vi + .fn() + .mockRejectedValueOnce(new Error('token fetch failed')) + .mockResolvedValue('jwt'); + const { builder, authTokens } = providerBuilder(provider); + + ConnectionManager.retain(key, builder); + await vi.advanceTimersByTimeAsync(0); + expect(ConnectionManager.getSnapshot(key)?.connectionError?.message).toBe( + 'token fetch failed' + ); + expect(authTokens).toEqual([]); + + await vi.advanceTimersByTimeAsync(connectionManagerReconnectDelayMs(0)); + expect(provider).toHaveBeenCalledTimes(2); + expect(authTokens).toEqual(['jwt']); + + ConnectionManager.release(key); + }); +}); diff --git a/crates/bindings-typescript/tests/consumer/app.ts b/crates/bindings-typescript/tests/consumer/app.ts new file mode 100644 index 00000000000..3eba0ddff36 --- /dev/null +++ b/crates/bindings-typescript/tests/consumer/app.ts @@ -0,0 +1,18 @@ +// A strict app on generated bindings that import the built package, typechecked +// by tests/consumer_types.test.ts. Generated schemas must keep satisfying the +// SDK's own constraints under exactOptionalPropertyTypes. +import type { TokenProvider } from 'spacetimedb'; +import { DbConnection } from '../../case-conversion-test-client/src/module_bindings'; + +const conn = DbConnection.builder() + .withUri('ws://localhost:3000') + .withDatabaseName('test') + .withToken(async () => 'token') + .build(); + +export const created: Promise = conn.reducers.createPlayer1({ + player1Name: 'player', + start2Level: 1, +}); +export const players = [...conn.db.player1.iter()]; +export const provider: TokenProvider = async () => undefined; diff --git a/crates/bindings-typescript/tests/consumer/tsconfig.json b/crates/bindings-typescript/tests/consumer/tsconfig.json new file mode 100644 index 00000000000..dc47f1ddf4d --- /dev/null +++ b/crates/bindings-typescript/tests/consumer/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "exactOptionalPropertyTypes": true, + "skipLibCheck": true, + "noEmit": true, + "target": "ES2022", + "lib": ["ES2022", "DOM"], + "module": "ESNext", + "moduleResolution": "Bundler" + }, + "files": ["app.ts"] +} diff --git a/crates/bindings-typescript/tests/consumer_types.test.ts b/crates/bindings-typescript/tests/consumer_types.test.ts new file mode 100644 index 00000000000..db0d708d217 --- /dev/null +++ b/crates/bindings-typescript/tests/consumer_types.test.ts @@ -0,0 +1,16 @@ +import { spawnSync } from 'node:child_process'; +import { createRequire } from 'node:module'; +import { fileURLToPath } from 'node:url'; +import { expect, test } from 'vitest'; + +// Checks the built `dist` typings, so run `pnpm build` first (CI does). +test('generated bindings typecheck in a strict consumer', () => { + const tsc = createRequire(import.meta.url).resolve('typescript/bin/tsc'); + const project = fileURLToPath( + new URL('./consumer/tsconfig.json', import.meta.url) + ); + const result = spawnSync(process.execPath, [tsc, '-p', project], { + encoding: 'utf8', + }); + expect(result.stdout + result.stderr).toBe(''); +}, 60_000); diff --git a/crates/bindings-typescript/tests/db_connection.test.ts b/crates/bindings-typescript/tests/db_connection.test.ts index 9e340637eb8..6971525f4b8 100644 --- a/crates/bindings-typescript/tests/db_connection.test.ts +++ b/crates/bindings-typescript/tests/db_connection.test.ts @@ -213,6 +213,40 @@ describe('DbConnection', () => { expect(client.isActive).toBe(false); }); + test('reports a server message it cannot apply through onDisconnect instead of throwing', async () => { + const onDisconnectPromise = new Deferred(); + const wsAdapter = new WebsocketTestAdapter(); + + const client = DbConnection.builder() + .withUri('ws://127.0.0.1:1234') + .withDatabaseName('db') + .withWSFn(wsAdapter.openWebSocket) + .onConnect(() => { + throw new Error('callback failed'); + }) + .onDisconnect((_ctx, error) => onDisconnectPromise.resolve(error)) + .build(); + + await client['wsPromise']; + wsAdapter.acceptConnection(); + // Node rethrows errors from WebSocket listeners on the next tick, which + // crashes the host process. + expect(() => + wsAdapter.sendToClient( + ServerMessage.InitialConnection({ + identity: anIdentity, + token: 'a-token', + connectionId: ConnectionId.random(), + }) + ) + ).not.toThrow(); + + expect((await onDisconnectPromise.promise)?.message).toBe( + 'callback failed' + ); + expect(client.isActive).toBe(false); + }); + test('marks disconnect as requested when disconnect() is called', async () => { const onDisconnectPromise = new Deferred(); const wsAdapter = new WebsocketTestAdapter(); @@ -338,6 +372,52 @@ describe('DbConnection', () => { expect(called).toBeTruthy(); }); + test.each([ + ['sync', (token: string) => token], + ['async', async (token: string) => token], + ])( + 'asks a %s token provider for a fresh token on every build', + async (_kind, provide) => { + let issued = 0; + const authTokens: (string | undefined)[] = []; + const builder = DbConnection.builder() + .withUri('ws://127.0.0.1:1234') + .withDatabaseName('db') + .withToken(() => provide(`token-${++issued}`)) + .withWSFn(args => { + authTokens.push(args.authToken); + return new WebsocketTestAdapter().openWebSocket(args); + }); + + const first = builder.build(); + await first['wsPromise']; + const second = builder.build(); + await second['wsPromise']; + + expect(authTokens).toEqual(['token-1', 'token-2']); + expect(first.token).toBe('token-1'); + expect(second.token).toBe('token-2'); + } + ); + + test('reports a throwing token provider through onConnectError', async () => { + let connectError: Error | undefined; + const client = DbConnection.builder() + .withUri('ws://127.0.0.1:1234') + .withDatabaseName('db') + .withToken(() => { + throw new Error('token fetch failed'); + }) + .withWSFn(new WebsocketTestAdapter().openWebSocket) + .onConnectError((_ctx, error) => { + connectError = error; + }) + .build(); + + await client['wsPromise']; + expect(connectError?.message).toBe('token fetch failed'); + }); + test('batches same-tick reducer calls when v3 is negotiated', async () => { const wsAdapter = new WebsocketTestAdapter(); const client = DbConnection.builder() diff --git a/crates/client-api/src/auth.rs b/crates/client-api/src/auth.rs index 729d001e2c3..9f40fd4f2ae 100644 --- a/crates/client-api/src/auth.rs +++ b/crates/client-api/src/auth.rs @@ -206,15 +206,23 @@ impl SpacetimeAuth { ) } - // Sign a new token with the same claims and a new expiry. + // Sign a short-lived copy of this token that we will be able to verify. // Note that this will not change the issuer, so the private_key might not match. - // We do this to create short-lived tokens that we will be able to verify. + // The copy keeps the original `iat` and expires within `expiry`, but never after the + // original: re-signing a copy can't renew a token forever, and modules can still tell + // when it was issued. pub fn re_sign_with_expiry( &self, signer: &impl TokenSigner, expiry: Duration, ) -> Result<(SpacetimeIdentityClaims, String), JwtError> { - TokenClaims::from(self.clone()).encode_and_sign_with_expiry(signer, Some(expiry)) + let cap = SystemTime::now() + expiry; + let claims = SpacetimeIdentityClaims { + exp: Some(self.claims.exp.map_or(cap, |exp| exp.min(cap))), + ..self.claims.clone() + }; + let token = signer.sign(&claims)?; + Ok((claims, token)) } } @@ -376,6 +384,35 @@ mod tests { Ok(()) } + // A re-signed copy keeps the original `iat` and never outlives the original token. + #[tokio::test] + async fn re_sign_never_extends_a_token() -> Result<(), anyhow::Error> { + let kp = JwtKeys::generate()?; + let claims = TokenClaims::new("localhost".into(), "test-subject".into()); + let re_sign = |token: String, claims| { + crate::auth::SpacetimeAuth::new(SpacetimeCreds::from_signed_token(token), claims)? + .re_sign_with_expiry(&kp.private, std::time::Duration::from_secs(60)) + .map_err(|e| anyhow!("{e}")) + }; + + let (_, token) = claims.encode_and_sign_with_expiry(&kp.private, Some(std::time::Duration::from_secs(30)))?; + let original = kp.public.validate_token(&token).await?; + let (_, copy) = re_sign(token, original.clone())?; + let copy = kp.public.validate_token(©).await?; + assert_eq!(copy.iat, original.iat); + assert_eq!(copy.exp, original.exp); + + // A token without an expiry gets one, 60 seconds out. + let (_, token) = claims.encode_and_sign(&kp.private)?; + let original = kp.public.validate_token(&token).await?; + let (_, copy) = re_sign(token, original.clone())?; + let copy = kp.public.validate_token(©).await?; + assert_eq!(copy.iat, original.iat); + let exp = copy.exp.ok_or_else(|| anyhow!("no exp"))?; + assert!(exp > std::time::SystemTime::now() + std::time::Duration::from_secs(50)); + Ok(()) + } + #[tokio::test] async fn authorization_rejection_custom_uses_display_message() -> Result<(), anyhow::Error> { let response = diff --git a/crates/client-api/src/routes/identity.rs b/crates/client-api/src/routes/identity.rs index fa42b460561..dfaf73ff779 100644 --- a/crates/client-api/src/routes/identity.rs +++ b/crates/client-api/src/routes/identity.rs @@ -96,7 +96,8 @@ pub struct WebsocketTokenResponse { pub token: String, } -// This endpoint takes a token from a client and sends a newly signed token with a 60s expiry. +// This endpoint takes a token from a client and sends a newly signed copy that expires within +// 60s, never after the original. // Note that even if the token has a different issuer, we will sign it with our key. // This is ok because `FullTokenValidator` checks if we signed the token before worrying about the issuer. pub async fn create_websocket_token( diff --git a/crates/codegen/examples/regen-typescript-moduledef.rs b/crates/codegen/examples/regen-typescript-moduledef.rs index a3fb1b30f76..faa800365d6 100644 --- a/crates/codegen/examples/regen-typescript-moduledef.rs +++ b/crates/codegen/examples/regen-typescript-moduledef.rs @@ -49,7 +49,7 @@ fn main() -> anyhow::Result<()> { if filename.starts_with("types/") { return Ok(()); } - let code = regex_replace!(&code, r#"from "spacetimedb";"#, r#"from "../../lib/type_builders";"#); + let code = regex_replace!(&code, r#"from "spacetimedb";"#, r#"from "../../lib/type_builders.ts";"#); // Elide types which are related to client-side only things let code = regex_replace!(&code, r"type CallReducerFlags as __CallReducerFlags,", r""); diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 2706949d05b..226ecb0fcc8 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -759,6 +759,16 @@ impl Lang for TypeScript { ); writeln!(out); + if has_table_accessor_aliases { + // Narrow `db` by merging an interface into the class rather than with a + // `declare` field, which Babel (React Native, Expo) rejects by default. + writeln!(out, "export interface DbConnection {{"); + out.indent(1); + writeln!(out, "db: DbView;"); + out.dedent(1); + writeln!(out, "}}"); + writeln!(out); + } writeln!(out, "/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */"); writeln!( out, @@ -766,9 +776,6 @@ impl Lang for TypeScript { ); out.indent(1); if has_table_accessor_aliases { - writeln!(out, "declare db: DbView;"); - - writeln!(out); writeln!( out, "constructor(config: __DbConnectionConfig) {{" diff --git a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap index 80b88f79b73..e32448cbd40 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap @@ -409,10 +409,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/demo/Blackholio/client-ts/src/module_bindings/index.ts b/demo/Blackholio/client-ts/src/module_bindings/index.ts index 81afe450da0..0e7f3e8df74 100644 --- a/demo/Blackholio/client-ts/src/module_bindings/index.ts +++ b/demo/Blackholio/client-ts/src/module_bindings/index.ts @@ -219,10 +219,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/docs/docs/00200-core-concepts/00500-authentication/00300-Clerk.md b/docs/docs/00200-core-concepts/00500-authentication/00300-Clerk.md index 13df43bf929..f75228cedc3 100644 --- a/docs/docs/00200-core-concepts/00500-authentication/00300-Clerk.md +++ b/docs/docs/00200-core-concepts/00500-authentication/00300-Clerk.md @@ -211,8 +211,10 @@ createRoot(document.getElementById('root')!).render( Update your `App.tsx` file to: -1. Read the Clerk token via `useClerkToken`. -2. Pass it to the `DbConnection` builder using `.withToken(...)`. +1. Get Clerk's `getToken` function via `useAuth`. +2. Pass the `DbConnection` builder a function that calls it, using `.withToken(...)`. + +Clerk session tokens expire after about a minute, so pass a function rather than a token string: the SDK calls it on every connection attempt, including automatic reconnects, and so always authenticates with a fresh token. This mirrors the Auth0 flow: SpacetimeDB receives a bearer token (JWT) and can validate it server-side. @@ -220,10 +222,10 @@ This mirrors the Auth0 flow: SpacetimeDB receives a bearer token (JWT) and can v ```tsx import { useMemo } from 'react'; +import { useAuth } from '@clerk/clerk-react'; import { Identity } from 'spacetimedb'; import { SpacetimeDBProvider } from 'spacetimedb/react'; import { DbConnection, ErrorContext } from './module_bindings'; -import { useClerkToken } from './ClerkTokenProvider'; const onConnect = (_conn: DbConnection, identity: Identity) => { console.log( @@ -241,17 +243,17 @@ const onConnectError = (_ctx: ErrorContext, err: Error) => { }; export default function App() { - const token = useClerkToken(); + const { getToken } = useAuth(); const connectionBuilder = useMemo(() => { return DbConnection.builder() .withUri('') .withDatabaseName('') - .withToken(token) + .withToken(async () => (await getToken()) ?? undefined) .onConnect(onConnect) .onDisconnect(onDisconnect) .onConnectError(onConnectError); - }, [token]); + }, [getToken]); return ( @@ -296,7 +298,7 @@ export function Header() { You are now set up to use **Clerk** authentication in your React application. When users access your app, they will be redirected to Clerk for sign-in, a session token (JWT) will be retrieved in the browser, and that token will be used to authenticate your SpacetimeDB connection. -If you are using **Clerk JWT templates** (recommended for controlling claims/audience/issuer), update the token retrieval line to: +If you are using **Clerk JWT templates** (recommended for controlling claims/audience/issuer), update both `getToken()` calls to: ```ts await getToken({ template: '' }); diff --git a/eslint.config.js b/eslint.config.js index ffe5c709e54..da1735a01ee 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -118,6 +118,31 @@ export default tseslint.config( ], }, }, + { + files: ['crates/bindings-typescript/src/**/*.{ts,tsx}'], + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: 'TSEnumDeclaration', + message: 'Do not use enums; stick to JS-compatible types.', + }, + { + selector: 'TSEnumDeclaration[const=true]', + message: 'Do not use const enum; use unions or objects.', + }, + { selector: 'Decorator', message: 'Do not use decorators.' }, + { + // tsc copies specifiers into the published .d.ts files verbatim, and + // NodeNext consumers cannot resolve extensionless relative imports. + selector: + ':matches(ImportDeclaration, ExportAllDeclaration, ExportNamedDeclaration, ImportExpression)[source.value=/^\\.(?!.*\\.(ts|tsx|js)$)/]', + message: + "Relative imports need an explicit extension (e.g. './foo.ts') so the published types resolve under NodeNext.", + }, + ], + }, + }, { files: ['templates/angular-ts/src/**/*.ts'], rules: { diff --git a/templates/hangman-react-ts/src/module_bindings/index.ts b/templates/hangman-react-ts/src/module_bindings/index.ts index 6852d40b6fe..625f44b14a7 100644 --- a/templates/hangman-react-ts/src/module_bindings/index.ts +++ b/templates/hangman-react-ts/src/module_bindings/index.ts @@ -259,10 +259,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< /** Builder class to configure a new database connection to the remote SpacetimeDB instance. */ export class DbConnectionBuilder extends __DbConnectionBuilder {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/templates/money-exchange-react-ts/src/module_bindings/index.ts b/templates/money-exchange-react-ts/src/module_bindings/index.ts index c4bf96e2c56..ee0cd17aa2f 100644 --- a/templates/money-exchange-react-ts/src/module_bindings/index.ts +++ b/templates/money-exchange-react-ts/src/module_bindings/index.ts @@ -229,10 +229,12 @@ export class SubscriptionBuilder extends __SubscriptionBuilderImpl< /** Builder class to configure a new database connection to the remote SpacetimeDB instance. */ export class DbConnectionBuilder extends __DbConnectionBuilder {} +export interface DbConnection { + db: DbView; +} + /** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */ export class DbConnection extends __DbConnectionImpl { - declare db: DbView; - constructor(config: __DbConnectionConfig) { super(config); this.db = __withTableAccessorAliases(this.db) as DbView; diff --git a/tools/generate-client-api/src/main.rs b/tools/generate-client-api/src/main.rs index 440e06cf6b9..e97c42f1760 100644 --- a/tools/generate-client-api/src/main.rs +++ b/tools/generate-client-api/src/main.rs @@ -48,8 +48,9 @@ fn run_capture(cmd: &str, args: &[&str]) -> Result { fn main() -> Result<()> { let out_dir = "src/sdk/client_api"; - let index_replacement = "../../index"; - let other_replacement = "../../lib/type_builders"; + // Explicit extensions keep the emitted .d.ts files resolvable under NodeNext. + let index_replacement = "../../index.ts"; + let other_replacement = "../../lib/type_builders.ts"; let workspace_dir = Path::new(env!("CARGO_MANIFEST_DIR")) .parent()