spacetimedb 2.0.3 → 2.1.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 (88) hide show
  1. package/LICENSE.txt +2 -2
  2. package/dist/angular/index.cjs +5 -1
  3. package/dist/angular/index.cjs.map +1 -1
  4. package/dist/angular/index.mjs +5 -1
  5. package/dist/angular/index.mjs.map +1 -1
  6. package/dist/browser/angular/index.mjs +5 -1
  7. package/dist/browser/angular/index.mjs.map +1 -1
  8. package/dist/browser/react/index.mjs +8 -1
  9. package/dist/browser/react/index.mjs.map +1 -1
  10. package/dist/browser/svelte/index.mjs +5 -1
  11. package/dist/browser/svelte/index.mjs.map +1 -1
  12. package/dist/browser/vue/index.mjs +5 -1
  13. package/dist/browser/vue/index.mjs.map +1 -1
  14. package/dist/index.browser.mjs +148 -100
  15. package/dist/index.browser.mjs.map +1 -1
  16. package/dist/index.cjs +148 -100
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.mjs +148 -100
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/lib/algebraic_type.d.ts.map +1 -1
  21. package/dist/lib/binary_writer.d.ts +1 -0
  22. package/dist/lib/binary_writer.d.ts.map +1 -1
  23. package/dist/lib/indexes.d.ts +1 -1
  24. package/dist/lib/indexes.d.ts.map +1 -1
  25. package/dist/lib/query.d.ts +14 -7
  26. package/dist/lib/query.d.ts.map +1 -1
  27. package/dist/lib/schema.d.ts +2 -0
  28. package/dist/lib/schema.d.ts.map +1 -1
  29. package/dist/lib/table.d.ts +25 -2
  30. package/dist/lib/table.d.ts.map +1 -1
  31. package/dist/min/index.browser.mjs +1 -1
  32. package/dist/min/index.browser.mjs.map +1 -1
  33. package/dist/min/react/index.mjs +1 -1
  34. package/dist/min/react/index.mjs.map +1 -1
  35. package/dist/min/sdk/index.browser.mjs +1 -1
  36. package/dist/min/sdk/index.browser.mjs.map +1 -1
  37. package/dist/react/index.cjs +8 -1
  38. package/dist/react/index.cjs.map +1 -1
  39. package/dist/react/index.mjs +8 -1
  40. package/dist/react/index.mjs.map +1 -1
  41. package/dist/react/useTable.d.ts.map +1 -1
  42. package/dist/sdk/db_connection_impl.d.ts.map +1 -1
  43. package/dist/sdk/index.browser.mjs +144 -98
  44. package/dist/sdk/index.browser.mjs.map +1 -1
  45. package/dist/sdk/index.cjs +144 -98
  46. package/dist/sdk/index.cjs.map +1 -1
  47. package/dist/sdk/index.mjs +144 -98
  48. package/dist/sdk/index.mjs.map +1 -1
  49. package/dist/sdk/table_cache.d.ts.map +1 -1
  50. package/dist/sdk/websocket_decompress_adapter.d.ts +17 -7
  51. package/dist/sdk/websocket_decompress_adapter.d.ts.map +1 -1
  52. package/dist/sdk/websocket_test_adapter.d.ts +3 -2
  53. package/dist/sdk/websocket_test_adapter.d.ts.map +1 -1
  54. package/dist/server/index.d.ts +1 -0
  55. package/dist/server/index.d.ts.map +1 -1
  56. package/dist/server/index.mjs +88 -30
  57. package/dist/server/index.mjs.map +1 -1
  58. package/dist/svelte/index.cjs +5 -1
  59. package/dist/svelte/index.cjs.map +1 -1
  60. package/dist/svelte/index.mjs +5 -1
  61. package/dist/svelte/index.mjs.map +1 -1
  62. package/dist/tanstack/SpacetimeDBQueryClient.d.ts +1 -0
  63. package/dist/tanstack/SpacetimeDBQueryClient.d.ts.map +1 -1
  64. package/dist/tanstack/index.cjs +26 -1
  65. package/dist/tanstack/index.cjs.map +1 -1
  66. package/dist/tanstack/index.mjs +26 -1
  67. package/dist/tanstack/index.mjs.map +1 -1
  68. package/dist/vue/index.cjs +5 -1
  69. package/dist/vue/index.cjs.map +1 -1
  70. package/dist/vue/index.mjs +5 -1
  71. package/dist/vue/index.mjs.map +1 -1
  72. package/package.json +1 -1
  73. package/src/lib/algebraic_type.ts +5 -1
  74. package/src/lib/binary_writer.ts +4 -0
  75. package/src/lib/indexes.ts +1 -1
  76. package/src/lib/query.ts +90 -25
  77. package/src/lib/schema.ts +66 -24
  78. package/src/lib/table.ts +47 -10
  79. package/src/react/useTable.ts +5 -0
  80. package/src/sdk/db_connection_impl.ts +38 -43
  81. package/src/sdk/table_cache.ts +14 -11
  82. package/src/sdk/websocket_decompress_adapter.ts +42 -45
  83. package/src/sdk/websocket_test_adapter.ts +3 -2
  84. package/src/server/index.ts +1 -0
  85. package/src/server/runtime.ts +7 -3
  86. package/src/server/schema.test-d.ts +37 -0
  87. package/src/server/view.test-d.ts +6 -0
  88. package/src/tanstack/SpacetimeDBQueryClient.ts +24 -0
@@ -583,6 +583,9 @@ var BinaryWriter = class {
583
583
  constructor(init) {
584
584
  this.buffer = typeof init === "number" ? new ResizableBuffer(init) : init;
585
585
  }
586
+ clear() {
587
+ this.offset = 0;
588
+ }
586
589
  reset(buffer) {
587
590
  this.buffer = buffer;
588
591
  this.offset = 0;
@@ -1176,7 +1179,8 @@ writer.offset += ${primitiveSizes[tag]};` : `writer.write${tag}(value.${name});`
1176
1179
  const result = { ${ty.elements.map(getElementInitializer).join(", ")} };
1177
1180
  const view = reader.view;
1178
1181
  ${ty.elements.map(
1179
- ({ name, algebraicType: { tag } }) => tag in primitiveJSName ? `result.${name} = view.get${primitiveJSName[tag]}(reader.offset, ${primitiveSizes[tag] > 1 ? "true" : ""});
1182
+ ({ name, algebraicType: { tag } }) => tag in primitiveJSName ? tag === "Bool" ? `result.${name} = view.getUint8(reader.offset) !== 0;
1183
+ reader.offset += 1;` : `result.${name} = view.get${primitiveJSName[tag]}(reader.offset, ${primitiveSizes[tag] > 1 ? "true" : ""});
1180
1184
  reader.offset += ${primitiveSizes[tag]};` : `result.${name} = reader.read${tag}();`
1181
1185
  ).join("\n")}
1182
1186
  return result;`;
@@ -1577,7 +1581,7 @@ var FromBuilder = class _FromBuilder {
1577
1581
  }
1578
1582
  [QueryBrand] = true;
1579
1583
  where(predicate) {
1580
- const newCondition = predicate(this.table.cols);
1584
+ const newCondition = normalizePredicateExpr(predicate(this.table.cols));
1581
1585
  const nextWhere = this.whereClause ? this.whereClause.and(newCondition) : newCondition;
1582
1586
  return new _FromBuilder(this.table, nextWhere);
1583
1587
  }
@@ -1672,7 +1676,8 @@ function createRowExpr(tableDef) {
1672
1676
  const column = new ColumnExpression(
1673
1677
  tableDef.sourceName,
1674
1678
  columnName,
1675
- columnBuilder.typeBuilder.algebraicType
1679
+ columnBuilder.typeBuilder.algebraicType,
1680
+ columnBuilder.columnMetadata.name
1676
1681
  );
1677
1682
  row[columnName] = Object.freeze(column);
1678
1683
  }
@@ -1690,14 +1695,18 @@ function renderSelectSqlWithJoins(table2, where, extraClauses = []) {
1690
1695
  }
1691
1696
  var ColumnExpression = class {
1692
1697
  type = "column";
1698
+ // This is the column accessor
1693
1699
  column;
1700
+ // The name of the column in the database.
1701
+ columnName;
1694
1702
  table;
1695
1703
  // phantom: actual runtime value is undefined
1696
1704
  tsValueType;
1697
1705
  spacetimeType;
1698
- constructor(table2, column, spacetimeType) {
1706
+ constructor(table2, column, spacetimeType, columnName) {
1699
1707
  this.table = table2;
1700
1708
  this.column = column;
1709
+ this.columnName = columnName || column;
1701
1710
  this.spacetimeType = spacetimeType;
1702
1711
  }
1703
1712
  eq(x) {
@@ -1754,15 +1763,36 @@ function normalizeValue(val) {
1754
1763
  }
1755
1764
  return literal(val);
1756
1765
  }
1766
+ function normalizePredicateExpr(value) {
1767
+ if (value instanceof BooleanExpr) return value;
1768
+ if (typeof value === "boolean") {
1769
+ return new BooleanExpr({
1770
+ type: "eq",
1771
+ left: literal(value),
1772
+ right: literal(true)
1773
+ });
1774
+ }
1775
+ return new BooleanExpr({
1776
+ type: "eq",
1777
+ left: value,
1778
+ right: literal(true)
1779
+ });
1780
+ }
1757
1781
  var BooleanExpr = class _BooleanExpr {
1758
1782
  constructor(data) {
1759
1783
  this.data = data;
1760
1784
  }
1761
1785
  and(other) {
1762
- return new _BooleanExpr({ type: "and", clauses: [this.data, other.data] });
1786
+ return new _BooleanExpr({
1787
+ type: "and",
1788
+ clauses: [this.data, other.data]
1789
+ });
1763
1790
  }
1764
1791
  or(other) {
1765
- return new _BooleanExpr({ type: "or", clauses: [this.data, other.data] });
1792
+ return new _BooleanExpr({
1793
+ type: "or",
1794
+ clauses: [this.data, other.data]
1795
+ });
1766
1796
  }
1767
1797
  not() {
1768
1798
  return new _BooleanExpr({ type: "not", clause: this.data });
@@ -1799,7 +1829,7 @@ function valueExprToSql(expr, tableAlias) {
1799
1829
  return literalValueToSql(expr.value);
1800
1830
  }
1801
1831
  const table2 = expr.table;
1802
- return `${quoteIdentifier(table2)}.${quoteIdentifier(expr.column)}`;
1832
+ return `${quoteIdentifier(table2)}.${quoteIdentifier(expr.columnName)}`;
1803
1833
  }
1804
1834
  function literalValueToSql(value) {
1805
1835
  if (value === null || value === void 0) {
@@ -4315,9 +4345,7 @@ var TableCacheImpl = class {
4315
4345
  this.tableDef = tableDef;
4316
4346
  this.rows = /* @__PURE__ */ new Map();
4317
4347
  this.emitter = new EventEmitter();
4318
- const indexesDef = this.tableDef.indexes || [];
4319
- for (const idx of indexesDef) {
4320
- const idxDef = idx;
4348
+ for (const idxDef of this.tableDef.resolvedIndexes) {
4321
4349
  const index = this.#makeReadonlyIndex(this.tableDef, idxDef);
4322
4350
  this[idxDef.name] = index;
4323
4351
  }
@@ -4874,37 +4902,39 @@ async function resolveWS() {
4874
4902
 
4875
4903
  // src/sdk/websocket_decompress_adapter.ts
4876
4904
  var WebsocketDecompressAdapter = class _WebsocketDecompressAdapter {
4877
- onclose;
4878
- onopen;
4879
- onmessage;
4880
- onerror;
4881
- #ws;
4882
- async #handleOnMessage(msg) {
4883
- const buffer = new Uint8Array(msg.data);
4884
- let decompressed;
4885
- if (buffer[0] === 0) {
4886
- decompressed = buffer.slice(1);
4887
- } else if (buffer[0] === 1) {
4888
- throw new Error(
4889
- "Brotli Compression not supported. Please use gzip or none compression in withCompression method on DbConnection."
4890
- );
4891
- } else if (buffer[0] === 2) {
4892
- decompressed = await decompress(buffer.slice(1), "gzip");
4893
- } else {
4894
- throw new Error(
4895
- "Unexpected Compression Algorithm. Please use `gzip` or `none`"
4896
- );
4897
- }
4898
- this.onmessage?.({ data: decompressed });
4905
+ set onclose(handler) {
4906
+ this.#ws.onclose = handler;
4899
4907
  }
4900
- #handleOnOpen(msg) {
4901
- this.onopen?.(msg);
4908
+ set onopen(handler) {
4909
+ this.#ws.onopen = handler;
4902
4910
  }
4903
- #handleOnError(msg) {
4904
- this.onerror?.(msg);
4911
+ set onmessage(handler) {
4912
+ this.#ws.onmessage = async (msg) => {
4913
+ const data = await this.#decompress(new Uint8Array(msg.data));
4914
+ handler({ data });
4915
+ };
4905
4916
  }
4906
- #handleOnClose(msg) {
4907
- this.onclose?.(msg);
4917
+ set onerror(handler) {
4918
+ this.#ws.onerror = handler;
4919
+ }
4920
+ #ws;
4921
+ async #decompress(buffer) {
4922
+ const tag = buffer[0];
4923
+ const data = buffer.subarray(1);
4924
+ switch (tag) {
4925
+ case 0:
4926
+ return data;
4927
+ case 1:
4928
+ throw new Error(
4929
+ "Brotli Compression not supported. Please use gzip or none compression in withCompression method on DbConnection."
4930
+ );
4931
+ case 2:
4932
+ return await decompress(data, "gzip");
4933
+ default:
4934
+ throw new Error(
4935
+ "Unexpected Compression Algorithm. Please use `gzip` or `none`"
4936
+ );
4937
+ }
4908
4938
  }
4909
4939
  send(msg) {
4910
4940
  this.#ws.send(msg);
@@ -4913,14 +4943,6 @@ var WebsocketDecompressAdapter = class _WebsocketDecompressAdapter {
4913
4943
  this.#ws.close();
4914
4944
  }
4915
4945
  constructor(ws) {
4916
- this.onmessage = void 0;
4917
- this.onopen = void 0;
4918
- this.onmessage = void 0;
4919
- this.onerror = void 0;
4920
- ws.onmessage = this.#handleOnMessage.bind(this);
4921
- ws.onerror = this.#handleOnError.bind(this);
4922
- ws.onclose = this.#handleOnClose.bind(this);
4923
- ws.onopen = this.#handleOnOpen.bind(this);
4924
4946
  ws.binaryType = "arraybuffer";
4925
4947
  this.#ws = ws;
4926
4948
  }
@@ -5557,12 +5579,13 @@ var DbConnectionImpl = class {
5557
5579
  }
5558
5580
  #makeReducers(def) {
5559
5581
  const out = {};
5582
+ const writer = new BinaryWriter(1024);
5560
5583
  for (const reducer of def.reducers) {
5561
5584
  const reducerName = reducer.name;
5562
5585
  const key = reducer.accessorName;
5563
5586
  const { serialize: serializeArgs } = this.#reducerArgsSerializers[reducerName];
5564
5587
  out[key] = (params) => {
5565
- const writer = new BinaryWriter(1024);
5588
+ writer.clear();
5566
5589
  serializeArgs(writer, params);
5567
5590
  const argsBuffer = writer.getBuffer();
5568
5591
  return this.callReducer(reducerName, argsBuffer, params);
@@ -5572,12 +5595,13 @@ var DbConnectionImpl = class {
5572
5595
  }
5573
5596
  #makeProcedures(def) {
5574
5597
  const out = {};
5598
+ const writer = new BinaryWriter(1024);
5575
5599
  for (const procedure of def.procedures) {
5576
5600
  const procedureName = procedure.name;
5577
5601
  const key = procedure.accessorName;
5578
5602
  const { serializeArgs, deserializeReturn } = this.#procedureSerializers[procedureName];
5579
5603
  out[key] = (params) => {
5580
- const writer = new BinaryWriter(1024);
5604
+ writer.clear();
5581
5605
  serializeArgs(writer, params);
5582
5606
  const argsBuffer = writer.getBuffer();
5583
5607
  return this.callProcedure(procedureName, argsBuffer).then((returnBuf) => {
@@ -5732,34 +5756,32 @@ var DbConnectionImpl = class {
5732
5756
  }
5733
5757
  return this.#mergeTableUpdates(updates);
5734
5758
  }
5735
- #sendEncoded(wsResolved, message) {
5736
- stdbLogger(
5737
- "trace",
5738
- () => `Sending message to server: ${stringify(message)}`
5739
- );
5740
- const writer = new BinaryWriter(1024);
5741
- ClientMessage.serialize(writer, message);
5742
- const encoded = writer.getBuffer();
5743
- wsResolved.send(encoded);
5744
- }
5745
5759
  #flushOutboundQueue(wsResolved) {
5746
- if (!this.isActive || this.#outboundQueue.length === 0) {
5747
- return;
5748
- }
5749
5760
  const pending = this.#outboundQueue.splice(0);
5750
5761
  for (const message of pending) {
5751
- this.#sendEncoded(wsResolved, message);
5762
+ wsResolved.send(message);
5752
5763
  }
5753
5764
  }
5765
+ #clientMessageEncoder = new BinaryWriter(1024);
5754
5766
  #sendMessage(message) {
5755
- this.wsPromise.then((wsResolved) => {
5756
- if (!wsResolved || !this.isActive) {
5757
- this.#outboundQueue.push(message);
5758
- return;
5759
- }
5760
- this.#flushOutboundQueue(wsResolved);
5761
- this.#sendEncoded(wsResolved, message);
5762
- });
5767
+ const writer = this.#clientMessageEncoder;
5768
+ writer.clear();
5769
+ ClientMessage.serialize(writer, message);
5770
+ const encoded = writer.getBuffer();
5771
+ if (this.ws && this.isActive) {
5772
+ if (this.#outboundQueue.length) this.#flushOutboundQueue(this.ws);
5773
+ stdbLogger(
5774
+ "trace",
5775
+ () => `Sending message to server: ${stringify(message)}`
5776
+ );
5777
+ this.ws.send(encoded);
5778
+ } else {
5779
+ stdbLogger(
5780
+ "trace",
5781
+ () => `Queuing message to server: ${stringify(message)}`
5782
+ );
5783
+ this.#outboundQueue.push(encoded.slice());
5784
+ }
5763
5785
  }
5764
5786
  #nextEventId() {
5765
5787
  this.#eventId += 1;
@@ -6101,11 +6123,7 @@ var DbConnectionImpl = class {
6101
6123
  * ```
6102
6124
  */
6103
6125
  disconnect() {
6104
- this.wsPromise.then((wsResolved) => {
6105
- if (wsResolved) {
6106
- wsResolved.close();
6107
- }
6108
- });
6126
+ this.wsPromise.then((ws) => ws?.close());
6109
6127
  }
6110
6128
  on(eventName, callback) {
6111
6129
  this.#emitter.on(eventName, callback);
@@ -6135,17 +6153,45 @@ var DbConnectionImpl = class {
6135
6153
 
6136
6154
  // src/lib/schema.ts
6137
6155
  function tablesToSchema(ctx, tables) {
6156
+ const tableDefs = /* @__PURE__ */ Object.create(null);
6157
+ for (const [accName, schema2] of Object.entries(tables)) {
6158
+ tableDefs[accName] = tableToSchema(
6159
+ accName,
6160
+ schema2,
6161
+ schema2.tableDef(ctx, accName)
6162
+ );
6163
+ }
6138
6164
  return {
6139
- tables: Object.fromEntries(
6140
- Object.entries(tables).map(([accName, schema2]) => [
6141
- accName,
6142
- tableToSchema(accName, schema2, schema2.tableDef(ctx, accName))
6143
- ])
6144
- )
6165
+ tables: tableDefs
6145
6166
  };
6146
6167
  }
6147
6168
  function tableToSchema(accName, schema2, tableDef) {
6148
6169
  const getColName = (i) => schema2.rowType.algebraicType.value.elements[i].name;
6170
+ const resolvedIndexes = tableDef.indexes.map(
6171
+ (idx) => {
6172
+ const accessorName = idx.accessorName;
6173
+ if (typeof accessorName !== "string" || accessorName.length === 0) {
6174
+ throw new TypeError(
6175
+ `Index '${idx.sourceName ?? "<unknown>"}' on table '${tableDef.sourceName}' is missing accessor name`
6176
+ );
6177
+ }
6178
+ const columnIds = idx.algorithm.tag === "Direct" ? [idx.algorithm.value] : idx.algorithm.value;
6179
+ const unique = tableDef.constraints.some(
6180
+ (c) => c.data.tag === "Unique" && c.data.value.columns.every((col) => columnIds.includes(col))
6181
+ );
6182
+ const algorithm = {
6183
+ BTree: "btree",
6184
+ Hash: "hash",
6185
+ Direct: "direct"
6186
+ }[idx.algorithm.tag];
6187
+ return {
6188
+ name: accessorName,
6189
+ unique,
6190
+ algorithm,
6191
+ columns: columnIds.map(getColName)
6192
+ };
6193
+ }
6194
+ );
6149
6195
  return {
6150
6196
  // For client,`schama.tableName` will always be there as canonical name.
6151
6197
  // For module, if explicit name is not provided via `name`, accessor name will
@@ -6155,26 +6201,16 @@ function tableToSchema(accName, schema2, tableDef) {
6155
6201
  columns: schema2.rowType.row,
6156
6202
  // typed as T[i]['rowType']['row'] under TablesToSchema<T>
6157
6203
  rowType: schema2.rowSpacetimeType,
6204
+ // Keep declarative indexes in their original shape for type-level consumers.
6205
+ indexes: schema2.idxs,
6158
6206
  constraints: tableDef.constraints.map((c) => ({
6159
6207
  name: c.sourceName,
6160
6208
  constraint: "unique",
6161
6209
  columns: c.data.value.columns.map(getColName)
6162
6210
  })),
6163
- // TODO: horrible horrible horrible. we smuggle this `Array<UntypedIndex>`
6164
- // by casting it to an `Array<IndexOpts>` as `TableToSchema` expects.
6165
- // This is then used in `TableCacheImpl.constructor` and who knows where else.
6166
- // We should stop lying about our types.
6167
- indexes: tableDef.indexes.map((idx) => {
6168
- const columnIds = idx.algorithm.tag === "Direct" ? [idx.algorithm.value] : idx.algorithm.value;
6169
- return {
6170
- name: idx.accessorName,
6171
- unique: tableDef.constraints.some(
6172
- (c) => c.data.value.columns.every((col) => columnIds.includes(col))
6173
- ),
6174
- algorithm: idx.algorithm.tag.toLowerCase(),
6175
- columns: columnIds.map(getColName)
6176
- };
6177
- }),
6211
+ // Expose resolved runtime indexes separately so runtime users don't have to
6212
+ // reinterpret `indexes` with unsafe casts.
6213
+ resolvedIndexes,
6178
6214
  tableDef,
6179
6215
  ...tableDef.isEvent ? { isEvent: true } : {}
6180
6216
  };
@@ -6998,6 +7034,14 @@ function table(opts, row, ..._) {
6998
7034
  }
6999
7035
  }
7000
7036
  for (const indexOpts of userIndexes ?? []) {
7037
+ const accessor = indexOpts.accessor;
7038
+ if (typeof accessor !== "string" || accessor.length === 0) {
7039
+ const tableLabel = name ?? "<unnamed>";
7040
+ const indexLabel = indexOpts.name ?? "<unnamed>";
7041
+ throw new TypeError(
7042
+ `Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'`
7043
+ );
7044
+ }
7001
7045
  let algorithm;
7002
7046
  switch (indexOpts.algorithm) {
7003
7047
  case "btree":
@@ -7018,7 +7062,7 @@ function table(opts, row, ..._) {
7018
7062
  }
7019
7063
  indexes.push({
7020
7064
  sourceName: void 0,
7021
- accessorName: indexOpts.accessor,
7065
+ accessorName: accessor,
7022
7066
  algorithm,
7023
7067
  canonicalName: indexOpts.name
7024
7068
  });
@@ -7068,7 +7112,9 @@ function table(opts, row, ..._) {
7068
7112
  isEvent
7069
7113
  };
7070
7114
  },
7071
- idxs: {},
7115
+ // Preserve the declared index options as runtime data so `tableToSchema`
7116
+ // can expose them without type-smuggling.
7117
+ idxs: userIndexes,
7072
7118
  constraints,
7073
7119
  schedule
7074
7120
  };