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