spacetimedb 2.1.0 → 2.2.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.
Files changed (110) hide show
  1. package/LICENSE.txt +2 -2
  2. package/dist/angular/index.cjs +7 -2
  3. package/dist/angular/index.cjs.map +1 -1
  4. package/dist/angular/index.mjs +7 -2
  5. package/dist/angular/index.mjs.map +1 -1
  6. package/dist/browser/angular/index.mjs +7 -2
  7. package/dist/browser/angular/index.mjs.map +1 -1
  8. package/dist/browser/react/index.mjs +57 -6
  9. package/dist/browser/react/index.mjs.map +1 -1
  10. package/dist/browser/svelte/index.mjs +7 -2
  11. package/dist/browser/svelte/index.mjs.map +1 -1
  12. package/dist/browser/vue/index.mjs +7 -2
  13. package/dist/browser/vue/index.mjs.map +1 -1
  14. package/dist/index.browser.mjs +459 -138
  15. package/dist/index.browser.mjs.map +1 -1
  16. package/dist/index.cjs +459 -138
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.mjs +459 -138
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/lib/binary_reader.d.ts +1 -1
  21. package/dist/lib/binary_reader.d.ts.map +1 -1
  22. package/dist/lib/binary_writer.d.ts +2 -1
  23. package/dist/lib/binary_writer.d.ts.map +1 -1
  24. package/dist/lib/filter.d.ts +2 -1
  25. package/dist/lib/filter.d.ts.map +1 -1
  26. package/dist/lib/table.d.ts +6 -0
  27. package/dist/lib/table.d.ts.map +1 -1
  28. package/dist/min/index.browser.mjs +1 -1
  29. package/dist/min/index.browser.mjs.map +1 -1
  30. package/dist/min/react/index.mjs +1 -1
  31. package/dist/min/react/index.mjs.map +1 -1
  32. package/dist/min/sdk/index.browser.mjs +1 -1
  33. package/dist/min/sdk/index.browser.mjs.map +1 -1
  34. package/dist/react/index.cjs +57 -5
  35. package/dist/react/index.cjs.map +1 -1
  36. package/dist/react/index.d.ts +1 -0
  37. package/dist/react/index.d.ts.map +1 -1
  38. package/dist/react/index.mjs +57 -6
  39. package/dist/react/index.mjs.map +1 -1
  40. package/dist/react/useProcedure.d.ts +4 -0
  41. package/dist/react/useProcedure.d.ts.map +1 -0
  42. package/dist/react/useTable.d.ts +2 -0
  43. package/dist/react/useTable.d.ts.map +1 -1
  44. package/dist/sdk/db_connection_builder.d.ts +3 -3
  45. package/dist/sdk/db_connection_builder.d.ts.map +1 -1
  46. package/dist/sdk/db_connection_impl.d.ts +3 -3
  47. package/dist/sdk/db_connection_impl.d.ts.map +1 -1
  48. package/dist/sdk/decompress.d.ts +1 -1
  49. package/dist/sdk/decompress.d.ts.map +1 -1
  50. package/dist/sdk/index.browser.mjs +459 -138
  51. package/dist/sdk/index.browser.mjs.map +1 -1
  52. package/dist/sdk/index.cjs +459 -138
  53. package/dist/sdk/index.cjs.map +1 -1
  54. package/dist/sdk/index.mjs +459 -138
  55. package/dist/sdk/index.mjs.map +1 -1
  56. package/dist/sdk/table_cache.d.ts +1 -0
  57. package/dist/sdk/table_cache.d.ts.map +1 -1
  58. package/dist/sdk/type_utils.d.ts +4 -1
  59. package/dist/sdk/type_utils.d.ts.map +1 -1
  60. package/dist/sdk/websocket_decompress_adapter.d.ts +5 -21
  61. package/dist/sdk/websocket_decompress_adapter.d.ts.map +1 -1
  62. package/dist/sdk/websocket_protocols.d.ts +6 -0
  63. package/dist/sdk/websocket_protocols.d.ts.map +1 -0
  64. package/dist/sdk/websocket_test_adapter.d.ts +14 -18
  65. package/dist/sdk/websocket_test_adapter.d.ts.map +1 -1
  66. package/dist/sdk/websocket_v3_frames.d.ts +9 -0
  67. package/dist/sdk/websocket_v3_frames.d.ts.map +1 -0
  68. package/dist/sdk/ws.d.ts +26 -1
  69. package/dist/sdk/ws.d.ts.map +1 -1
  70. package/dist/server/http_internal.d.ts.map +1 -1
  71. package/dist/server/index.d.ts +1 -1
  72. package/dist/server/index.d.ts.map +1 -1
  73. package/dist/server/index.mjs +53 -6
  74. package/dist/server/index.mjs.map +1 -1
  75. package/dist/server/runtime.d.ts +29 -2
  76. package/dist/server/runtime.d.ts.map +1 -1
  77. package/dist/svelte/index.cjs +7 -2
  78. package/dist/svelte/index.cjs.map +1 -1
  79. package/dist/svelte/index.mjs +7 -2
  80. package/dist/svelte/index.mjs.map +1 -1
  81. package/dist/tanstack/index.cjs +7 -2
  82. package/dist/tanstack/index.cjs.map +1 -1
  83. package/dist/tanstack/index.mjs +7 -2
  84. package/dist/tanstack/index.mjs.map +1 -1
  85. package/dist/vue/index.cjs +7 -2
  86. package/dist/vue/index.cjs.map +1 -1
  87. package/dist/vue/index.mjs +7 -2
  88. package/dist/vue/index.mjs.map +1 -1
  89. package/package.json +2 -2
  90. package/src/lib/binary_reader.ts +5 -2
  91. package/src/lib/binary_writer.ts +7 -1
  92. package/src/lib/filter.ts +12 -1
  93. package/src/lib/table.ts +9 -1
  94. package/src/react/index.ts +1 -0
  95. package/src/react/useProcedure.ts +60 -0
  96. package/src/react/useTable.ts +17 -2
  97. package/src/sdk/db_connection_builder.ts +16 -7
  98. package/src/sdk/db_connection_impl.ts +404 -89
  99. package/src/sdk/decompress.ts +7 -23
  100. package/src/sdk/table_cache.ts +5 -5
  101. package/src/sdk/type_utils.ts +10 -1
  102. package/src/sdk/websocket_decompress_adapter.ts +15 -77
  103. package/src/sdk/websocket_protocols.ts +25 -0
  104. package/src/sdk/websocket_test_adapter.ts +65 -29
  105. package/src/sdk/websocket_v3_frames.ts +126 -0
  106. package/src/sdk/ws.ts +81 -3
  107. package/src/server/http_internal.ts +10 -1
  108. package/src/server/index.ts +1 -1
  109. package/src/server/runtime.ts +39 -1
  110. package/src/server/sys.d.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacetimedb",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
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": {
@@ -94,7 +94,7 @@
94
94
  "name": "esm (gzip)",
95
95
  "path": "dist/index.mjs",
96
96
  "gzip": true,
97
- "limit": "40 kB"
97
+ "limit": "48 kB"
98
98
  },
99
99
  {
100
100
  "name": "esm (uncompressed)",
@@ -25,8 +25,11 @@ export default class BinaryReader {
25
25
  this.offset = 0;
26
26
  }
27
27
 
28
- reset(view: DataView) {
29
- this.view = view;
28
+ reset(input: Uint8Array | DataView) {
29
+ this.view =
30
+ input instanceof DataView
31
+ ? input
32
+ : new DataView(input.buffer, input.byteOffset, input.byteLength);
30
33
  this.offset = 0;
31
34
  }
32
35
 
@@ -63,7 +63,7 @@ export default class BinaryWriter {
63
63
  return fromByteArray(this.getBuffer());
64
64
  }
65
65
 
66
- getBuffer(): Uint8Array {
66
+ getBuffer(): Uint8Array<ArrayBuffer> {
67
67
  return new Uint8Array(this.buffer.buffer, 0, this.offset);
68
68
  }
69
69
 
@@ -93,6 +93,12 @@ export default class BinaryWriter {
93
93
  this.offset += 1;
94
94
  }
95
95
 
96
+ writeBytes(value: Uint8Array): void {
97
+ this.expandBuffer(value.length);
98
+ new Uint8Array(this.buffer.buffer, this.offset, value.length).set(value);
99
+ this.offset += value.length;
100
+ }
101
+
96
102
  writeI8(value: number): void {
97
103
  this.expandBuffer(1);
98
104
  this.view.setInt8(this.offset, value);
package/src/lib/filter.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { RowType, UntypedTableDef } from './table';
2
+ import { Timestamp } from './timestamp';
2
3
  import { Uuid } from './uuid';
3
4
 
4
- export type Value = string | number | boolean | Uuid;
5
+ export type Value = string | number | boolean | Uuid | Timestamp;
5
6
 
6
7
  export type Expr<Column extends string> =
7
8
  | { type: 'eq'; key: Column; value: Value }
@@ -77,6 +78,13 @@ export function evaluate<Column extends string>(
77
78
  if (v instanceof Uuid && typeof expr.value === 'string') {
78
79
  return v.toString() === expr.value;
79
80
  }
81
+
82
+ if (v instanceof Timestamp) {
83
+ // Value of the Column and passed Value are both Timestamps so compare microseconds.
84
+ if (expr.value instanceof Timestamp) {
85
+ return v.microsSinceUnixEpoch === expr.value.microsSinceUnixEpoch;
86
+ }
87
+ }
80
88
  }
81
89
  return false;
82
90
  }
@@ -103,6 +111,9 @@ function formatValue(v: Value): string {
103
111
  if (v instanceof Uuid) {
104
112
  return `'${v.toString()}'`;
105
113
  }
114
+ if (v instanceof Timestamp) {
115
+ return `'${v.toISOString()}'`;
116
+ }
106
117
 
107
118
  return '';
108
119
  }
package/src/lib/table.ts CHANGED
@@ -268,6 +268,13 @@ export interface TableMethods<TableDef extends UntypedTableDef>
268
268
 
269
269
  /** Delete a row equal to `row`. Returns true if something was deleted. */
270
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;
271
278
  }
272
279
 
273
280
  /**
@@ -404,7 +411,8 @@ export function table<Row extends RowObj, const Opts extends TableOpts<Row>>(
404
411
  });
405
412
  }
406
413
 
407
- if (meta.defaultValue) {
414
+ // Check for defaultValue on the property to allow for 0, false, '', and undefined as defaults
415
+ if (Object.prototype.hasOwnProperty.call(meta, 'defaultValue')) {
408
416
  const writer = new BinaryWriter(16);
409
417
  builder.serialize(writer, meta.defaultValue);
410
418
  defaultValues.push({
@@ -2,3 +2,4 @@ export * from './SpacetimeDBProvider.ts';
2
2
  export { useSpacetimeDB } from './useSpacetimeDB.ts';
3
3
  export { useTable } from './useTable.ts';
4
4
  export { useReducer } from './useReducer.ts';
5
+ export { useProcedure } from './useProcedure.ts';
@@ -0,0 +1,60 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import type { UntypedProcedureDef } from '../sdk/procedures';
3
+ import { useSpacetimeDB } from './useSpacetimeDB';
4
+ import type {
5
+ ProcedureParamsType,
6
+ ProcedureReturnType,
7
+ } from '../sdk/type_utils';
8
+
9
+ export function useProcedure<ProcedureDef extends UntypedProcedureDef>(
10
+ procedureDef: ProcedureDef
11
+ ): (
12
+ ...params: ProcedureParamsType<ProcedureDef>
13
+ ) => Promise<ProcedureReturnType<ProcedureDef>> {
14
+ const { getConnection, isActive } = useSpacetimeDB();
15
+ const procedureName = procedureDef.accessorName;
16
+
17
+ // Holds calls made before the connection exists
18
+ const queueRef = useRef<
19
+ {
20
+ params: ProcedureParamsType<ProcedureDef>;
21
+ resolve: (val: any) => void;
22
+ reject: (err: unknown) => void;
23
+ }[]
24
+ >([]);
25
+
26
+ // Flush when we finally have a connection
27
+ useEffect(() => {
28
+ const conn = getConnection();
29
+ if (!conn) {
30
+ return;
31
+ }
32
+ const fn = (conn.procedures as any)[procedureName] as (
33
+ ...p: ProcedureParamsType<ProcedureDef>
34
+ ) => Promise<ProcedureReturnType<ProcedureDef>>;
35
+ if (queueRef.current.length) {
36
+ const pending = queueRef.current.splice(0);
37
+ for (const item of pending) {
38
+ fn(...item.params).then(item.resolve, item.reject);
39
+ }
40
+ }
41
+ }, [getConnection, procedureName, isActive]);
42
+
43
+ return useCallback(
44
+ (...params: ProcedureParamsType<ProcedureDef>) => {
45
+ const conn = getConnection();
46
+ if (!conn) {
47
+ return new Promise<ProcedureReturnType<ProcedureDef>>(
48
+ (resolve, reject) => {
49
+ queueRef.current.push({ params, resolve, reject });
50
+ }
51
+ );
52
+ }
53
+ const fn = (conn.procedures as any)[procedureName] as (
54
+ ...p: ProcedureParamsType<ProcedureDef>
55
+ ) => Promise<ProcedureReturnType<ProcedureDef>>;
56
+ return fn(...params);
57
+ },
58
+ [getConnection, procedureName]
59
+ );
60
+ }
@@ -24,6 +24,8 @@ export interface UseTableCallbacks<RowType> {
24
24
  onInsert?: (row: RowType) => void;
25
25
  onDelete?: (row: RowType) => void;
26
26
  onUpdate?: (oldRow: RowType, newRow: RowType) => void;
27
+ /** Whether the subscription is active. Defaults to `true`. */
28
+ enabled?: boolean;
27
29
  }
28
30
 
29
31
  type MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';
@@ -67,6 +69,7 @@ export function useTable<TableDef extends UntypedTableDef>(
67
69
  callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>
68
70
  ): [readonly Prettify<RowType<TableDef>>[], boolean] {
69
71
  type UseTableRowType = RowType<TableDef>;
72
+ const enabled = callbacks?.enabled ?? true;
70
73
  const accessorName = getQueryAccessorName(query);
71
74
  const whereExpr = getQueryWhereClause(query);
72
75
 
@@ -93,6 +96,9 @@ export function useTable<TableDef extends UntypedTableDef>(
93
96
  readonly Prettify<UseTableRowType>[],
94
97
  boolean,
95
98
  ] => {
99
+ if (!enabled) {
100
+ return [[], true];
101
+ }
96
102
  const connection = connectionState.getConnection();
97
103
  if (!connection) {
98
104
  return [[], false];
@@ -107,7 +113,7 @@ export function useTable<TableDef extends UntypedTableDef>(
107
113
  // TODO: investigating refactoring so that this is no longer necessary, as we have had genuine bugs with missed deps.
108
114
  // See https://github.com/clockworklabs/SpacetimeDB/pull/4580.
109
115
  // eslint-disable-next-line react-hooks/exhaustive-deps
110
- }, [connectionState, accessorName, querySql, subscribeApplied]);
116
+ }, [connectionState, accessorName, querySql, subscribeApplied, enabled]);
111
117
 
112
118
  // Invalidate the cached snapshot when computeSnapshot changes (e.g. when
113
119
  // subscribeApplied flips to true) so getSnapshot() recomputes on the next
@@ -117,6 +123,10 @@ export function useTable<TableDef extends UntypedTableDef>(
117
123
  }, [computeSnapshot]);
118
124
 
119
125
  useEffect(() => {
126
+ if (!enabled) {
127
+ setSubscribeApplied(false);
128
+ return;
129
+ }
120
130
  const connection = connectionState.getConnection()!;
121
131
  if (connectionState.isActive && connection) {
122
132
  const cancel = connection
@@ -129,10 +139,14 @@ export function useTable<TableDef extends UntypedTableDef>(
129
139
  cancel.unsubscribe();
130
140
  };
131
141
  }
132
- }, [querySql, connectionState.isActive, connectionState]);
142
+ }, [querySql, connectionState.isActive, connectionState, enabled]);
133
143
 
134
144
  const subscribe = useCallback(
135
145
  (onStoreChange: () => void) => {
146
+ if (!enabled) {
147
+ return () => {};
148
+ }
149
+
136
150
  const onInsert = (
137
151
  ctx: EventContextInterface<UntypedRemoteModule>,
138
152
  row: any
@@ -218,6 +232,7 @@ export function useTable<TableDef extends UntypedTableDef>(
218
232
  callbacks?.onDelete,
219
233
  callbacks?.onInsert,
220
234
  callbacks?.onUpdate,
235
+ enabled,
221
236
  ]
222
237
  );
223
238
 
@@ -8,6 +8,7 @@ import type {
8
8
  } from '../';
9
9
  import { ensureMinimumVersionOrThrow } from './version';
10
10
  import { WebsocketDecompressAdapter } from './websocket_decompress_adapter';
11
+ import type { WebSocketFactory } from './ws';
11
12
 
12
13
  /**
13
14
  * The database client connection to a SpacetimeDB server.
@@ -23,10 +24,10 @@ export class DbConnectionBuilder<DbConnection extends DbConnectionImpl<any>> {
23
24
  #identity?: Identity;
24
25
  #token?: string;
25
26
  #emitter: EventEmitter<ConnectionEvent> = new EventEmitter();
26
- #compression: 'gzip' | 'none' = 'gzip';
27
+ #compression: 'gzip' | 'brotli' | 'none' = 'gzip';
27
28
  #lightMode: boolean = false;
28
29
  #confirmedReads?: boolean;
29
- #createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn;
30
+ #createWSFn: WebSocketFactory;
30
31
 
31
32
  /**
32
33
  * Creates a new `DbConnectionBuilder` database client and set the initial parameters.
@@ -42,7 +43,7 @@ export class DbConnectionBuilder<DbConnection extends DbConnectionImpl<any>> {
42
43
  config: DbConnectionConfig<RemoteModuleOf<DbConnection>>
43
44
  ) => DbConnection
44
45
  ) {
45
- this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn;
46
+ this.#createWSFn = WebsocketDecompressAdapter.openWebSocket;
46
47
  }
47
48
 
48
49
  /**
@@ -82,9 +83,7 @@ export class DbConnectionBuilder<DbConnection extends DbConnectionImpl<any>> {
82
83
  return this;
83
84
  }
84
85
 
85
- withWSFn(
86
- createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn
87
- ): this {
86
+ withWSFn(createWSFn: WebSocketFactory): this {
88
87
  this.#createWSFn = createWSFn;
89
88
  return this;
90
89
  }
@@ -94,7 +93,17 @@ export class DbConnectionBuilder<DbConnection extends DbConnectionImpl<any>> {
94
93
  *
95
94
  * @param compression The compression algorithm to use for the connection.
96
95
  */
97
- withCompression(compression: 'gzip' | 'none'): this {
96
+ withCompression(compression: 'gzip' | 'brotli' | 'none'): this {
97
+ if (compression === 'brotli') {
98
+ try {
99
+ new DecompressionStream('brotli' as CompressionFormat);
100
+ } catch (e) {
101
+ throw new TypeError(
102
+ `Brotli compression is not supported by the runtime. Please choose a different compression method.`,
103
+ { cause: e }
104
+ );
105
+ }
106
+ }
98
107
  this.#compression = compression;
99
108
  return this;
100
109
  }