spacetimedb 2.5.0 → 2.6.1
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/dist/browser/react/index.mjs +129 -57
- package/dist/browser/react/index.mjs.map +1 -1
- package/dist/browser/solid/index.mjs +120 -50
- package/dist/browser/solid/index.mjs.map +1 -1
- package/dist/browser/svelte/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/lib/type_builders.d.ts +18 -3
- package/dist/lib/type_builders.d.ts.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/event_context.d.ts +3 -3
- package/dist/sdk/event_context.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/procedures.d.ts +2 -2
- package/dist/sdk/procedures.d.ts.map +1 -1
- package/dist/sdk/reducers.d.ts +3 -4
- package/dist/sdk/reducers.d.ts.map +1 -1
- package/dist/sdk/table_cache.d.ts.map +1 -1
- package/dist/sdk/type_utils.d.ts +3 -3
- package/dist/sdk/type_utils.d.ts.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/views.d.ts +4 -2
- package/dist/server/views.d.ts.map +1 -1
- package/dist/solid/index.cjs +120 -50
- package/dist/solid/index.cjs.map +1 -1
- package/dist/solid/index.mjs +120 -50
- package/dist/solid/index.mjs.map +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.mjs.map +1 -1
- package/dist/svelte/useReducer.d.ts +2 -2
- package/dist/svelte/useReducer.d.ts.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/package.json +22 -21
- package/src/lib/type_builders.test-d.ts +2 -0
- package/src/lib/type_builders.ts +39 -3
- package/src/react/useTable.ts +16 -11
- package/src/sdk/connection_manager.ts +173 -58
- package/src/sdk/db_connection_impl.ts +22 -6
- package/src/sdk/event_context.ts +3 -3
- package/src/sdk/procedures.ts +6 -2
- package/src/sdk/reducers.ts +3 -4
- package/src/sdk/table_cache.ts +2 -1
- package/src/sdk/type_utils.ts +3 -3
- package/src/sdk/websocket_test_adapter.ts +8 -1
- package/src/server/views.ts +5 -2
- package/src/svelte/useReducer.ts +2 -2
- package/LICENSE.txt +0 -759
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spacetimedb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "API and ABI bindings for the SpacetimeDB TypeScript module library",
|
|
5
5
|
"homepage": "https://github.com/clockworklabs/SpacetimeDB#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -22,6 +22,26 @@
|
|
|
22
22
|
"sideEffects": [
|
|
23
23
|
"./src/server/polyfills.ts"
|
|
24
24
|
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build:js": "tsup",
|
|
27
|
+
"build:types": "tsc -p tsconfig.build.json",
|
|
28
|
+
"build": "pnpm -s build:js && pnpm -s build:types",
|
|
29
|
+
"format": "prettier . --write --ignore-path ../../.prettierignore",
|
|
30
|
+
"lint": "eslint . && prettier . --check --ignore-path ../../.prettierignore",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:typecheck": "vitest typecheck --run",
|
|
33
|
+
"coverage": "vitest run --coverage",
|
|
34
|
+
"brotli-size": "brotli-size dist/index.js",
|
|
35
|
+
"size": "pnpm -s build && size-limit",
|
|
36
|
+
"generate:moduledef": "cargo run -p spacetimedb-codegen --example regen-typescript-moduledef && prettier --write src/lib/autogen",
|
|
37
|
+
"generate:client-api": "cargo run -p generate-client-api && prettier --write src/sdk/client_api",
|
|
38
|
+
"generate:test-app": "pnpm --filter @clockworklabs/test-app generate",
|
|
39
|
+
"generate:examples:chat-react-ts": "pnpm --filter @clockworklabs/chat-react-ts generate",
|
|
40
|
+
"generate:examples:react-ts": "pnpm --filter @clockworklabs/react-ts generate",
|
|
41
|
+
"generate:examples:empty": "pnpm --filter @clockworklabs/empty-client generate",
|
|
42
|
+
"generate": "pnpm generate:moduledef && pnpm generate:client-api && pnpm generate:test-app && pnpm generate:examples:chat-react-ts && pnpm generate:examples:react-ts && pnpm generate:examples:empty",
|
|
43
|
+
"prepublishOnly": "pnpm run build && pnpm run test && pnpm run size"
|
|
44
|
+
},
|
|
25
45
|
"main": "dist/index.cjs",
|
|
26
46
|
"module": "dist/index.mjs",
|
|
27
47
|
"types": "dist/index.d.ts",
|
|
@@ -228,24 +248,5 @@
|
|
|
228
248
|
"typescript-eslint": "^8.18.2",
|
|
229
249
|
"vite": "^7.1.5",
|
|
230
250
|
"vitest": "^3.2.4"
|
|
231
|
-
},
|
|
232
|
-
"scripts": {
|
|
233
|
-
"build:js": "tsup",
|
|
234
|
-
"build:types": "tsc -p tsconfig.build.json",
|
|
235
|
-
"build": "pnpm -s build:js && pnpm -s build:types",
|
|
236
|
-
"format": "prettier . --write --ignore-path ../../.prettierignore",
|
|
237
|
-
"lint": "eslint . && prettier . --check --ignore-path ../../.prettierignore",
|
|
238
|
-
"test": "vitest run",
|
|
239
|
-
"test:typecheck": "vitest typecheck --run",
|
|
240
|
-
"coverage": "vitest run --coverage",
|
|
241
|
-
"brotli-size": "brotli-size dist/index.js",
|
|
242
|
-
"size": "pnpm -s build && size-limit",
|
|
243
|
-
"generate:moduledef": "cargo run -p spacetimedb-codegen --example regen-typescript-moduledef && prettier --write src/lib/autogen",
|
|
244
|
-
"generate:client-api": "cargo run -p generate-client-api && prettier --write src/sdk/client_api",
|
|
245
|
-
"generate:test-app": "pnpm --filter @clockworklabs/test-app generate",
|
|
246
|
-
"generate:examples:chat-react-ts": "pnpm --filter @clockworklabs/chat-react-ts generate",
|
|
247
|
-
"generate:examples:react-ts": "pnpm --filter @clockworklabs/react-ts generate",
|
|
248
|
-
"generate:examples:empty": "pnpm --filter @clockworklabs/empty-client generate",
|
|
249
|
-
"generate": "pnpm generate:moduledef && pnpm generate:client-api && pnpm generate:test-app && pnpm generate:examples:chat-react-ts && pnpm generate:examples:react-ts && pnpm generate:examples:empty"
|
|
250
251
|
}
|
|
251
|
-
}
|
|
252
|
+
}
|
|
@@ -35,6 +35,8 @@ const rowOptionOptional = {
|
|
|
35
35
|
};
|
|
36
36
|
type RowOptionOptional = InferTypeOfRow<typeof rowOptionOptional>;
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
38
|
+
const _rowOptionOptionalOmitted: RowOptionOptional = {};
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
38
40
|
const _rowOptionOptionalNone: RowOptionOptional = {
|
|
39
41
|
foo: undefined,
|
|
40
42
|
};
|
package/src/lib/type_builders.ts
CHANGED
|
@@ -39,9 +39,45 @@ export type Infer<T> = T extends RowObj
|
|
|
39
39
|
/**
|
|
40
40
|
* Helper type to extract the type of a row from an object.
|
|
41
41
|
*/
|
|
42
|
-
|
|
43
|
-
[K in keyof T & string]
|
|
44
|
-
|
|
42
|
+
type OptionalRowKeys<T extends RowObj> = {
|
|
43
|
+
[K in keyof T & string]-?: CollapseColumn<T[K]> extends OptionBuilder<any>
|
|
44
|
+
? K
|
|
45
|
+
: never;
|
|
46
|
+
}[keyof T & string];
|
|
47
|
+
|
|
48
|
+
type RequiredRowKeys<T extends RowObj> = Exclude<
|
|
49
|
+
keyof T & string,
|
|
50
|
+
OptionalRowKeys<T>
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
export type InferTypeOfRow<T extends RowObj> = Prettify<
|
|
54
|
+
{
|
|
55
|
+
[K in RequiredRowKeys<T>]: InferTypeOfTypeBuilder<CollapseColumn<T[K]>>;
|
|
56
|
+
} & {
|
|
57
|
+
[K in OptionalRowKeys<T>]?: InferTypeOfTypeBuilder<CollapseColumn<T[K]>>;
|
|
58
|
+
}
|
|
59
|
+
>;
|
|
60
|
+
|
|
61
|
+
type OptionalParamKeys<T extends RowObj> = {
|
|
62
|
+
[K in keyof T & string]-?: undefined extends InferTypeOfTypeBuilder<
|
|
63
|
+
CollapseColumn<T[K]>
|
|
64
|
+
>
|
|
65
|
+
? K
|
|
66
|
+
: never;
|
|
67
|
+
}[keyof T & string];
|
|
68
|
+
|
|
69
|
+
type RequiredParamKeys<T extends RowObj> = Exclude<
|
|
70
|
+
keyof T & string,
|
|
71
|
+
OptionalParamKeys<T>
|
|
72
|
+
>;
|
|
73
|
+
|
|
74
|
+
export type InferTypeOfParams<T extends RowObj> = Prettify<
|
|
75
|
+
{
|
|
76
|
+
[K in RequiredParamKeys<T>]: InferTypeOfTypeBuilder<CollapseColumn<T[K]>>;
|
|
77
|
+
} & {
|
|
78
|
+
[K in OptionalParamKeys<T>]?: InferTypeOfTypeBuilder<CollapseColumn<T[K]>>;
|
|
79
|
+
}
|
|
80
|
+
>;
|
|
45
81
|
|
|
46
82
|
/**
|
|
47
83
|
* Helper type to extract the type of a row from an object.
|
package/src/react/useTable.ts
CHANGED
|
@@ -127,18 +127,23 @@ export function useTable<TableDef extends UntypedTableDef>(
|
|
|
127
127
|
setSubscribeApplied(false);
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
|
-
const connection = connectionState.getConnection()
|
|
131
|
-
if (connectionState.isActive
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
.subscribe(querySql);
|
|
138
|
-
return () => {
|
|
139
|
-
cancel.unsubscribe();
|
|
140
|
-
};
|
|
130
|
+
const connection = connectionState.getConnection();
|
|
131
|
+
if (!connectionState.isActive || !connection) {
|
|
132
|
+
// The connection dropped (or was replaced and has not reconnected
|
|
133
|
+
// yet), so any previously applied subscription no longer reflects the
|
|
134
|
+
// current cache. Report not-ready until the new subscription applies.
|
|
135
|
+
setSubscribeApplied(false);
|
|
136
|
+
return;
|
|
141
137
|
}
|
|
138
|
+
const cancel = connection
|
|
139
|
+
.subscriptionBuilder()
|
|
140
|
+
.onApplied(() => {
|
|
141
|
+
setSubscribeApplied(true);
|
|
142
|
+
})
|
|
143
|
+
.subscribe(querySql);
|
|
144
|
+
return () => {
|
|
145
|
+
cancel.unsubscribe();
|
|
146
|
+
};
|
|
142
147
|
}, [querySql, connectionState.isActive, connectionState, enabled]);
|
|
143
148
|
|
|
144
149
|
const subscribe = useCallback(
|
|
@@ -46,12 +46,30 @@ export type ConnectionState = {
|
|
|
46
46
|
|
|
47
47
|
type Listener = () => void;
|
|
48
48
|
|
|
49
|
+
export const CONNECTION_MANAGER_RECONNECT_BASE_DELAY_MS = 1000;
|
|
50
|
+
export const CONNECTION_MANAGER_RECONNECT_MAX_DELAY_MS = 30_000;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Computes the reconnect delay for the given attempt (0-based) using
|
|
54
|
+
* exponential backoff: the base delay doubles with each consecutive failed
|
|
55
|
+
* attempt, capped at the maximum delay.
|
|
56
|
+
*/
|
|
57
|
+
export function connectionManagerReconnectDelayMs(attempt: number): number {
|
|
58
|
+
return Math.min(
|
|
59
|
+
CONNECTION_MANAGER_RECONNECT_BASE_DELAY_MS * 2 ** attempt,
|
|
60
|
+
CONNECTION_MANAGER_RECONNECT_MAX_DELAY_MS
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
49
64
|
type ManagedConnection = {
|
|
50
65
|
connection?: DbConnectionImpl<any>;
|
|
66
|
+
builder?: DbConnectionBuilder<any>;
|
|
51
67
|
refCount: number;
|
|
52
68
|
state: ConnectionState;
|
|
53
69
|
listeners: Set<Listener>;
|
|
54
70
|
pendingRelease: ReturnType<typeof setTimeout> | null;
|
|
71
|
+
reconnectTimer: ReturnType<typeof setTimeout> | null;
|
|
72
|
+
reconnectAttempt: number;
|
|
55
73
|
onConnect?: (conn: DbConnectionImpl<any>) => void;
|
|
56
74
|
onDisconnect?: (ctx: ErrorContextInterface<any>, error?: Error) => void;
|
|
57
75
|
onConnectError?: (ctx: ErrorContextInterface<any>, error: Error) => void;
|
|
@@ -91,10 +109,13 @@ class ConnectionManagerImpl {
|
|
|
91
109
|
}
|
|
92
110
|
const managed: ManagedConnection = {
|
|
93
111
|
connection: undefined,
|
|
112
|
+
builder: undefined,
|
|
94
113
|
refCount: 0,
|
|
95
114
|
state: defaultState(),
|
|
96
115
|
listeners: new Set(),
|
|
97
116
|
pendingRelease: null,
|
|
117
|
+
reconnectTimer: null,
|
|
118
|
+
reconnectAttempt: 0,
|
|
98
119
|
};
|
|
99
120
|
this.#connections.set(key, managed);
|
|
100
121
|
return managed;
|
|
@@ -106,47 +127,25 @@ class ConnectionManagerImpl {
|
|
|
106
127
|
}
|
|
107
128
|
}
|
|
108
129
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
* @returns The managed connection instance
|
|
117
|
-
*/
|
|
118
|
-
retain<T extends DbConnectionImpl<any>>(
|
|
119
|
-
key: string,
|
|
120
|
-
builder: DbConnectionBuilder<T>
|
|
121
|
-
): T {
|
|
122
|
-
const managed = this.#ensureEntry(key);
|
|
123
|
-
if (managed.pendingRelease) {
|
|
124
|
-
clearTimeout(managed.pendingRelease);
|
|
125
|
-
managed.pendingRelease = null;
|
|
126
|
-
}
|
|
127
|
-
managed.refCount += 1;
|
|
128
|
-
if (managed.connection) {
|
|
129
|
-
return managed.connection as T;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const connection = builder.build();
|
|
133
|
-
managed.connection = connection;
|
|
134
|
-
|
|
135
|
-
const updateState = (updates: Partial<ConnectionState>) => {
|
|
136
|
-
managed.state = { ...managed.state, ...updates };
|
|
137
|
-
this.#notify(managed);
|
|
138
|
-
};
|
|
130
|
+
#updateState(
|
|
131
|
+
managed: ManagedConnection,
|
|
132
|
+
updates: Partial<ConnectionState>
|
|
133
|
+
): void {
|
|
134
|
+
managed.state = { ...managed.state, ...updates };
|
|
135
|
+
this.#notify(managed);
|
|
136
|
+
}
|
|
139
137
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
connectionId: connection.connectionId,
|
|
145
|
-
connectionError: undefined,
|
|
146
|
-
});
|
|
138
|
+
#ensureCallbacks(managed: ManagedConnection): void {
|
|
139
|
+
if (managed.onConnect) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
147
142
|
|
|
148
143
|
managed.onConnect = conn => {
|
|
149
|
-
|
|
144
|
+
if (conn !== managed.connection) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
managed.reconnectAttempt = 0;
|
|
148
|
+
this.#updateState(managed, {
|
|
150
149
|
isActive: conn.isActive,
|
|
151
150
|
identity: conn.identity,
|
|
152
151
|
token: conn.token,
|
|
@@ -156,26 +155,145 @@ class ConnectionManagerImpl {
|
|
|
156
155
|
};
|
|
157
156
|
|
|
158
157
|
managed.onDisconnect = (ctx, error) => {
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
if (ctx !== managed.connection) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.#updateState(managed, {
|
|
162
|
+
isActive: false,
|
|
161
163
|
connectionError: error ?? undefined,
|
|
162
164
|
});
|
|
165
|
+
this.#scheduleReconnect(managed);
|
|
163
166
|
};
|
|
164
167
|
|
|
165
168
|
managed.onConnectError = (ctx, error) => {
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
if (ctx !== managed.connection) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.#updateState(managed, {
|
|
173
|
+
isActive: false,
|
|
168
174
|
connectionError: error,
|
|
169
175
|
});
|
|
176
|
+
this.#scheduleReconnect(managed);
|
|
170
177
|
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#attachCallbacks<T extends DbConnectionImpl<any>>(
|
|
181
|
+
managed: ManagedConnection,
|
|
182
|
+
builder: DbConnectionBuilder<T>
|
|
183
|
+
): void {
|
|
184
|
+
this.#ensureCallbacks(managed);
|
|
185
|
+
builder.onConnect(managed.onConnect!);
|
|
186
|
+
builder.onDisconnect(managed.onDisconnect!);
|
|
187
|
+
builder.onConnectError(managed.onConnectError!);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#detachCallbacks(
|
|
191
|
+
managed: ManagedConnection,
|
|
192
|
+
connection: DbConnectionImpl<any>
|
|
193
|
+
): void {
|
|
194
|
+
if (managed.onConnect) {
|
|
195
|
+
connection.removeOnConnect(managed.onConnect as any);
|
|
196
|
+
}
|
|
197
|
+
if (managed.onDisconnect) {
|
|
198
|
+
connection.removeOnDisconnect(managed.onDisconnect as any);
|
|
199
|
+
}
|
|
200
|
+
if (managed.onConnectError) {
|
|
201
|
+
connection.removeOnConnectError(managed.onConnectError as any);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
171
204
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
builder
|
|
205
|
+
#buildManagedConnection<T extends DbConnectionImpl<any>>(
|
|
206
|
+
managed: ManagedConnection,
|
|
207
|
+
builder: DbConnectionBuilder<T>
|
|
208
|
+
): T {
|
|
209
|
+
managed.builder = builder;
|
|
210
|
+
const connection = builder.build();
|
|
211
|
+
managed.connection = connection;
|
|
212
|
+
this.#attachCallbacks(managed, builder);
|
|
213
|
+
|
|
214
|
+
this.#updateState(managed, {
|
|
215
|
+
isActive: connection.isActive,
|
|
216
|
+
identity: connection.identity,
|
|
217
|
+
token: connection.token,
|
|
218
|
+
connectionId: connection.connectionId,
|
|
219
|
+
connectionError: undefined,
|
|
220
|
+
});
|
|
175
221
|
|
|
176
222
|
return connection as T;
|
|
177
223
|
}
|
|
178
224
|
|
|
225
|
+
#scheduleReconnect(managed: ManagedConnection): void {
|
|
226
|
+
if (
|
|
227
|
+
managed.refCount <= 0 ||
|
|
228
|
+
managed.pendingRelease ||
|
|
229
|
+
managed.reconnectTimer ||
|
|
230
|
+
!managed.builder
|
|
231
|
+
) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const connection = managed.connection;
|
|
236
|
+
if (connection) {
|
|
237
|
+
this.#detachCallbacks(managed, connection);
|
|
238
|
+
}
|
|
239
|
+
managed.connection = undefined;
|
|
240
|
+
|
|
241
|
+
// The application asked this connection to close; don't fight it. A
|
|
242
|
+
// subsequent retain() will still build a fresh connection.
|
|
243
|
+
if (connection?.isDisconnectRequested) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const delay = connectionManagerReconnectDelayMs(managed.reconnectAttempt);
|
|
248
|
+
managed.reconnectAttempt += 1;
|
|
249
|
+
managed.reconnectTimer = setTimeout(() => {
|
|
250
|
+
managed.reconnectTimer = null;
|
|
251
|
+
if (
|
|
252
|
+
managed.refCount <= 0 ||
|
|
253
|
+
managed.pendingRelease ||
|
|
254
|
+
managed.connection ||
|
|
255
|
+
!managed.builder
|
|
256
|
+
) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
this.#buildManagedConnection(managed, managed.builder);
|
|
261
|
+
}, delay);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Retains a connection, incrementing its reference count.
|
|
266
|
+
* Creates the connection on first call; returns existing connection on subsequent calls.
|
|
267
|
+
* Cancels any pending release if the connection was about to be cleaned up.
|
|
268
|
+
*
|
|
269
|
+
* @param key - Unique identifier for the connection (use getKey to generate)
|
|
270
|
+
* @param builder - Connection builder to create the connection if needed
|
|
271
|
+
* @returns The managed connection instance
|
|
272
|
+
*/
|
|
273
|
+
retain<T extends DbConnectionImpl<any>>(
|
|
274
|
+
key: string,
|
|
275
|
+
builder: DbConnectionBuilder<T>
|
|
276
|
+
): T {
|
|
277
|
+
const managed = this.#ensureEntry(key);
|
|
278
|
+
if (managed.pendingRelease) {
|
|
279
|
+
clearTimeout(managed.pendingRelease);
|
|
280
|
+
managed.pendingRelease = null;
|
|
281
|
+
}
|
|
282
|
+
if (managed.reconnectTimer) {
|
|
283
|
+
clearTimeout(managed.reconnectTimer);
|
|
284
|
+
managed.reconnectTimer = null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
managed.refCount += 1;
|
|
288
|
+
managed.builder = builder;
|
|
289
|
+
|
|
290
|
+
if (managed.connection) {
|
|
291
|
+
return managed.connection as T;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return this.#buildManagedConnection(managed, builder);
|
|
295
|
+
}
|
|
296
|
+
|
|
179
297
|
release(key: string): void {
|
|
180
298
|
const managed = this.#connections.get(key);
|
|
181
299
|
if (!managed) {
|
|
@@ -187,24 +305,21 @@ class ConnectionManagerImpl {
|
|
|
187
305
|
return;
|
|
188
306
|
}
|
|
189
307
|
|
|
308
|
+
if (managed.reconnectTimer) {
|
|
309
|
+
clearTimeout(managed.reconnectTimer);
|
|
310
|
+
managed.reconnectTimer = null;
|
|
311
|
+
}
|
|
312
|
+
|
|
190
313
|
managed.pendingRelease = setTimeout(() => {
|
|
191
314
|
managed.pendingRelease = null;
|
|
192
315
|
if (managed.refCount > 0) {
|
|
193
316
|
return;
|
|
194
317
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
managed.connection.removeOnDisconnect(managed.onDisconnect as any);
|
|
201
|
-
}
|
|
202
|
-
if (managed.onConnectError) {
|
|
203
|
-
managed.connection.removeOnConnectError(
|
|
204
|
-
managed.onConnectError as any
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
managed.connection.disconnect();
|
|
318
|
+
const connection = managed.connection;
|
|
319
|
+
managed.connection = undefined;
|
|
320
|
+
if (connection) {
|
|
321
|
+
this.#detachCallbacks(managed, connection);
|
|
322
|
+
connection.disconnect();
|
|
208
323
|
}
|
|
209
324
|
this.#connections.delete(key);
|
|
210
325
|
}, 0);
|
|
@@ -23,7 +23,12 @@ import {
|
|
|
23
23
|
type SubscriptionEventContextInterface,
|
|
24
24
|
} from './event_context.ts';
|
|
25
25
|
import { EventEmitter } from './event_emitter.ts';
|
|
26
|
-
import type {
|
|
26
|
+
import type {
|
|
27
|
+
Deserializer,
|
|
28
|
+
Identity,
|
|
29
|
+
InferTypeOfParams,
|
|
30
|
+
Serializer,
|
|
31
|
+
} from '../';
|
|
27
32
|
import type {
|
|
28
33
|
ProcedureResultMessage,
|
|
29
34
|
ReducerResultMessage,
|
|
@@ -138,6 +143,14 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
138
143
|
*/
|
|
139
144
|
isActive = false;
|
|
140
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Whether `disconnect()` has been called on this connection.
|
|
148
|
+
* Once requested, the connection will not be reused: managed environments
|
|
149
|
+
* (such as the React `SpacetimeDBProvider`) use this to avoid reconnecting
|
|
150
|
+
* after an intentional disconnect.
|
|
151
|
+
*/
|
|
152
|
+
isDisconnectRequested = false;
|
|
153
|
+
|
|
141
154
|
/**
|
|
142
155
|
* This connection's public identity.
|
|
143
156
|
*/
|
|
@@ -321,12 +334,12 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
321
334
|
this.ws = v;
|
|
322
335
|
|
|
323
336
|
this.ws.onclose = () => {
|
|
324
|
-
this.#emitter.emit('disconnect', this);
|
|
325
337
|
this.isActive = false;
|
|
338
|
+
this.#emitter.emit('disconnect', this);
|
|
326
339
|
};
|
|
327
340
|
this.ws.onerror = (e: ErrorEvent) => {
|
|
328
|
-
this.#emitter.emit('connectError', this, e);
|
|
329
341
|
this.isActive = false;
|
|
342
|
+
this.#emitter.emit('connectError', this, e);
|
|
330
343
|
};
|
|
331
344
|
this.ws.onopen = this.#handleOnOpen.bind(this);
|
|
332
345
|
this.ws.onmessage = this.#handleOnMessage.bind(this);
|
|
@@ -375,7 +388,9 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
375
388
|
const { serialize: serializeArgs } =
|
|
376
389
|
this.#reducerArgsSerializers[reducerName];
|
|
377
390
|
|
|
378
|
-
(out as any)[key] = (
|
|
391
|
+
(out as any)[key] = (
|
|
392
|
+
params: InferTypeOfParams<typeof reducer.params>
|
|
393
|
+
) => {
|
|
379
394
|
const writer = this.#reducerArgsEncoder;
|
|
380
395
|
writer.clear();
|
|
381
396
|
serializeArgs(writer, params);
|
|
@@ -406,7 +421,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
406
421
|
this.#procedureSerializers[procedureName];
|
|
407
422
|
|
|
408
423
|
(out as any)[key] = (
|
|
409
|
-
params:
|
|
424
|
+
params: InferTypeOfParams<typeof procedure.params>
|
|
410
425
|
): Promise<any> => {
|
|
411
426
|
writer.clear();
|
|
412
427
|
serializeArgs(writer, params);
|
|
@@ -428,7 +443,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
428
443
|
event: Event<
|
|
429
444
|
ReducerEventInfo<
|
|
430
445
|
RemoteModule['reducers'][number]['name'],
|
|
431
|
-
|
|
446
|
+
InferTypeOfParams<RemoteModule['reducers'][number]['params']>
|
|
432
447
|
>
|
|
433
448
|
>
|
|
434
449
|
): EventContextInterface<RemoteModule> {
|
|
@@ -1292,6 +1307,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
1292
1307
|
* ```
|
|
1293
1308
|
*/
|
|
1294
1309
|
disconnect(): void {
|
|
1310
|
+
this.isDisconnectRequested = true;
|
|
1295
1311
|
this.wsPromise.then(ws => ws?.close());
|
|
1296
1312
|
}
|
|
1297
1313
|
|
package/src/sdk/event_context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InferTypeOfParams } from '../lib/type_builders.ts';
|
|
2
2
|
import type { DbContext } from './db_context';
|
|
3
3
|
import type { Event } from './event.ts';
|
|
4
4
|
import type { ReducerEvent } from './reducer_event.ts';
|
|
@@ -13,7 +13,7 @@ export interface EventContextInterface<RemoteModule extends UntypedRemoteModule>
|
|
|
13
13
|
event: Event<
|
|
14
14
|
ReducerEventInfo<
|
|
15
15
|
RemoteModule['reducers'][number]['name'],
|
|
16
|
-
|
|
16
|
+
InferTypeOfParams<RemoteModule['reducers'][number]['params']>
|
|
17
17
|
>
|
|
18
18
|
>;
|
|
19
19
|
}
|
|
@@ -25,7 +25,7 @@ export interface ReducerEventContextInterface<
|
|
|
25
25
|
event: ReducerEvent<
|
|
26
26
|
ReducerEventInfo<
|
|
27
27
|
RemoteModule['reducers'][number]['name'],
|
|
28
|
-
|
|
28
|
+
InferTypeOfParams<RemoteModule['reducers'][number]['params']>
|
|
29
29
|
>
|
|
30
30
|
>;
|
|
31
31
|
}
|
package/src/sdk/procedures.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ParamsObj } from '../lib/reducers';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
Infer,
|
|
4
|
+
InferTypeOfParams,
|
|
5
|
+
TypeBuilder,
|
|
6
|
+
} from '../lib/type_builders';
|
|
3
7
|
import type { CamelCase } from '../lib/type_util';
|
|
4
8
|
import { coerceParams, toCamelCase, type CoerceParams } from '../lib/util';
|
|
5
9
|
import type { UntypedRemoteModule } from './spacetime_module';
|
|
@@ -20,7 +24,7 @@ export type ProceduresView<RemoteModule> = IfAny<
|
|
|
20
24
|
? // x: camelCase(name)
|
|
21
25
|
{
|
|
22
26
|
[K in RemoteModule['procedures'][number] as K['accessorName']]: (
|
|
23
|
-
params:
|
|
27
|
+
params: InferTypeOfParams<K['params']>
|
|
24
28
|
) => Promise<Infer<K['returnType']>>;
|
|
25
29
|
}
|
|
26
30
|
: never
|
package/src/sdk/reducers.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { ProductType } from '../lib/algebraic_type';
|
|
2
2
|
import type { ReducerSchema } from '../lib/reducer_schema';
|
|
3
3
|
import type { ParamsObj } from '../lib/reducers';
|
|
4
|
-
import type
|
|
5
|
-
import { RowBuilder, type InferTypeOfRow } from '../lib/type_builders';
|
|
4
|
+
import { RowBuilder, type InferTypeOfParams } from '../lib/type_builders';
|
|
6
5
|
import { toCamelCase } from '../lib/util';
|
|
7
6
|
import type { SubscriptionEventContextInterface } from './event_context';
|
|
8
7
|
import type { UntypedRemoteModule } from './spacetime_module';
|
|
@@ -26,7 +25,7 @@ export type ReducersView<RemoteModule> = IfAny<
|
|
|
26
25
|
RemoteModule extends UntypedRemoteModule
|
|
27
26
|
? {
|
|
28
27
|
[K in RemoteModule['reducers'][number] as K['accessorName']]: (
|
|
29
|
-
params:
|
|
28
|
+
params: InferTypeOfParams<K['params']>
|
|
30
29
|
) => Promise<void>;
|
|
31
30
|
}
|
|
32
31
|
: never
|
|
@@ -43,7 +42,7 @@ export type ReducerEventInfo<
|
|
|
43
42
|
export type UntypedReducerDef = {
|
|
44
43
|
name: string;
|
|
45
44
|
accessorName: string;
|
|
46
|
-
params:
|
|
45
|
+
params: ParamsObj;
|
|
47
46
|
paramsType: ProductType;
|
|
48
47
|
};
|
|
49
48
|
|
package/src/sdk/table_cache.ts
CHANGED
|
@@ -123,7 +123,8 @@ export class TableCacheImpl<
|
|
|
123
123
|
const columns = idx.columns;
|
|
124
124
|
|
|
125
125
|
// Extract the tuple key for this btree index (column order preserved)
|
|
126
|
-
const getKey = (row: Row): readonly unknown[] =>
|
|
126
|
+
const getKey = (row: Row): readonly unknown[] =>
|
|
127
|
+
columns.map(c => (row as Record<string, unknown>)[c]);
|
|
127
128
|
|
|
128
129
|
// The server’s ranged scan fixes all prefix cols to equality and applies
|
|
129
130
|
// the bound only to the *last* term. We mirror that.
|
package/src/sdk/type_utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Infer,
|
|
1
|
+
import type { Infer, InferTypeOfParams } from '.';
|
|
2
2
|
import type { Prettify } from '../lib/type_util';
|
|
3
3
|
import type { UntypedProcedureDef } from './procedures';
|
|
4
4
|
import type { UntypedReducerDef } from './reducers';
|
|
@@ -7,11 +7,11 @@ export type IsEmptyObject<T> = [keyof T] extends [never] ? true : false;
|
|
|
7
7
|
export type MaybeParams<T> = IsEmptyObject<T> extends true ? [] : [params: T];
|
|
8
8
|
|
|
9
9
|
export type ParamsType<R extends UntypedReducerDef> = MaybeParams<
|
|
10
|
-
Prettify<
|
|
10
|
+
Prettify<InferTypeOfParams<R['params']>>
|
|
11
11
|
>;
|
|
12
12
|
|
|
13
13
|
export type ProcedureParamsType<P extends UntypedProcedureDef> = MaybeParams<
|
|
14
|
-
Prettify<
|
|
14
|
+
Prettify<InferTypeOfParams<P['params']>>
|
|
15
15
|
>;
|
|
16
16
|
|
|
17
17
|
export type ProcedureReturnType<P extends UntypedProcedureDef> = Infer<
|
|
@@ -19,6 +19,7 @@ class WebsocketTestAdapter implements WebSocketAdapter {
|
|
|
19
19
|
#onclose: (ev: CloseEvent) => void = () => {};
|
|
20
20
|
#onopen: () => void = () => {};
|
|
21
21
|
#onmessage: (msg: { data: Uint8Array }) => void = () => {};
|
|
22
|
+
#onerror: (msg: ErrorEvent) => void = () => {};
|
|
22
23
|
|
|
23
24
|
constructor() {
|
|
24
25
|
this.messageQueue = [];
|
|
@@ -39,7 +40,13 @@ class WebsocketTestAdapter implements WebSocketAdapter {
|
|
|
39
40
|
this.#onmessage = handler;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
set onerror(
|
|
43
|
+
set onerror(handler: (msg: ErrorEvent) => void) {
|
|
44
|
+
this.#onerror = handler;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
error(error: Error): void {
|
|
48
|
+
this.#onerror(error as unknown as ErrorEvent);
|
|
49
|
+
}
|
|
43
50
|
|
|
44
51
|
send(message: Uint8Array<ArrayBuffer>): void {
|
|
45
52
|
const rawMessage = message.slice();
|
package/src/server/views.ts
CHANGED
|
@@ -327,8 +327,11 @@ type ViewInfo<F> = {
|
|
|
327
327
|
returnTypeBaseSize: number;
|
|
328
328
|
};
|
|
329
329
|
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
type AnyViewFn = (ctx: ViewCtx<any>, params: any) => any;
|
|
331
|
+
type AnyAnonymousViewFn = (ctx: AnonymousViewCtx<any>, params: any) => any;
|
|
332
|
+
|
|
333
|
+
export type Views = ViewInfo<AnyViewFn>[];
|
|
334
|
+
export type AnonViews = ViewInfo<AnyAnonymousViewFn>[];
|
|
332
335
|
|
|
333
336
|
// A helper to get the product type out of a type builder.
|
|
334
337
|
// This is only non-never if the type builder is an array.
|