spacetimedb 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +759 -759
- package/README.md +211 -120
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.mjs.map +1 -1
- package/dist/browser/angular/index.mjs.map +1 -1
- package/dist/browser/react/index.mjs +129 -57
- package/dist/browser/react/index.mjs.map +1 -1
- package/dist/browser/solid/index.mjs +1933 -0
- package/dist/browser/solid/index.mjs.map +1 -0
- package/dist/browser/svelte/index.mjs.map +1 -1
- package/dist/browser/vue/index.mjs.map +1 -1
- package/dist/index.browser.mjs +10 -2
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +10 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +10 -2
- package/dist/index.mjs.map +1 -1
- package/dist/min/index.browser.mjs +1 -1
- package/dist/min/index.browser.mjs.map +1 -1
- package/dist/min/react/index.mjs +1 -1
- package/dist/min/react/index.mjs.map +1 -1
- package/dist/min/sdk/index.browser.mjs +1 -1
- package/dist/min/sdk/index.browser.mjs.map +1 -1
- package/dist/react/index.cjs +129 -57
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.mjs +129 -57
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useTable.d.ts.map +1 -1
- package/dist/sdk/connection_manager.d.ts +8 -0
- package/dist/sdk/connection_manager.d.ts.map +1 -1
- package/dist/sdk/db_connection_impl.d.ts +7 -0
- package/dist/sdk/db_connection_impl.d.ts.map +1 -1
- package/dist/sdk/index.browser.mjs +10 -2
- package/dist/sdk/index.browser.mjs.map +1 -1
- package/dist/sdk/index.cjs +10 -2
- package/dist/sdk/index.cjs.map +1 -1
- package/dist/sdk/index.mjs +10 -2
- package/dist/sdk/index.mjs.map +1 -1
- package/dist/sdk/websocket_test_adapter.d.ts +2 -1
- package/dist/sdk/websocket_test_adapter.d.ts.map +1 -1
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/solid/SpacetimeDBProvider.d.ts +7 -0
- package/dist/solid/SpacetimeDBProvider.d.ts.map +1 -0
- package/dist/solid/connection_state.d.ts +6 -0
- package/dist/solid/connection_state.d.ts.map +1 -0
- package/dist/solid/index.cjs +1939 -0
- package/dist/solid/index.cjs.map +1 -0
- package/dist/solid/index.d.ts +6 -0
- package/dist/solid/index.d.ts.map +1 -0
- package/dist/solid/index.mjs +1933 -0
- package/dist/solid/index.mjs.map +1 -0
- package/dist/solid/useProcedure.d.ts +4 -0
- package/dist/solid/useProcedure.d.ts.map +1 -0
- package/dist/solid/useReducer.d.ts +4 -0
- package/dist/solid/useReducer.d.ts.map +1 -0
- package/dist/solid/useSpacetimeDB.d.ts +4 -0
- package/dist/solid/useSpacetimeDB.d.ts.map +1 -0
- package/dist/solid/useTable.d.ts +32 -0
- package/dist/solid/useTable.d.ts.map +1 -0
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.mjs.map +1 -1
- package/dist/tanstack/index.cjs +120 -50
- package/dist/tanstack/index.cjs.map +1 -1
- package/dist/tanstack/index.mjs +120 -50
- package/dist/tanstack/index.mjs.map +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +13 -3
- package/src/angular/connection_state.ts +19 -19
- package/src/angular/index.ts +3 -3
- package/src/angular/injectors/index.ts +4 -4
- package/src/angular/injectors/inject-reducer.ts +62 -62
- package/src/angular/injectors/inject-spacetimedb-connected.ts +13 -13
- package/src/angular/injectors/inject-spacetimedb.ts +10 -10
- package/src/angular/injectors/inject-table.ts +234 -234
- package/src/angular/providers/index.ts +1 -1
- package/src/angular/providers/provide-spacetimedb.ts +96 -96
- package/src/index.ts +16 -16
- package/src/lib/algebraic_type.ts +819 -819
- package/src/lib/algebraic_type_variants.ts +26 -26
- package/src/lib/algebraic_value.ts +10 -10
- package/src/lib/autogen/types.ts +746 -746
- package/src/lib/binary_reader.ts +188 -188
- package/src/lib/binary_writer.ts +213 -213
- package/src/lib/connection_id.ts +102 -102
- package/src/lib/constraints.ts +48 -48
- package/src/lib/errors.ts +26 -26
- package/src/lib/filter.ts +195 -195
- package/src/lib/identity.ts +83 -83
- package/src/lib/indexes.ts +251 -251
- package/src/lib/option.ts +34 -34
- package/src/lib/query.ts +1019 -1019
- package/src/lib/reducer_schema.ts +38 -38
- package/src/lib/reducers.ts +116 -116
- package/src/lib/result.ts +36 -36
- package/src/lib/schedule_at.ts +86 -86
- package/src/lib/schema.ts +420 -420
- package/src/lib/table.ts +548 -548
- package/src/lib/table_schema.ts +64 -64
- package/src/lib/time_duration.ts +77 -77
- package/src/lib/timestamp.ts +148 -148
- package/src/lib/type_builders.test-d.ts +128 -128
- package/src/lib/type_builders.ts +4014 -4014
- package/src/lib/type_util.ts +124 -124
- package/src/lib/util.ts +196 -196
- package/src/lib/uuid.ts +337 -337
- package/src/react/SpacetimeDBProvider.ts +84 -84
- package/src/react/connection_state.ts +6 -6
- package/src/react/index.ts +5 -5
- package/src/react/useProcedure.ts +60 -60
- package/src/react/useReducer.ts +53 -53
- package/src/react/useSpacetimeDB.ts +18 -18
- package/src/react/useTable.ts +256 -251
- package/src/sdk/client_api/index.ts +114 -114
- package/src/sdk/client_api/types/procedures.ts +8 -8
- package/src/sdk/client_api/types/reducers.ts +8 -8
- package/src/sdk/client_api/types.ts +288 -288
- package/src/sdk/client_cache.ts +129 -129
- package/src/sdk/client_table.ts +179 -179
- package/src/sdk/connection_manager.ts +352 -237
- package/src/sdk/db_connection_builder.ts +290 -290
- package/src/sdk/db_connection_impl.ts +1356 -1347
- package/src/sdk/db_context.ts +28 -28
- package/src/sdk/db_view.ts +12 -12
- package/src/sdk/decompress.ts +51 -51
- package/src/sdk/event.ts +18 -18
- package/src/sdk/event_context.ts +51 -51
- package/src/sdk/event_emitter.ts +32 -32
- package/src/sdk/index.ts +14 -14
- package/src/sdk/internal.ts +2 -2
- package/src/sdk/json_api.ts +46 -46
- package/src/sdk/logger.ts +134 -134
- package/src/sdk/message_types.ts +46 -46
- package/src/sdk/procedures.ts +83 -83
- package/src/sdk/reducer_event.ts +20 -20
- package/src/sdk/reducer_handle.ts +12 -12
- package/src/sdk/reducers.ts +159 -159
- package/src/sdk/schema.ts +45 -45
- package/src/sdk/spacetime_module.ts +28 -28
- package/src/sdk/subscription_builder_impl.ts +275 -275
- package/src/sdk/table_cache.ts +581 -581
- package/src/sdk/type_utils.ts +19 -19
- package/src/sdk/version.ts +133 -133
- package/src/sdk/websocket_decompress_adapter.ts +63 -63
- package/src/sdk/websocket_protocols.ts +25 -25
- package/src/sdk/websocket_test_adapter.ts +107 -100
- package/src/sdk/websocket_v3_frames.ts +126 -126
- package/src/sdk/ws.ts +105 -105
- package/src/server/console.ts +81 -81
- package/src/server/db_view.ts +21 -21
- package/src/server/errors.ts +138 -138
- package/src/server/http.test-d.ts +80 -80
- package/src/server/http.ts +14 -14
- package/src/server/http_handlers.ts +413 -413
- package/src/server/http_internal.ts +79 -79
- package/src/server/http_shared.ts +186 -186
- package/src/server/index.ts +37 -37
- package/src/server/polyfills.ts +4 -4
- package/src/server/procedures.ts +239 -239
- package/src/server/query.ts +1 -1
- package/src/server/range.ts +53 -53
- package/src/server/reducers.ts +113 -113
- package/src/server/rng.ts +113 -113
- package/src/server/runtime.ts +1102 -1102
- package/src/server/schema.test-d.ts +99 -99
- package/src/server/schema.ts +663 -663
- package/src/server/sys.d.ts +125 -125
- package/src/server/view.test-d.ts +194 -194
- package/src/server/views.ts +340 -340
- package/src/solid/SpacetimeDBProvider.ts +97 -0
- package/src/solid/connection_state.ts +6 -0
- package/src/solid/index.ts +5 -0
- package/src/solid/useProcedure.ts +57 -0
- package/src/solid/useReducer.ts +50 -0
- package/src/solid/useSpacetimeDB.ts +18 -0
- package/src/solid/useTable.ts +203 -0
- package/src/svelte/SpacetimeDBProvider.ts +101 -101
- package/src/svelte/connection_state.ts +16 -16
- package/src/svelte/index.ts +4 -4
- package/src/svelte/useReducer.ts +61 -61
- package/src/svelte/useSpacetimeDB.ts +22 -22
- package/src/svelte/useTable.ts +218 -218
- package/src/tanstack/SpacetimeDBQueryClient.ts +330 -330
- package/src/tanstack/hooks.ts +83 -83
- package/src/tanstack/index.ts +16 -16
- package/src/util-stub.ts +1 -1
- package/src/vue/SpacetimeDBProvider.ts +157 -157
- package/src/vue/connection_state.ts +19 -19
- package/src/vue/index.ts +5 -5
- package/src/vue/useProcedure.ts +62 -62
- package/src/vue/useReducer.ts +55 -55
- package/src/vue/useSpacetimeDB.ts +18 -18
- package/src/vue/useTable.ts +229 -229
package/src/lib/table.ts
CHANGED
|
@@ -1,548 +1,548 @@
|
|
|
1
|
-
import type { ProcedureExport, ReducerExport, t } from '../server';
|
|
2
|
-
import type { errors } from '../server/errors';
|
|
3
|
-
import {
|
|
4
|
-
ExplicitNameEntry,
|
|
5
|
-
RawColumnDefaultValueV10,
|
|
6
|
-
RawConstraintDefV10,
|
|
7
|
-
RawIndexAlgorithm,
|
|
8
|
-
RawIndexDefV10,
|
|
9
|
-
RawSequenceDefV10,
|
|
10
|
-
RawTableDefV10,
|
|
11
|
-
} from './autogen/types';
|
|
12
|
-
import BinaryWriter from './binary_writer';
|
|
13
|
-
import type { AllUnique, ConstraintOpts } from './constraints';
|
|
14
|
-
import type {
|
|
15
|
-
ColumnIndex,
|
|
16
|
-
IndexColumns,
|
|
17
|
-
Indexes,
|
|
18
|
-
IndexOpts,
|
|
19
|
-
ReadonlyIndexes,
|
|
20
|
-
UntypedIndex,
|
|
21
|
-
} from './indexes';
|
|
22
|
-
import ScheduleAt from './schedule_at';
|
|
23
|
-
import type { TableSchema } from './table_schema';
|
|
24
|
-
import {
|
|
25
|
-
RowBuilder,
|
|
26
|
-
type ColumnBuilder,
|
|
27
|
-
type ColumnMetadata,
|
|
28
|
-
type InferTypeOfRow,
|
|
29
|
-
type RowObj,
|
|
30
|
-
type TypeBuilder,
|
|
31
|
-
} from './type_builders';
|
|
32
|
-
import type {
|
|
33
|
-
InvalidColumnMetadata,
|
|
34
|
-
Prettify,
|
|
35
|
-
ValidateColumnMetadata,
|
|
36
|
-
} from './type_util';
|
|
37
|
-
import { toPascalCase } from './util';
|
|
38
|
-
|
|
39
|
-
export type AlgebraicTypeRef = number;
|
|
40
|
-
type ColId = number;
|
|
41
|
-
type ColList = ColId[];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Check if any column in the row has invalid metadata.
|
|
45
|
-
*/
|
|
46
|
-
type HasInvalidColumn<Row extends RowObj> =
|
|
47
|
-
// this checks if Row exactly equals RowObj - if it does, we can't
|
|
48
|
-
// do type-system-level checking, so just let it pass
|
|
49
|
-
(<G>() => G extends Row ? 1 : 2) extends <G>() => G extends RowObj ? 1 : 2
|
|
50
|
-
? false
|
|
51
|
-
: {
|
|
52
|
-
[K in keyof Row]: Row[K] extends ColumnBuilder<any, any, infer M>
|
|
53
|
-
? ValidateColumnMetadata<M> extends InvalidColumnMetadata<any>
|
|
54
|
-
? true
|
|
55
|
-
: false
|
|
56
|
-
: false;
|
|
57
|
-
}[keyof Row] extends false
|
|
58
|
-
? false
|
|
59
|
-
: true;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Extract the names of columns that have invalid metadata.
|
|
63
|
-
*/
|
|
64
|
-
type InvalidColumnNames<Row extends RowObj> = {
|
|
65
|
-
[K in keyof Row]: Row[K] extends ColumnBuilder<any, any, infer M>
|
|
66
|
-
? ValidateColumnMetadata<M> extends InvalidColumnMetadata<any>
|
|
67
|
-
? K & string
|
|
68
|
-
: never
|
|
69
|
-
: never;
|
|
70
|
-
}[keyof Row];
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* A descriptive error type that surfaces the validation error.
|
|
74
|
-
* The type name itself contains the error message for better CLI output.
|
|
75
|
-
*/
|
|
76
|
-
type ERROR_default_cannot_be_combined_with_primaryKey_unique_or_autoInc<
|
|
77
|
-
InvalidColumns extends string,
|
|
78
|
-
> = {
|
|
79
|
-
_invalidColumns: InvalidColumns;
|
|
80
|
-
_fix: 'Remove either default() or the constraint (primaryKey/unique/autoInc) from these columns';
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* A helper type to extract the row type from a TableDef
|
|
85
|
-
*/
|
|
86
|
-
export type RowType<TableDef extends Pick<UntypedTableDef, 'columns'>> =
|
|
87
|
-
InferTypeOfRow<TableDef['columns']>;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Coerces a column which may be a TypeBuilder or ColumnBuilder into a ColumnBuilder
|
|
91
|
-
*/
|
|
92
|
-
export type CoerceColumn<
|
|
93
|
-
Col extends TypeBuilder<any, any> | ColumnBuilder<any, any, any>,
|
|
94
|
-
> =
|
|
95
|
-
Col extends TypeBuilder<infer T, infer U>
|
|
96
|
-
? ColumnBuilder<T, U, ColumnMetadata<any>>
|
|
97
|
-
: Col;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Coerces a RowObj where TypeBuilders are replaced with ColumnBuilders
|
|
101
|
-
*/
|
|
102
|
-
export type CoerceRow<Row extends RowObj> = {
|
|
103
|
-
[k in keyof Row & string]: CoerceColumn<Row[k]>;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Helper type to coerce an array of IndexOpts
|
|
108
|
-
*/
|
|
109
|
-
type CoerceArray<X extends IndexOpts<any>[]> = X;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* An untyped representation of a table's schema.
|
|
113
|
-
*/
|
|
114
|
-
export type UntypedTableDef = {
|
|
115
|
-
sourceName: string;
|
|
116
|
-
accessorName: string;
|
|
117
|
-
columns: Record<string, ColumnBuilder<any, any, ColumnMetadata<any>>>;
|
|
118
|
-
// This is really just a ProductType where all the elements have names.
|
|
119
|
-
rowType: RowBuilder<RowObj>['algebraicType']['value'];
|
|
120
|
-
/**
|
|
121
|
-
* Declarative multi-column indexes supplied by user code in `table({ indexes: [...] }, ...)`.
|
|
122
|
-
*
|
|
123
|
-
* This is intentionally the *declarative* shape (`IndexOpts`) because a lot of
|
|
124
|
-
* type-level behavior is derived from these entries (for example query-builder
|
|
125
|
-
* inference over composite indexes).
|
|
126
|
-
*/
|
|
127
|
-
indexes: readonly IndexOpts<any>[];
|
|
128
|
-
/**
|
|
129
|
-
* Fully-resolved runtime indexes materialized from `RawTableDefV10`.
|
|
130
|
-
*
|
|
131
|
-
* This contains both:
|
|
132
|
-
* 1) field-level indexes inferred from column metadata, and
|
|
133
|
-
* 2) explicit table-level indexes.
|
|
134
|
-
*
|
|
135
|
-
* Runtime consumers like `TableCacheImpl` should use this field instead of
|
|
136
|
-
* reinterpreting `indexes` as runtime index metadata.
|
|
137
|
-
*/
|
|
138
|
-
resolvedIndexes: readonly UntypedIndex<any>[];
|
|
139
|
-
constraints: readonly ConstraintOpts<any>[];
|
|
140
|
-
tableDef: RawTableDefV10;
|
|
141
|
-
isEvent?: boolean;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* A type representing the indexes defined on a table.
|
|
146
|
-
*/
|
|
147
|
-
export type TableIndexes<TableDef extends UntypedTableDef> = {
|
|
148
|
-
[K in keyof TableDef['columns'] & string as ColumnIndex<
|
|
149
|
-
K,
|
|
150
|
-
TableDef['columns'][K]['columnMetadata']
|
|
151
|
-
> extends never
|
|
152
|
-
? never
|
|
153
|
-
: K]: ColumnIndex<K, TableDef['columns'][K]['columnMetadata']>;
|
|
154
|
-
} & {
|
|
155
|
-
[I in TableDef['indexes'][number] as I['accessor'] & {}]: TableIndexFromDef<
|
|
156
|
-
TableDef,
|
|
157
|
-
I
|
|
158
|
-
>;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
type TableIndexFromDef<
|
|
162
|
-
TableDef extends UntypedTableDef,
|
|
163
|
-
I extends IndexOpts<keyof TableDef['columns'] & string>,
|
|
164
|
-
> =
|
|
165
|
-
NormalizeIndexColumns<TableDef, I> extends infer Cols extends ReadonlyArray<
|
|
166
|
-
keyof TableDef['columns'] & string
|
|
167
|
-
>
|
|
168
|
-
? {
|
|
169
|
-
name: I['accessor'];
|
|
170
|
-
unique: AllUnique<TableDef, Cols>;
|
|
171
|
-
algorithm: Lowercase<I['algorithm']>;
|
|
172
|
-
columns: Cols;
|
|
173
|
-
}
|
|
174
|
-
: never;
|
|
175
|
-
|
|
176
|
-
type NormalizeIndexColumns<
|
|
177
|
-
TableDef extends UntypedTableDef,
|
|
178
|
-
I extends IndexOpts<keyof TableDef['columns'] & string>,
|
|
179
|
-
> =
|
|
180
|
-
IndexColumns<I> extends ReadonlyArray<keyof TableDef['columns'] & string>
|
|
181
|
-
? IndexColumns<I>
|
|
182
|
-
: never;
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Options for configuring a database table.
|
|
186
|
-
* - `name`: The name of the table.
|
|
187
|
-
* - `public`: Whether the table is publicly accessible. Defaults to `false`.
|
|
188
|
-
* - `indexes`: An array of index configurations for the table.
|
|
189
|
-
* - `constraints`: An array of constraint configurations for the table.
|
|
190
|
-
* - `scheduled`: The name of the reducer to be executed based on the scheduled rows in this table.
|
|
191
|
-
*/
|
|
192
|
-
export type TableOpts<Row extends RowObj> = {
|
|
193
|
-
name?: string;
|
|
194
|
-
public?: boolean;
|
|
195
|
-
indexes?: IndexOpts<keyof Row & string>[]; // declarative multi‑column indexes
|
|
196
|
-
constraints?: ConstraintOpts<keyof Row & string>[];
|
|
197
|
-
scheduled?: () =>
|
|
198
|
-
| ReducerExport<any, { [k: string]: RowBuilder<RowObj> }>
|
|
199
|
-
| ProcedureExport<
|
|
200
|
-
any,
|
|
201
|
-
{ [k: string]: RowBuilder<RowObj> },
|
|
202
|
-
ReturnType<typeof t.unit>
|
|
203
|
-
>;
|
|
204
|
-
event?: boolean;
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Extracts the indices from TableOpts, defaulting to an empty array if none are provided.
|
|
209
|
-
*/
|
|
210
|
-
type OptsIndices<Opts extends TableOpts<any>> = Opts extends {
|
|
211
|
-
indexes: infer Ixs extends NonNullable<any[]>;
|
|
212
|
-
}
|
|
213
|
-
? Ixs
|
|
214
|
-
: CoerceArray<[]>;
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Extracts the constraints from TableOpts, defaulting to an empty array if none are provided.
|
|
218
|
-
*/
|
|
219
|
-
type OptsConstraints<Opts extends TableOpts<any>> = Opts extends {
|
|
220
|
-
constraints: infer Constraints extends NonNullable<any[]>;
|
|
221
|
-
}
|
|
222
|
-
? Constraints
|
|
223
|
-
: CoerceArray<[]>;
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Table<Row, UniqueConstraintViolation = never, AutoIncOverflow = never>
|
|
227
|
-
*
|
|
228
|
-
* - Row: row shape
|
|
229
|
-
* - UCV: unique-constraint violation error type (never if none)
|
|
230
|
-
* - AIO: auto-increment overflow error type (never if none)
|
|
231
|
-
*/
|
|
232
|
-
export type Table<TableDef extends UntypedTableDef> = Prettify<
|
|
233
|
-
TableMethods<TableDef> & Indexes<TableDef, TableIndexes<TableDef>>
|
|
234
|
-
>;
|
|
235
|
-
|
|
236
|
-
export type ReadonlyTable<TableDef extends UntypedTableDef> = Prettify<
|
|
237
|
-
ReadonlyTableMethods<TableDef> &
|
|
238
|
-
ReadonlyIndexes<TableDef, TableIndexes<TableDef>>
|
|
239
|
-
>;
|
|
240
|
-
|
|
241
|
-
export interface ReadonlyTableMethods<TableDef extends UntypedTableDef> {
|
|
242
|
-
/**
|
|
243
|
-
* Returns the number of rows in this table.
|
|
244
|
-
*
|
|
245
|
-
* This reads datastore metadata, so it runs in constant time.
|
|
246
|
-
* It also takes into account modifications by the current transaction.
|
|
247
|
-
*/
|
|
248
|
-
count(): bigint;
|
|
249
|
-
|
|
250
|
-
/** Iterate over all rows in the TX state. Rust Iterator<Item=Row> → TS IterableIterator<Row>. */
|
|
251
|
-
iter(): IteratorObject<Prettify<RowType<TableDef>>, undefined>;
|
|
252
|
-
[Symbol.iterator](): IteratorObject<Prettify<RowType<TableDef>>, undefined>;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* A type representing the methods available on a table.
|
|
257
|
-
*/
|
|
258
|
-
export interface TableMethods<TableDef extends UntypedTableDef>
|
|
259
|
-
extends ReadonlyTableMethods<TableDef> {
|
|
260
|
-
/**
|
|
261
|
-
* Insert and return the inserted row (auto-increment fields filled).
|
|
262
|
-
*
|
|
263
|
-
* May throw on error:
|
|
264
|
-
* * If there are any unique or primary key columns in this table, may throw {@link errors.UniqueAlreadyExists}.
|
|
265
|
-
* * If there are any auto-incrementing columns in this table, may throw {@link errors.AutoIncOverflow}.
|
|
266
|
-
* */
|
|
267
|
-
insert(row: Prettify<RowType<TableDef>>): Prettify<RowType<TableDef>>;
|
|
268
|
-
|
|
269
|
-
/** Delete a row equal to `row`. Returns true if something was deleted. */
|
|
270
|
-
delete(row: Prettify<RowType<TableDef>>): boolean;
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Clears the table of all rows.
|
|
274
|
-
* Returns the number of rows deleted,
|
|
275
|
-
* i.e., the return value of `this.count()` before this call.
|
|
276
|
-
*/
|
|
277
|
-
clear(): bigint;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Defines a database table with schema and options.
|
|
282
|
-
*
|
|
283
|
-
* @param opts - Table configuration including name, indexes, and access control
|
|
284
|
-
* @param row - Product type defining the table's row structure
|
|
285
|
-
* @returns Table handle for use in schema() function
|
|
286
|
-
*
|
|
287
|
-
* @example
|
|
288
|
-
* ```ts
|
|
289
|
-
* const playerTable = table(
|
|
290
|
-
* { name: 'player', public: true },
|
|
291
|
-
* {
|
|
292
|
-
* id: t.u32().primaryKey(),
|
|
293
|
-
* name: t.string().index('btree')
|
|
294
|
-
* }
|
|
295
|
-
* );
|
|
296
|
-
* ```
|
|
297
|
-
*
|
|
298
|
-
* ## Column Validation Error
|
|
299
|
-
*
|
|
300
|
-
* **If you see an error like "Expected 3 arguments, but got 2"**, this means
|
|
301
|
-
* one of your columns has an invalid combination of attributes.
|
|
302
|
-
*
|
|
303
|
-
* Specifically, `default()` cannot be combined with:
|
|
304
|
-
* - `primaryKey()`
|
|
305
|
-
* - `unique()`
|
|
306
|
-
* - `autoInc()`
|
|
307
|
-
*
|
|
308
|
-
* **Example of invalid code:**
|
|
309
|
-
* ```ts
|
|
310
|
-
* // ERROR: default() + primaryKey() is not allowed
|
|
311
|
-
* const badTable = table(
|
|
312
|
-
* { name: 'bad' },
|
|
313
|
-
* { id: t.u64().default(0n).primaryKey() } // <- This causes "Expected 3 arguments"
|
|
314
|
-
* );
|
|
315
|
-
* ```
|
|
316
|
-
*
|
|
317
|
-
* **How to fix:** Remove either `default()` or the constraint (`primaryKey`/`unique`/`autoInc`).
|
|
318
|
-
*/
|
|
319
|
-
export function table<Row extends RowObj, const Opts extends TableOpts<Row>>(
|
|
320
|
-
opts: Opts,
|
|
321
|
-
row: Row | RowBuilder<Row>,
|
|
322
|
-
// ⚠️ INTERNAL: This parameter enforces compile-time validation of column metadata.
|
|
323
|
-
// It is never passed at runtime. If you see "Expected 3 arguments, but got 2",
|
|
324
|
-
// it means a column has an invalid combination (e.g., default + primaryKey).
|
|
325
|
-
// See the JSDoc above for details on how to fix this error.
|
|
326
|
-
..._: HasInvalidColumn<Row> extends true
|
|
327
|
-
? [
|
|
328
|
-
error: ERROR_default_cannot_be_combined_with_primaryKey_unique_or_autoInc<
|
|
329
|
-
InvalidColumnNames<Row>
|
|
330
|
-
>,
|
|
331
|
-
]
|
|
332
|
-
: []
|
|
333
|
-
): TableSchema<CoerceRow<Row>, OptsIndices<Opts>> {
|
|
334
|
-
const {
|
|
335
|
-
name,
|
|
336
|
-
public: isPublic = false,
|
|
337
|
-
indexes: userIndexes = [],
|
|
338
|
-
scheduled,
|
|
339
|
-
event: isEvent = false,
|
|
340
|
-
} = opts;
|
|
341
|
-
|
|
342
|
-
// 1. column catalogue + helpers
|
|
343
|
-
const colIds = new Map<keyof Row & string, ColId>();
|
|
344
|
-
const colNameList: string[] = [];
|
|
345
|
-
|
|
346
|
-
if (!(row instanceof RowBuilder)) {
|
|
347
|
-
row = new RowBuilder(row);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
row.algebraicType.value.elements.forEach((elem, i) => {
|
|
351
|
-
colIds.set(elem.name, i);
|
|
352
|
-
colNameList.push(elem.name);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
// gather primary keys, per‑column indexes, uniques, sequences
|
|
356
|
-
const pk: ColList = [];
|
|
357
|
-
const indexes: (RawIndexDefV10 & { canonicalName?: string })[] = [];
|
|
358
|
-
const constraints: RawConstraintDefV10[] = [];
|
|
359
|
-
const sequences: RawSequenceDefV10[] = [];
|
|
360
|
-
|
|
361
|
-
let scheduleAtCol: ColId | undefined;
|
|
362
|
-
const defaultValues: RawColumnDefaultValueV10[] = [];
|
|
363
|
-
|
|
364
|
-
for (const [name, builder] of Object.entries(row.row)) {
|
|
365
|
-
const meta: ColumnMetadata<any> = builder.columnMetadata;
|
|
366
|
-
|
|
367
|
-
if (meta.isPrimaryKey) {
|
|
368
|
-
pk.push(colIds.get(name)!);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const isUnique = meta.isUnique || meta.isPrimaryKey;
|
|
372
|
-
|
|
373
|
-
// implicit 1‑column indexes
|
|
374
|
-
if (meta.indexType || isUnique) {
|
|
375
|
-
const algo = meta.indexType ?? 'btree';
|
|
376
|
-
const id = colIds.get(name)!;
|
|
377
|
-
let algorithm: RawIndexAlgorithm;
|
|
378
|
-
switch (algo) {
|
|
379
|
-
case 'btree':
|
|
380
|
-
algorithm = RawIndexAlgorithm.BTree([id]);
|
|
381
|
-
break;
|
|
382
|
-
case 'hash':
|
|
383
|
-
algorithm = RawIndexAlgorithm.Hash([id]);
|
|
384
|
-
break;
|
|
385
|
-
case 'direct':
|
|
386
|
-
algorithm = RawIndexAlgorithm.Direct(id);
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
indexes.push({
|
|
390
|
-
sourceName: undefined, // Unnamed indexes will be assigned a globally unique name
|
|
391
|
-
accessorName: name,
|
|
392
|
-
algorithm,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (isUnique) {
|
|
397
|
-
constraints.push({
|
|
398
|
-
sourceName: undefined,
|
|
399
|
-
data: { tag: 'Unique', value: { columns: [colIds.get(name)!] } },
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (meta.isAutoIncrement) {
|
|
404
|
-
sequences.push({
|
|
405
|
-
sourceName: undefined,
|
|
406
|
-
start: undefined,
|
|
407
|
-
minValue: undefined,
|
|
408
|
-
maxValue: undefined,
|
|
409
|
-
column: colIds.get(name)!,
|
|
410
|
-
increment: 1n,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Check for defaultValue on the property to allow for 0, false, '', and undefined as defaults
|
|
415
|
-
if (Object.prototype.hasOwnProperty.call(meta, 'defaultValue')) {
|
|
416
|
-
const writer = new BinaryWriter(16);
|
|
417
|
-
builder.serialize(writer, meta.defaultValue);
|
|
418
|
-
defaultValues.push({
|
|
419
|
-
colId: colIds.get(name)!,
|
|
420
|
-
value: writer.getBuffer(),
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// If this column is shaped like ScheduleAtAlgebraicType, mark it as the schedule‑at column
|
|
425
|
-
if (scheduled) {
|
|
426
|
-
const algebraicType = builder.typeBuilder.algebraicType;
|
|
427
|
-
if (ScheduleAt.isScheduleAt(algebraicType)) {
|
|
428
|
-
scheduleAtCol = colIds.get(name)!;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// convert explicit multi‑column indexes coming from options.indexes
|
|
434
|
-
for (const indexOpts of userIndexes ?? []) {
|
|
435
|
-
const accessor = indexOpts.accessor;
|
|
436
|
-
if (typeof accessor !== 'string' || accessor.length === 0) {
|
|
437
|
-
const tableLabel = name ?? '<unnamed>';
|
|
438
|
-
const indexLabel = indexOpts.name ?? '<unnamed>';
|
|
439
|
-
throw new TypeError(
|
|
440
|
-
`Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'`
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
let algorithm: RawIndexAlgorithm;
|
|
444
|
-
switch (indexOpts.algorithm) {
|
|
445
|
-
case 'btree':
|
|
446
|
-
algorithm = {
|
|
447
|
-
tag: 'BTree',
|
|
448
|
-
value: indexOpts.columns.map(c => colIds.get(c)!),
|
|
449
|
-
};
|
|
450
|
-
break;
|
|
451
|
-
case 'hash':
|
|
452
|
-
algorithm = {
|
|
453
|
-
tag: 'Hash',
|
|
454
|
-
value: indexOpts.columns.map(c => colIds.get(c)!),
|
|
455
|
-
};
|
|
456
|
-
break;
|
|
457
|
-
case 'direct':
|
|
458
|
-
algorithm = { tag: 'Direct', value: colIds.get(indexOpts.column)! };
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Unnamed indexes are assigned a globally unique source name.
|
|
463
|
-
// `accessor` controls the TypeScript property used to access the index.
|
|
464
|
-
// `name` (if present) is preserved as the canonical schema name.
|
|
465
|
-
//
|
|
466
|
-
// IMPORTANT: we intentionally do not reject duplicate accessor names here.
|
|
467
|
-
// This preserves existing behavior for raw table definitions. Downstream
|
|
468
|
-
// runtime consumers decide how duplicates are resolved:
|
|
469
|
-
// - server runtime merges duplicate accessors onto one accessor object
|
|
470
|
-
// - client cache assignment is last-write-wins for duplicate accessors
|
|
471
|
-
indexes.push({
|
|
472
|
-
sourceName: undefined,
|
|
473
|
-
accessorName: accessor,
|
|
474
|
-
algorithm,
|
|
475
|
-
canonicalName: indexOpts.name,
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// add explicit constraints from options.constraints
|
|
480
|
-
for (const constraintOpts of opts.constraints ?? []) {
|
|
481
|
-
if (constraintOpts.constraint === 'unique') {
|
|
482
|
-
const data: RawConstraintDefV10['data'] = {
|
|
483
|
-
tag: 'Unique',
|
|
484
|
-
value: { columns: constraintOpts.columns.map(c => colIds.get(c)!) },
|
|
485
|
-
};
|
|
486
|
-
constraints.push({ sourceName: constraintOpts.name, data });
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const productType = row.algebraicType.value as RowBuilder<
|
|
492
|
-
CoerceRow<Row>
|
|
493
|
-
>['algebraicType']['value'];
|
|
494
|
-
|
|
495
|
-
const schedule =
|
|
496
|
-
scheduled && scheduleAtCol !== undefined
|
|
497
|
-
? { scheduleAtCol, reducer: scheduled }
|
|
498
|
-
: undefined;
|
|
499
|
-
|
|
500
|
-
return {
|
|
501
|
-
rowType: row as RowBuilder<CoerceRow<Row>>,
|
|
502
|
-
tableName: name,
|
|
503
|
-
rowSpacetimeType: productType,
|
|
504
|
-
tableDef: (ctx, accName) => {
|
|
505
|
-
const tableName = name ?? accName;
|
|
506
|
-
if (row.typeName === undefined) {
|
|
507
|
-
row.typeName = toPascalCase(tableName);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Build index source names using accName
|
|
511
|
-
for (const index of indexes) {
|
|
512
|
-
const cols =
|
|
513
|
-
index.algorithm.tag === 'Direct'
|
|
514
|
-
? [index.algorithm.value]
|
|
515
|
-
: index.algorithm.value;
|
|
516
|
-
|
|
517
|
-
const colS = cols.map(i => colNameList[i]).join('_');
|
|
518
|
-
const sourceName =
|
|
519
|
-
(index.sourceName = `${accName}_${colS}_idx_${index.algorithm.tag.toLowerCase()}`);
|
|
520
|
-
|
|
521
|
-
const { canonicalName } = index;
|
|
522
|
-
if (canonicalName !== undefined) {
|
|
523
|
-
ctx.moduleDef.explicitNames.entries.push(
|
|
524
|
-
ExplicitNameEntry.Index({ sourceName, canonicalName })
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
return {
|
|
530
|
-
sourceName: accName,
|
|
531
|
-
productTypeRef: ctx.registerTypesRecursively(row).ref,
|
|
532
|
-
primaryKey: pk,
|
|
533
|
-
indexes,
|
|
534
|
-
constraints,
|
|
535
|
-
sequences,
|
|
536
|
-
tableType: { tag: 'User' },
|
|
537
|
-
tableAccess: { tag: isPublic ? 'Public' : 'Private' },
|
|
538
|
-
defaultValues,
|
|
539
|
-
isEvent,
|
|
540
|
-
};
|
|
541
|
-
},
|
|
542
|
-
// Preserve the declared index options as runtime data so `tableToSchema`
|
|
543
|
-
// can expose them without type-smuggling.
|
|
544
|
-
idxs: userIndexes as OptsIndices<Opts>,
|
|
545
|
-
constraints: constraints as OptsConstraints<Opts>,
|
|
546
|
-
schedule,
|
|
547
|
-
};
|
|
548
|
-
}
|
|
1
|
+
import type { ProcedureExport, ReducerExport, t } from '../server';
|
|
2
|
+
import type { errors } from '../server/errors';
|
|
3
|
+
import {
|
|
4
|
+
ExplicitNameEntry,
|
|
5
|
+
RawColumnDefaultValueV10,
|
|
6
|
+
RawConstraintDefV10,
|
|
7
|
+
RawIndexAlgorithm,
|
|
8
|
+
RawIndexDefV10,
|
|
9
|
+
RawSequenceDefV10,
|
|
10
|
+
RawTableDefV10,
|
|
11
|
+
} from './autogen/types';
|
|
12
|
+
import BinaryWriter from './binary_writer';
|
|
13
|
+
import type { AllUnique, ConstraintOpts } from './constraints';
|
|
14
|
+
import type {
|
|
15
|
+
ColumnIndex,
|
|
16
|
+
IndexColumns,
|
|
17
|
+
Indexes,
|
|
18
|
+
IndexOpts,
|
|
19
|
+
ReadonlyIndexes,
|
|
20
|
+
UntypedIndex,
|
|
21
|
+
} from './indexes';
|
|
22
|
+
import ScheduleAt from './schedule_at';
|
|
23
|
+
import type { TableSchema } from './table_schema';
|
|
24
|
+
import {
|
|
25
|
+
RowBuilder,
|
|
26
|
+
type ColumnBuilder,
|
|
27
|
+
type ColumnMetadata,
|
|
28
|
+
type InferTypeOfRow,
|
|
29
|
+
type RowObj,
|
|
30
|
+
type TypeBuilder,
|
|
31
|
+
} from './type_builders';
|
|
32
|
+
import type {
|
|
33
|
+
InvalidColumnMetadata,
|
|
34
|
+
Prettify,
|
|
35
|
+
ValidateColumnMetadata,
|
|
36
|
+
} from './type_util';
|
|
37
|
+
import { toPascalCase } from './util';
|
|
38
|
+
|
|
39
|
+
export type AlgebraicTypeRef = number;
|
|
40
|
+
type ColId = number;
|
|
41
|
+
type ColList = ColId[];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if any column in the row has invalid metadata.
|
|
45
|
+
*/
|
|
46
|
+
type HasInvalidColumn<Row extends RowObj> =
|
|
47
|
+
// this checks if Row exactly equals RowObj - if it does, we can't
|
|
48
|
+
// do type-system-level checking, so just let it pass
|
|
49
|
+
(<G>() => G extends Row ? 1 : 2) extends <G>() => G extends RowObj ? 1 : 2
|
|
50
|
+
? false
|
|
51
|
+
: {
|
|
52
|
+
[K in keyof Row]: Row[K] extends ColumnBuilder<any, any, infer M>
|
|
53
|
+
? ValidateColumnMetadata<M> extends InvalidColumnMetadata<any>
|
|
54
|
+
? true
|
|
55
|
+
: false
|
|
56
|
+
: false;
|
|
57
|
+
}[keyof Row] extends false
|
|
58
|
+
? false
|
|
59
|
+
: true;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Extract the names of columns that have invalid metadata.
|
|
63
|
+
*/
|
|
64
|
+
type InvalidColumnNames<Row extends RowObj> = {
|
|
65
|
+
[K in keyof Row]: Row[K] extends ColumnBuilder<any, any, infer M>
|
|
66
|
+
? ValidateColumnMetadata<M> extends InvalidColumnMetadata<any>
|
|
67
|
+
? K & string
|
|
68
|
+
: never
|
|
69
|
+
: never;
|
|
70
|
+
}[keyof Row];
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A descriptive error type that surfaces the validation error.
|
|
74
|
+
* The type name itself contains the error message for better CLI output.
|
|
75
|
+
*/
|
|
76
|
+
type ERROR_default_cannot_be_combined_with_primaryKey_unique_or_autoInc<
|
|
77
|
+
InvalidColumns extends string,
|
|
78
|
+
> = {
|
|
79
|
+
_invalidColumns: InvalidColumns;
|
|
80
|
+
_fix: 'Remove either default() or the constraint (primaryKey/unique/autoInc) from these columns';
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A helper type to extract the row type from a TableDef
|
|
85
|
+
*/
|
|
86
|
+
export type RowType<TableDef extends Pick<UntypedTableDef, 'columns'>> =
|
|
87
|
+
InferTypeOfRow<TableDef['columns']>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Coerces a column which may be a TypeBuilder or ColumnBuilder into a ColumnBuilder
|
|
91
|
+
*/
|
|
92
|
+
export type CoerceColumn<
|
|
93
|
+
Col extends TypeBuilder<any, any> | ColumnBuilder<any, any, any>,
|
|
94
|
+
> =
|
|
95
|
+
Col extends TypeBuilder<infer T, infer U>
|
|
96
|
+
? ColumnBuilder<T, U, ColumnMetadata<any>>
|
|
97
|
+
: Col;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Coerces a RowObj where TypeBuilders are replaced with ColumnBuilders
|
|
101
|
+
*/
|
|
102
|
+
export type CoerceRow<Row extends RowObj> = {
|
|
103
|
+
[k in keyof Row & string]: CoerceColumn<Row[k]>;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Helper type to coerce an array of IndexOpts
|
|
108
|
+
*/
|
|
109
|
+
type CoerceArray<X extends IndexOpts<any>[]> = X;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* An untyped representation of a table's schema.
|
|
113
|
+
*/
|
|
114
|
+
export type UntypedTableDef = {
|
|
115
|
+
sourceName: string;
|
|
116
|
+
accessorName: string;
|
|
117
|
+
columns: Record<string, ColumnBuilder<any, any, ColumnMetadata<any>>>;
|
|
118
|
+
// This is really just a ProductType where all the elements have names.
|
|
119
|
+
rowType: RowBuilder<RowObj>['algebraicType']['value'];
|
|
120
|
+
/**
|
|
121
|
+
* Declarative multi-column indexes supplied by user code in `table({ indexes: [...] }, ...)`.
|
|
122
|
+
*
|
|
123
|
+
* This is intentionally the *declarative* shape (`IndexOpts`) because a lot of
|
|
124
|
+
* type-level behavior is derived from these entries (for example query-builder
|
|
125
|
+
* inference over composite indexes).
|
|
126
|
+
*/
|
|
127
|
+
indexes: readonly IndexOpts<any>[];
|
|
128
|
+
/**
|
|
129
|
+
* Fully-resolved runtime indexes materialized from `RawTableDefV10`.
|
|
130
|
+
*
|
|
131
|
+
* This contains both:
|
|
132
|
+
* 1) field-level indexes inferred from column metadata, and
|
|
133
|
+
* 2) explicit table-level indexes.
|
|
134
|
+
*
|
|
135
|
+
* Runtime consumers like `TableCacheImpl` should use this field instead of
|
|
136
|
+
* reinterpreting `indexes` as runtime index metadata.
|
|
137
|
+
*/
|
|
138
|
+
resolvedIndexes: readonly UntypedIndex<any>[];
|
|
139
|
+
constraints: readonly ConstraintOpts<any>[];
|
|
140
|
+
tableDef: RawTableDefV10;
|
|
141
|
+
isEvent?: boolean;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A type representing the indexes defined on a table.
|
|
146
|
+
*/
|
|
147
|
+
export type TableIndexes<TableDef extends UntypedTableDef> = {
|
|
148
|
+
[K in keyof TableDef['columns'] & string as ColumnIndex<
|
|
149
|
+
K,
|
|
150
|
+
TableDef['columns'][K]['columnMetadata']
|
|
151
|
+
> extends never
|
|
152
|
+
? never
|
|
153
|
+
: K]: ColumnIndex<K, TableDef['columns'][K]['columnMetadata']>;
|
|
154
|
+
} & {
|
|
155
|
+
[I in TableDef['indexes'][number] as I['accessor'] & {}]: TableIndexFromDef<
|
|
156
|
+
TableDef,
|
|
157
|
+
I
|
|
158
|
+
>;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
type TableIndexFromDef<
|
|
162
|
+
TableDef extends UntypedTableDef,
|
|
163
|
+
I extends IndexOpts<keyof TableDef['columns'] & string>,
|
|
164
|
+
> =
|
|
165
|
+
NormalizeIndexColumns<TableDef, I> extends infer Cols extends ReadonlyArray<
|
|
166
|
+
keyof TableDef['columns'] & string
|
|
167
|
+
>
|
|
168
|
+
? {
|
|
169
|
+
name: I['accessor'];
|
|
170
|
+
unique: AllUnique<TableDef, Cols>;
|
|
171
|
+
algorithm: Lowercase<I['algorithm']>;
|
|
172
|
+
columns: Cols;
|
|
173
|
+
}
|
|
174
|
+
: never;
|
|
175
|
+
|
|
176
|
+
type NormalizeIndexColumns<
|
|
177
|
+
TableDef extends UntypedTableDef,
|
|
178
|
+
I extends IndexOpts<keyof TableDef['columns'] & string>,
|
|
179
|
+
> =
|
|
180
|
+
IndexColumns<I> extends ReadonlyArray<keyof TableDef['columns'] & string>
|
|
181
|
+
? IndexColumns<I>
|
|
182
|
+
: never;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Options for configuring a database table.
|
|
186
|
+
* - `name`: The name of the table.
|
|
187
|
+
* - `public`: Whether the table is publicly accessible. Defaults to `false`.
|
|
188
|
+
* - `indexes`: An array of index configurations for the table.
|
|
189
|
+
* - `constraints`: An array of constraint configurations for the table.
|
|
190
|
+
* - `scheduled`: The name of the reducer to be executed based on the scheduled rows in this table.
|
|
191
|
+
*/
|
|
192
|
+
export type TableOpts<Row extends RowObj> = {
|
|
193
|
+
name?: string;
|
|
194
|
+
public?: boolean;
|
|
195
|
+
indexes?: IndexOpts<keyof Row & string>[]; // declarative multi‑column indexes
|
|
196
|
+
constraints?: ConstraintOpts<keyof Row & string>[];
|
|
197
|
+
scheduled?: () =>
|
|
198
|
+
| ReducerExport<any, { [k: string]: RowBuilder<RowObj> }>
|
|
199
|
+
| ProcedureExport<
|
|
200
|
+
any,
|
|
201
|
+
{ [k: string]: RowBuilder<RowObj> },
|
|
202
|
+
ReturnType<typeof t.unit>
|
|
203
|
+
>;
|
|
204
|
+
event?: boolean;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Extracts the indices from TableOpts, defaulting to an empty array if none are provided.
|
|
209
|
+
*/
|
|
210
|
+
type OptsIndices<Opts extends TableOpts<any>> = Opts extends {
|
|
211
|
+
indexes: infer Ixs extends NonNullable<any[]>;
|
|
212
|
+
}
|
|
213
|
+
? Ixs
|
|
214
|
+
: CoerceArray<[]>;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Extracts the constraints from TableOpts, defaulting to an empty array if none are provided.
|
|
218
|
+
*/
|
|
219
|
+
type OptsConstraints<Opts extends TableOpts<any>> = Opts extends {
|
|
220
|
+
constraints: infer Constraints extends NonNullable<any[]>;
|
|
221
|
+
}
|
|
222
|
+
? Constraints
|
|
223
|
+
: CoerceArray<[]>;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Table<Row, UniqueConstraintViolation = never, AutoIncOverflow = never>
|
|
227
|
+
*
|
|
228
|
+
* - Row: row shape
|
|
229
|
+
* - UCV: unique-constraint violation error type (never if none)
|
|
230
|
+
* - AIO: auto-increment overflow error type (never if none)
|
|
231
|
+
*/
|
|
232
|
+
export type Table<TableDef extends UntypedTableDef> = Prettify<
|
|
233
|
+
TableMethods<TableDef> & Indexes<TableDef, TableIndexes<TableDef>>
|
|
234
|
+
>;
|
|
235
|
+
|
|
236
|
+
export type ReadonlyTable<TableDef extends UntypedTableDef> = Prettify<
|
|
237
|
+
ReadonlyTableMethods<TableDef> &
|
|
238
|
+
ReadonlyIndexes<TableDef, TableIndexes<TableDef>>
|
|
239
|
+
>;
|
|
240
|
+
|
|
241
|
+
export interface ReadonlyTableMethods<TableDef extends UntypedTableDef> {
|
|
242
|
+
/**
|
|
243
|
+
* Returns the number of rows in this table.
|
|
244
|
+
*
|
|
245
|
+
* This reads datastore metadata, so it runs in constant time.
|
|
246
|
+
* It also takes into account modifications by the current transaction.
|
|
247
|
+
*/
|
|
248
|
+
count(): bigint;
|
|
249
|
+
|
|
250
|
+
/** Iterate over all rows in the TX state. Rust Iterator<Item=Row> → TS IterableIterator<Row>. */
|
|
251
|
+
iter(): IteratorObject<Prettify<RowType<TableDef>>, undefined>;
|
|
252
|
+
[Symbol.iterator](): IteratorObject<Prettify<RowType<TableDef>>, undefined>;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A type representing the methods available on a table.
|
|
257
|
+
*/
|
|
258
|
+
export interface TableMethods<TableDef extends UntypedTableDef>
|
|
259
|
+
extends ReadonlyTableMethods<TableDef> {
|
|
260
|
+
/**
|
|
261
|
+
* Insert and return the inserted row (auto-increment fields filled).
|
|
262
|
+
*
|
|
263
|
+
* May throw on error:
|
|
264
|
+
* * If there are any unique or primary key columns in this table, may throw {@link errors.UniqueAlreadyExists}.
|
|
265
|
+
* * If there are any auto-incrementing columns in this table, may throw {@link errors.AutoIncOverflow}.
|
|
266
|
+
* */
|
|
267
|
+
insert(row: Prettify<RowType<TableDef>>): Prettify<RowType<TableDef>>;
|
|
268
|
+
|
|
269
|
+
/** Delete a row equal to `row`. Returns true if something was deleted. */
|
|
270
|
+
delete(row: Prettify<RowType<TableDef>>): boolean;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Clears the table of all rows.
|
|
274
|
+
* Returns the number of rows deleted,
|
|
275
|
+
* i.e., the return value of `this.count()` before this call.
|
|
276
|
+
*/
|
|
277
|
+
clear(): bigint;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Defines a database table with schema and options.
|
|
282
|
+
*
|
|
283
|
+
* @param opts - Table configuration including name, indexes, and access control
|
|
284
|
+
* @param row - Product type defining the table's row structure
|
|
285
|
+
* @returns Table handle for use in schema() function
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* const playerTable = table(
|
|
290
|
+
* { name: 'player', public: true },
|
|
291
|
+
* {
|
|
292
|
+
* id: t.u32().primaryKey(),
|
|
293
|
+
* name: t.string().index('btree')
|
|
294
|
+
* }
|
|
295
|
+
* );
|
|
296
|
+
* ```
|
|
297
|
+
*
|
|
298
|
+
* ## Column Validation Error
|
|
299
|
+
*
|
|
300
|
+
* **If you see an error like "Expected 3 arguments, but got 2"**, this means
|
|
301
|
+
* one of your columns has an invalid combination of attributes.
|
|
302
|
+
*
|
|
303
|
+
* Specifically, `default()` cannot be combined with:
|
|
304
|
+
* - `primaryKey()`
|
|
305
|
+
* - `unique()`
|
|
306
|
+
* - `autoInc()`
|
|
307
|
+
*
|
|
308
|
+
* **Example of invalid code:**
|
|
309
|
+
* ```ts
|
|
310
|
+
* // ERROR: default() + primaryKey() is not allowed
|
|
311
|
+
* const badTable = table(
|
|
312
|
+
* { name: 'bad' },
|
|
313
|
+
* { id: t.u64().default(0n).primaryKey() } // <- This causes "Expected 3 arguments"
|
|
314
|
+
* );
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* **How to fix:** Remove either `default()` or the constraint (`primaryKey`/`unique`/`autoInc`).
|
|
318
|
+
*/
|
|
319
|
+
export function table<Row extends RowObj, const Opts extends TableOpts<Row>>(
|
|
320
|
+
opts: Opts,
|
|
321
|
+
row: Row | RowBuilder<Row>,
|
|
322
|
+
// ⚠️ INTERNAL: This parameter enforces compile-time validation of column metadata.
|
|
323
|
+
// It is never passed at runtime. If you see "Expected 3 arguments, but got 2",
|
|
324
|
+
// it means a column has an invalid combination (e.g., default + primaryKey).
|
|
325
|
+
// See the JSDoc above for details on how to fix this error.
|
|
326
|
+
..._: HasInvalidColumn<Row> extends true
|
|
327
|
+
? [
|
|
328
|
+
error: ERROR_default_cannot_be_combined_with_primaryKey_unique_or_autoInc<
|
|
329
|
+
InvalidColumnNames<Row>
|
|
330
|
+
>,
|
|
331
|
+
]
|
|
332
|
+
: []
|
|
333
|
+
): TableSchema<CoerceRow<Row>, OptsIndices<Opts>> {
|
|
334
|
+
const {
|
|
335
|
+
name,
|
|
336
|
+
public: isPublic = false,
|
|
337
|
+
indexes: userIndexes = [],
|
|
338
|
+
scheduled,
|
|
339
|
+
event: isEvent = false,
|
|
340
|
+
} = opts;
|
|
341
|
+
|
|
342
|
+
// 1. column catalogue + helpers
|
|
343
|
+
const colIds = new Map<keyof Row & string, ColId>();
|
|
344
|
+
const colNameList: string[] = [];
|
|
345
|
+
|
|
346
|
+
if (!(row instanceof RowBuilder)) {
|
|
347
|
+
row = new RowBuilder(row);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
row.algebraicType.value.elements.forEach((elem, i) => {
|
|
351
|
+
colIds.set(elem.name, i);
|
|
352
|
+
colNameList.push(elem.name);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// gather primary keys, per‑column indexes, uniques, sequences
|
|
356
|
+
const pk: ColList = [];
|
|
357
|
+
const indexes: (RawIndexDefV10 & { canonicalName?: string })[] = [];
|
|
358
|
+
const constraints: RawConstraintDefV10[] = [];
|
|
359
|
+
const sequences: RawSequenceDefV10[] = [];
|
|
360
|
+
|
|
361
|
+
let scheduleAtCol: ColId | undefined;
|
|
362
|
+
const defaultValues: RawColumnDefaultValueV10[] = [];
|
|
363
|
+
|
|
364
|
+
for (const [name, builder] of Object.entries(row.row)) {
|
|
365
|
+
const meta: ColumnMetadata<any> = builder.columnMetadata;
|
|
366
|
+
|
|
367
|
+
if (meta.isPrimaryKey) {
|
|
368
|
+
pk.push(colIds.get(name)!);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const isUnique = meta.isUnique || meta.isPrimaryKey;
|
|
372
|
+
|
|
373
|
+
// implicit 1‑column indexes
|
|
374
|
+
if (meta.indexType || isUnique) {
|
|
375
|
+
const algo = meta.indexType ?? 'btree';
|
|
376
|
+
const id = colIds.get(name)!;
|
|
377
|
+
let algorithm: RawIndexAlgorithm;
|
|
378
|
+
switch (algo) {
|
|
379
|
+
case 'btree':
|
|
380
|
+
algorithm = RawIndexAlgorithm.BTree([id]);
|
|
381
|
+
break;
|
|
382
|
+
case 'hash':
|
|
383
|
+
algorithm = RawIndexAlgorithm.Hash([id]);
|
|
384
|
+
break;
|
|
385
|
+
case 'direct':
|
|
386
|
+
algorithm = RawIndexAlgorithm.Direct(id);
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
indexes.push({
|
|
390
|
+
sourceName: undefined, // Unnamed indexes will be assigned a globally unique name
|
|
391
|
+
accessorName: name,
|
|
392
|
+
algorithm,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (isUnique) {
|
|
397
|
+
constraints.push({
|
|
398
|
+
sourceName: undefined,
|
|
399
|
+
data: { tag: 'Unique', value: { columns: [colIds.get(name)!] } },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (meta.isAutoIncrement) {
|
|
404
|
+
sequences.push({
|
|
405
|
+
sourceName: undefined,
|
|
406
|
+
start: undefined,
|
|
407
|
+
minValue: undefined,
|
|
408
|
+
maxValue: undefined,
|
|
409
|
+
column: colIds.get(name)!,
|
|
410
|
+
increment: 1n,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Check for defaultValue on the property to allow for 0, false, '', and undefined as defaults
|
|
415
|
+
if (Object.prototype.hasOwnProperty.call(meta, 'defaultValue')) {
|
|
416
|
+
const writer = new BinaryWriter(16);
|
|
417
|
+
builder.serialize(writer, meta.defaultValue);
|
|
418
|
+
defaultValues.push({
|
|
419
|
+
colId: colIds.get(name)!,
|
|
420
|
+
value: writer.getBuffer(),
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// If this column is shaped like ScheduleAtAlgebraicType, mark it as the schedule‑at column
|
|
425
|
+
if (scheduled) {
|
|
426
|
+
const algebraicType = builder.typeBuilder.algebraicType;
|
|
427
|
+
if (ScheduleAt.isScheduleAt(algebraicType)) {
|
|
428
|
+
scheduleAtCol = colIds.get(name)!;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// convert explicit multi‑column indexes coming from options.indexes
|
|
434
|
+
for (const indexOpts of userIndexes ?? []) {
|
|
435
|
+
const accessor = indexOpts.accessor;
|
|
436
|
+
if (typeof accessor !== 'string' || accessor.length === 0) {
|
|
437
|
+
const tableLabel = name ?? '<unnamed>';
|
|
438
|
+
const indexLabel = indexOpts.name ?? '<unnamed>';
|
|
439
|
+
throw new TypeError(
|
|
440
|
+
`Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
let algorithm: RawIndexAlgorithm;
|
|
444
|
+
switch (indexOpts.algorithm) {
|
|
445
|
+
case 'btree':
|
|
446
|
+
algorithm = {
|
|
447
|
+
tag: 'BTree',
|
|
448
|
+
value: indexOpts.columns.map(c => colIds.get(c)!),
|
|
449
|
+
};
|
|
450
|
+
break;
|
|
451
|
+
case 'hash':
|
|
452
|
+
algorithm = {
|
|
453
|
+
tag: 'Hash',
|
|
454
|
+
value: indexOpts.columns.map(c => colIds.get(c)!),
|
|
455
|
+
};
|
|
456
|
+
break;
|
|
457
|
+
case 'direct':
|
|
458
|
+
algorithm = { tag: 'Direct', value: colIds.get(indexOpts.column)! };
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Unnamed indexes are assigned a globally unique source name.
|
|
463
|
+
// `accessor` controls the TypeScript property used to access the index.
|
|
464
|
+
// `name` (if present) is preserved as the canonical schema name.
|
|
465
|
+
//
|
|
466
|
+
// IMPORTANT: we intentionally do not reject duplicate accessor names here.
|
|
467
|
+
// This preserves existing behavior for raw table definitions. Downstream
|
|
468
|
+
// runtime consumers decide how duplicates are resolved:
|
|
469
|
+
// - server runtime merges duplicate accessors onto one accessor object
|
|
470
|
+
// - client cache assignment is last-write-wins for duplicate accessors
|
|
471
|
+
indexes.push({
|
|
472
|
+
sourceName: undefined,
|
|
473
|
+
accessorName: accessor,
|
|
474
|
+
algorithm,
|
|
475
|
+
canonicalName: indexOpts.name,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// add explicit constraints from options.constraints
|
|
480
|
+
for (const constraintOpts of opts.constraints ?? []) {
|
|
481
|
+
if (constraintOpts.constraint === 'unique') {
|
|
482
|
+
const data: RawConstraintDefV10['data'] = {
|
|
483
|
+
tag: 'Unique',
|
|
484
|
+
value: { columns: constraintOpts.columns.map(c => colIds.get(c)!) },
|
|
485
|
+
};
|
|
486
|
+
constraints.push({ sourceName: constraintOpts.name, data });
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const productType = row.algebraicType.value as RowBuilder<
|
|
492
|
+
CoerceRow<Row>
|
|
493
|
+
>['algebraicType']['value'];
|
|
494
|
+
|
|
495
|
+
const schedule =
|
|
496
|
+
scheduled && scheduleAtCol !== undefined
|
|
497
|
+
? { scheduleAtCol, reducer: scheduled }
|
|
498
|
+
: undefined;
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
rowType: row as RowBuilder<CoerceRow<Row>>,
|
|
502
|
+
tableName: name,
|
|
503
|
+
rowSpacetimeType: productType,
|
|
504
|
+
tableDef: (ctx, accName) => {
|
|
505
|
+
const tableName = name ?? accName;
|
|
506
|
+
if (row.typeName === undefined) {
|
|
507
|
+
row.typeName = toPascalCase(tableName);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Build index source names using accName
|
|
511
|
+
for (const index of indexes) {
|
|
512
|
+
const cols =
|
|
513
|
+
index.algorithm.tag === 'Direct'
|
|
514
|
+
? [index.algorithm.value]
|
|
515
|
+
: index.algorithm.value;
|
|
516
|
+
|
|
517
|
+
const colS = cols.map(i => colNameList[i]).join('_');
|
|
518
|
+
const sourceName =
|
|
519
|
+
(index.sourceName = `${accName}_${colS}_idx_${index.algorithm.tag.toLowerCase()}`);
|
|
520
|
+
|
|
521
|
+
const { canonicalName } = index;
|
|
522
|
+
if (canonicalName !== undefined) {
|
|
523
|
+
ctx.moduleDef.explicitNames.entries.push(
|
|
524
|
+
ExplicitNameEntry.Index({ sourceName, canonicalName })
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
sourceName: accName,
|
|
531
|
+
productTypeRef: ctx.registerTypesRecursively(row).ref,
|
|
532
|
+
primaryKey: pk,
|
|
533
|
+
indexes,
|
|
534
|
+
constraints,
|
|
535
|
+
sequences,
|
|
536
|
+
tableType: { tag: 'User' },
|
|
537
|
+
tableAccess: { tag: isPublic ? 'Public' : 'Private' },
|
|
538
|
+
defaultValues,
|
|
539
|
+
isEvent,
|
|
540
|
+
};
|
|
541
|
+
},
|
|
542
|
+
// Preserve the declared index options as runtime data so `tableToSchema`
|
|
543
|
+
// can expose them without type-smuggling.
|
|
544
|
+
idxs: userIndexes as OptsIndices<Opts>,
|
|
545
|
+
constraints: constraints as OptsConstraints<Opts>,
|
|
546
|
+
schedule,
|
|
547
|
+
};
|
|
548
|
+
}
|