spacetimedb 2.3.0 → 2.4.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 (59) hide show
  1. package/LICENSE.txt +2 -2
  2. package/dist/index.browser.mjs +50 -4
  3. package/dist/index.browser.mjs.map +1 -1
  4. package/dist/index.cjs +50 -4
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.mjs +50 -4
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/lib/autogen/types.d.ts +674 -18
  9. package/dist/lib/autogen/types.d.ts.map +1 -1
  10. package/dist/lib/schema.d.ts.map +1 -1
  11. package/dist/min/index.browser.mjs +1 -1
  12. package/dist/min/index.browser.mjs.map +1 -1
  13. package/dist/min/sdk/index.browser.mjs +1 -1
  14. package/dist/min/sdk/index.browser.mjs.map +1 -1
  15. package/dist/sdk/decompress.d.ts.map +1 -1
  16. package/dist/sdk/index.browser.mjs +50 -4
  17. package/dist/sdk/index.browser.mjs.map +1 -1
  18. package/dist/sdk/index.cjs +50 -4
  19. package/dist/sdk/index.cjs.map +1 -1
  20. package/dist/sdk/index.mjs +50 -4
  21. package/dist/sdk/index.mjs.map +1 -1
  22. package/dist/server/http.d.ts +1 -2
  23. package/dist/server/http.d.ts.map +1 -1
  24. package/dist/server/http.test-d.d.ts +2 -0
  25. package/dist/server/http.test-d.d.ts.map +1 -0
  26. package/dist/server/http_handlers.d.ts +82 -0
  27. package/dist/server/http_handlers.d.ts.map +1 -0
  28. package/dist/server/http_internal.d.ts +1 -32
  29. package/dist/server/http_internal.d.ts.map +1 -1
  30. package/dist/server/http_shared.d.ts +44 -0
  31. package/dist/server/http_shared.d.ts.map +1 -0
  32. package/dist/server/index.d.ts +2 -0
  33. package/dist/server/index.d.ts.map +1 -1
  34. package/dist/server/index.mjs +582 -134
  35. package/dist/server/index.mjs.map +1 -1
  36. package/dist/server/runtime.d.ts +1 -0
  37. package/dist/server/runtime.d.ts.map +1 -1
  38. package/dist/server/schema.d.ts +16 -1
  39. package/dist/server/schema.d.ts.map +1 -1
  40. package/dist/server/views.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/lib/autogen/types.ts +29 -0
  43. package/src/lib/schema.ts +14 -0
  44. package/src/sdk/decompress.ts +19 -4
  45. package/src/sdk/logger.ts +1 -1
  46. package/src/server/http.test-d.ts +80 -0
  47. package/src/server/http.ts +14 -2
  48. package/src/server/http_handlers.ts +413 -0
  49. package/src/server/http_internal.ts +15 -142
  50. package/src/server/http_shared.ts +186 -0
  51. package/src/server/index.ts +11 -0
  52. package/src/server/procedures.ts +8 -30
  53. package/src/server/runtime.ts +137 -1
  54. package/src/server/schema.ts +71 -2
  55. package/src/server/sys.d.ts +7 -0
  56. package/src/server/views.ts +1 -0
  57. package/dist/lib/http_types.d.ts +0 -2
  58. package/dist/lib/http_types.d.ts.map +0 -1
  59. package/src/lib/http_types.ts +0 -8
@@ -1,6 +1,7 @@
1
1
  import * as _syscalls2_0 from 'spacetime:sys@2.0';
2
2
  import { moduleHooks } from 'spacetime:sys@2.0';
3
- import { headersToList, Headers } from 'headers-polyfill';
3
+ import { Headers, headersToList } from 'headers-polyfill';
4
+ export { Headers } from 'headers-polyfill';
4
5
  import * as _syscalls2_1 from 'spacetime:sys@2.1';
5
6
 
6
7
  typeof globalThis!=="undefined"&&((globalThis.global=globalThis.global||globalThis),(globalThis.window=globalThis.window||globalThis));
@@ -4606,6 +4607,12 @@ var Lifecycle = t.enum("Lifecycle", {
4606
4607
  OnConnect: t.unit(),
4607
4608
  OnDisconnect: t.unit()
4608
4609
  });
4610
+ var MethodOrAny = t.enum("MethodOrAny", {
4611
+ Any: t.unit(),
4612
+ get Method() {
4613
+ return HttpMethod;
4614
+ }
4615
+ });
4609
4616
  var MiscModuleExport = t.enum("MiscModuleExport", {
4610
4617
  get TypeAlias() {
4611
4618
  return TypeAlias;
@@ -4663,6 +4670,16 @@ var RawConstraintDefV9 = t.object("RawConstraintDefV9", {
4663
4670
  return RawConstraintDataV9;
4664
4671
  }
4665
4672
  });
4673
+ var RawHttpHandlerDefV10 = t.object("RawHttpHandlerDefV10", {
4674
+ sourceName: t.string()
4675
+ });
4676
+ var RawHttpRouteDefV10 = t.object("RawHttpRouteDefV10", {
4677
+ handlerFunction: t.string(),
4678
+ get method() {
4679
+ return MethodOrAny;
4680
+ },
4681
+ path: t.string()
4682
+ });
4666
4683
  var RawIndexAlgorithm = t.enum("RawIndexAlgorithm", {
4667
4684
  BTree: t.array(t.u16()),
4668
4685
  Hash: t.array(t.u16()),
@@ -4759,6 +4776,12 @@ var RawModuleDefV10Section = t.enum("RawModuleDefV10Section", {
4759
4776
  },
4760
4777
  get ExplicitNames() {
4761
4778
  return ExplicitNames;
4779
+ },
4780
+ get HttpHandlers() {
4781
+ return t.array(RawHttpHandlerDefV10);
4782
+ },
4783
+ get HttpRoutes() {
4784
+ return t.array(RawHttpRouteDefV10);
4762
4785
  }
4763
4786
  });
4764
4787
  var RawModuleDefV8 = t.object("RawModuleDefV8", {
@@ -5107,6 +5130,8 @@ var ModuleContext = class {
5107
5130
  procedures: [],
5108
5131
  views: [],
5109
5132
  lifeCycleReducers: [],
5133
+ httpHandlers: [],
5134
+ httpRoutes: [],
5110
5135
  caseConversionPolicy: { tag: "SnakeCase" },
5111
5136
  explicitNames: {
5112
5137
  entries: []
@@ -5134,6 +5159,18 @@ var ModuleContext = class {
5134
5159
  value: module.lifeCycleReducers
5135
5160
  }
5136
5161
  );
5162
+ push(
5163
+ module.httpHandlers && {
5164
+ tag: "HttpHandlers",
5165
+ value: module.httpHandlers
5166
+ }
5167
+ );
5168
+ push(
5169
+ module.httpRoutes && {
5170
+ tag: "HttpRoutes",
5171
+ value: module.httpRoutes
5172
+ }
5173
+ );
5137
5174
  push(
5138
5175
  module.rowLevelSecurity && {
5139
5176
  tag: "RowLevelSecurity",
@@ -5263,6 +5300,396 @@ function splitName(name) {
5263
5300
  const scope = name.split(".");
5264
5301
  return { sourceName: scope.pop(), scope };
5265
5302
  }
5303
+ var textEncoder = new TextEncoder();
5304
+ var textDecoder = new TextDecoder("utf-8");
5305
+ function deserializeMethod(method) {
5306
+ switch (method.tag) {
5307
+ case "Get":
5308
+ return "GET";
5309
+ case "Head":
5310
+ return "HEAD";
5311
+ case "Post":
5312
+ return "POST";
5313
+ case "Put":
5314
+ return "PUT";
5315
+ case "Delete":
5316
+ return "DELETE";
5317
+ case "Connect":
5318
+ return "CONNECT";
5319
+ case "Options":
5320
+ return "OPTIONS";
5321
+ case "Trace":
5322
+ return "TRACE";
5323
+ case "Patch":
5324
+ return "PATCH";
5325
+ case "Extension":
5326
+ return method.value;
5327
+ }
5328
+ }
5329
+ var methods = /* @__PURE__ */ new Map([
5330
+ ["GET", { tag: "Get" }],
5331
+ ["HEAD", { tag: "Head" }],
5332
+ ["POST", { tag: "Post" }],
5333
+ ["PUT", { tag: "Put" }],
5334
+ ["DELETE", { tag: "Delete" }],
5335
+ ["CONNECT", { tag: "Connect" }],
5336
+ ["OPTIONS", { tag: "Options" }],
5337
+ ["TRACE", { tag: "Trace" }],
5338
+ ["PATCH", { tag: "Patch" }]
5339
+ ]);
5340
+ function serializeMethod(method) {
5341
+ return methods.get(method?.toUpperCase() ?? "GET") ?? {
5342
+ tag: "Extension",
5343
+ value: method
5344
+ };
5345
+ }
5346
+ function serializeHeaders(headers) {
5347
+ return {
5348
+ entries: headersToList(headers).flatMap(([k, v]) => Array.isArray(v) ? v.map((v2) => [k, v2]) : [[k, v]]).map(([name, value]) => ({ name, value: textEncoder.encode(value) }))
5349
+ };
5350
+ }
5351
+ function deserializeHeaders(headers) {
5352
+ return new Headers(
5353
+ headers.entries.map(({ name, value }) => [
5354
+ name,
5355
+ textDecoder.decode(value)
5356
+ ])
5357
+ );
5358
+ }
5359
+ var makeResponse = Symbol("makeResponse");
5360
+ var SyncResponse = class _SyncResponse {
5361
+ #body;
5362
+ #inner;
5363
+ constructor(body, init) {
5364
+ if (body == null) {
5365
+ this.#body = null;
5366
+ } else if (typeof body === "string") {
5367
+ this.#body = body;
5368
+ } else {
5369
+ this.#body = new Uint8Array(body).buffer;
5370
+ }
5371
+ this.#inner = {
5372
+ headers: new Headers(init?.headers),
5373
+ status: init?.status ?? 200,
5374
+ statusText: init?.statusText ?? "",
5375
+ type: "default",
5376
+ url: null,
5377
+ aborted: false,
5378
+ version: init?.version ?? { tag: "Http11" }
5379
+ };
5380
+ }
5381
+ static [makeResponse](body, inner) {
5382
+ const me = new _SyncResponse(body);
5383
+ me.#inner = inner;
5384
+ return me;
5385
+ }
5386
+ get headers() {
5387
+ return this.#inner.headers;
5388
+ }
5389
+ get status() {
5390
+ return this.#inner.status;
5391
+ }
5392
+ get statusText() {
5393
+ return this.#inner.statusText;
5394
+ }
5395
+ get ok() {
5396
+ return 200 <= this.#inner.status && this.#inner.status <= 299;
5397
+ }
5398
+ get url() {
5399
+ return this.#inner.url ?? "";
5400
+ }
5401
+ get type() {
5402
+ return this.#inner.type;
5403
+ }
5404
+ get version() {
5405
+ return this.#inner.version;
5406
+ }
5407
+ arrayBuffer() {
5408
+ return this.bytes().buffer;
5409
+ }
5410
+ bytes() {
5411
+ if (this.#body == null) {
5412
+ return new Uint8Array();
5413
+ } else if (typeof this.#body === "string") {
5414
+ return textEncoder.encode(this.#body);
5415
+ } else {
5416
+ return new Uint8Array(this.#body);
5417
+ }
5418
+ }
5419
+ json() {
5420
+ return JSON.parse(this.text());
5421
+ }
5422
+ text() {
5423
+ if (this.#body == null) {
5424
+ return "";
5425
+ } else if (typeof this.#body === "string") {
5426
+ return this.#body;
5427
+ } else {
5428
+ return textDecoder.decode(this.#body);
5429
+ }
5430
+ }
5431
+ };
5432
+
5433
+ // src/server/http_handlers.ts
5434
+ var httpHandlerFn = Symbol("SpacetimeDB.httpHandlerFn");
5435
+ var ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION = "ASCII lowercase letters, digits and `-_~/`";
5436
+ var makeRequest = Symbol("makeRequest");
5437
+ function coerceRequestBody(body) {
5438
+ if (body == null) {
5439
+ return null;
5440
+ }
5441
+ if (typeof body === "string") {
5442
+ return body;
5443
+ }
5444
+ return new Uint8Array(body);
5445
+ }
5446
+ function requestBodyToBytes(body) {
5447
+ if (body == null) {
5448
+ return new Uint8Array();
5449
+ }
5450
+ if (typeof body === "string") {
5451
+ return textEncoder.encode(body);
5452
+ }
5453
+ return body;
5454
+ }
5455
+ function requestBodyToText(body) {
5456
+ if (body == null) {
5457
+ return "";
5458
+ }
5459
+ if (typeof body === "string") {
5460
+ return body;
5461
+ }
5462
+ return textDecoder.decode(body);
5463
+ }
5464
+ function characterIsAcceptableForRoutePath(c) {
5465
+ return c >= "a" && c <= "z" || c >= "0" && c <= "9" || c === "-" || c === "_" || c === "~" || c === "/";
5466
+ }
5467
+ function assertValidPath(path) {
5468
+ if (path !== "" && !path.startsWith("/")) {
5469
+ throw new TypeError(`Route paths must start with \`/\`: ${path}`);
5470
+ }
5471
+ if (![...path].every(characterIsAcceptableForRoutePath)) {
5472
+ throw new TypeError(
5473
+ `Route paths may contain only ${ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION}: ${path}`
5474
+ );
5475
+ }
5476
+ }
5477
+ function routesOverlap(a, b) {
5478
+ const methodsMatch = (left, right) => {
5479
+ if (left.tag !== right.tag) {
5480
+ return false;
5481
+ }
5482
+ if (left.tag === "Extension" && right.tag === "Extension") {
5483
+ return left.value === right.value;
5484
+ }
5485
+ return true;
5486
+ };
5487
+ return a.path === b.path && (a.method.tag === "Any" || b.method.tag === "Any" || a.method.tag === "Method" && b.method.tag === "Method" && methodsMatch(a.method.value, b.method.value));
5488
+ }
5489
+ function joinPaths(prefix, suffix) {
5490
+ if (prefix === "/") {
5491
+ return suffix;
5492
+ }
5493
+ if (suffix === "/") {
5494
+ return prefix;
5495
+ }
5496
+ let prefixEnd = prefix.length;
5497
+ while (prefixEnd > 0 && prefix[prefixEnd - 1] === "/") {
5498
+ prefixEnd--;
5499
+ }
5500
+ let suffixStart = 0;
5501
+ while (suffixStart < suffix.length && suffix[suffixStart] === "/") {
5502
+ suffixStart++;
5503
+ }
5504
+ const joinedPrefix = prefix.slice(0, prefixEnd);
5505
+ const joinedSuffix = suffix.slice(suffixStart);
5506
+ return `${joinedPrefix}/${joinedSuffix}`;
5507
+ }
5508
+ var Request = class _Request {
5509
+ #body;
5510
+ #inner;
5511
+ constructor(url, init = {}) {
5512
+ this.#body = coerceRequestBody(init.body);
5513
+ this.#inner = {
5514
+ headers: new Headers(init.headers),
5515
+ method: init.method ?? "GET",
5516
+ uri: "" + url,
5517
+ version: init.version ?? { tag: "Http11" }
5518
+ };
5519
+ }
5520
+ static [makeRequest](body, inner) {
5521
+ const me = new _Request(inner.uri);
5522
+ me.#body = coerceRequestBody(body);
5523
+ me.#inner = inner;
5524
+ return me;
5525
+ }
5526
+ get headers() {
5527
+ return this.#inner.headers;
5528
+ }
5529
+ get method() {
5530
+ return this.#inner.method;
5531
+ }
5532
+ get uri() {
5533
+ return this.#inner.uri;
5534
+ }
5535
+ get url() {
5536
+ return this.#inner.uri;
5537
+ }
5538
+ get version() {
5539
+ return this.#inner.version;
5540
+ }
5541
+ arrayBuffer() {
5542
+ return this.bytes().buffer;
5543
+ }
5544
+ bytes() {
5545
+ return requestBodyToBytes(this.#body);
5546
+ }
5547
+ json() {
5548
+ return JSON.parse(this.text());
5549
+ }
5550
+ text() {
5551
+ return requestBodyToText(this.#body);
5552
+ }
5553
+ };
5554
+ var exportedHttpHandlerObjects = /* @__PURE__ */ new WeakSet();
5555
+ var Router = class _Router {
5556
+ #routes;
5557
+ constructor(routes = []) {
5558
+ this.#routes = routes;
5559
+ }
5560
+ get(path, handler) {
5561
+ return this.addRoute(
5562
+ { tag: "Method", value: { tag: "Get" } },
5563
+ path,
5564
+ handler
5565
+ );
5566
+ }
5567
+ head(path, handler) {
5568
+ return this.addRoute(
5569
+ { tag: "Method", value: { tag: "Head" } },
5570
+ path,
5571
+ handler
5572
+ );
5573
+ }
5574
+ options(path, handler) {
5575
+ return this.addRoute(
5576
+ { tag: "Method", value: { tag: "Options" } },
5577
+ path,
5578
+ handler
5579
+ );
5580
+ }
5581
+ put(path, handler) {
5582
+ return this.addRoute(
5583
+ { tag: "Method", value: { tag: "Put" } },
5584
+ path,
5585
+ handler
5586
+ );
5587
+ }
5588
+ delete(path, handler) {
5589
+ return this.addRoute(
5590
+ { tag: "Method", value: { tag: "Delete" } },
5591
+ path,
5592
+ handler
5593
+ );
5594
+ }
5595
+ post(path, handler) {
5596
+ return this.addRoute(
5597
+ { tag: "Method", value: { tag: "Post" } },
5598
+ path,
5599
+ handler
5600
+ );
5601
+ }
5602
+ patch(path, handler) {
5603
+ return this.addRoute(
5604
+ { tag: "Method", value: { tag: "Patch" } },
5605
+ path,
5606
+ handler
5607
+ );
5608
+ }
5609
+ any(path, handler) {
5610
+ return this.addRoute({ tag: "Any" }, path, handler);
5611
+ }
5612
+ nest(path, subRouter) {
5613
+ assertValidPath(path);
5614
+ if (this.#routes.some((route) => route.path.startsWith(path))) {
5615
+ throw new TypeError(
5616
+ `Cannot nest router at \`${path}\`; existing routes overlap with nested path`
5617
+ );
5618
+ }
5619
+ let merged = new _Router(this.#routes);
5620
+ for (const route of subRouter.#routes) {
5621
+ merged = merged.addRoute(
5622
+ route.method,
5623
+ joinPaths(path, route.path),
5624
+ route.handler
5625
+ );
5626
+ }
5627
+ return merged;
5628
+ }
5629
+ merge(otherRouter) {
5630
+ let merged = new _Router(this.#routes);
5631
+ for (const route of otherRouter.#routes) {
5632
+ merged = merged.addRoute(route.method, route.path, route.handler);
5633
+ }
5634
+ return merged;
5635
+ }
5636
+ intoRoutes() {
5637
+ return this.#routes.slice();
5638
+ }
5639
+ addRoute(method, path, handler) {
5640
+ assertValidPath(path);
5641
+ const candidate = { method, path, handler };
5642
+ if (this.#routes.some((route) => routesOverlap(route, candidate))) {
5643
+ throw new TypeError(`Route conflict for \`${path}\``);
5644
+ }
5645
+ return new _Router([...this.#routes, candidate]);
5646
+ }
5647
+ };
5648
+ function makeHttpHandlerExport(ctx, opts, fn) {
5649
+ const handlerExport = {
5650
+ [httpHandlerFn]: fn,
5651
+ [exportContext]: ctx,
5652
+ [registerExport](ctx2, exportName) {
5653
+ if (exportedHttpHandlerObjects.has(handlerExport)) {
5654
+ throw new TypeError(
5655
+ `HTTP handler '${exportName}' was exported more than once`
5656
+ );
5657
+ }
5658
+ exportedHttpHandlerObjects.add(handlerExport);
5659
+ registerHttpHandler(ctx2, exportName, fn, opts);
5660
+ ctx2.httpHandlerExports.set(
5661
+ handlerExport,
5662
+ exportName
5663
+ );
5664
+ }
5665
+ };
5666
+ return handlerExport;
5667
+ }
5668
+ function makeHttpRouterExport(ctx, router) {
5669
+ return {
5670
+ [exportContext]: ctx,
5671
+ [registerExport](ctx2) {
5672
+ ctx2.pendingHttpRoutes.push(...router.intoRoutes());
5673
+ }
5674
+ };
5675
+ }
5676
+ function registerHttpHandler(ctx, exportName, fn, opts) {
5677
+ ctx.defineHttpHandler(exportName);
5678
+ ctx.moduleDef.httpHandlers.push({ sourceName: exportName });
5679
+ if (opts?.name != null) {
5680
+ ctx.moduleDef.explicitNames.entries.push({
5681
+ tag: "Function",
5682
+ value: {
5683
+ sourceName: exportName,
5684
+ canonicalName: opts.name
5685
+ }
5686
+ });
5687
+ }
5688
+ if (!fn.name) {
5689
+ Object.defineProperty(fn, "name", { value: exportName, writable: false });
5690
+ }
5691
+ ctx.httpHandlers.push(fn);
5692
+ }
5266
5693
 
5267
5694
  // src/server/http_internal.ts
5268
5695
  var import_statuses = __toESM(require_statuses());
@@ -5894,6 +6321,7 @@ function makeAnonViewExport(ctx, opts, params, ret, fn) {
5894
6321
  return viewExport;
5895
6322
  }
5896
6323
  function registerView(ctx, opts, exportName, anon, params, ret, fn) {
6324
+ ctx.defineFunction(exportName);
5897
6325
  const paramsBuilder = new RowBuilder(params, toPascalCase(exportName));
5898
6326
  let returnType = ctx.registerTypesRecursively(ret).algebraicType;
5899
6327
  const { typespace } = ctx;
@@ -6303,6 +6731,24 @@ function makeRandom(seed) {
6303
6731
  // src/server/runtime.ts
6304
6732
  var { freeze } = Object;
6305
6733
  var sys = { ..._syscalls2_0, ..._syscalls2_1 };
6734
+ function requestFromWire(request, body) {
6735
+ return Request[makeRequest](body, {
6736
+ headers: deserializeHeaders(request.headers),
6737
+ method: deserializeMethod(request.method),
6738
+ uri: request.uri,
6739
+ version: request.version
6740
+ });
6741
+ }
6742
+ function responseIntoWire(response) {
6743
+ return [
6744
+ {
6745
+ headers: serializeHeaders(response.headers),
6746
+ version: response.version,
6747
+ code: response.status
6748
+ },
6749
+ response.bytes()
6750
+ ];
6751
+ }
6306
6752
  function parseJsonObject(json) {
6307
6753
  let value;
6308
6754
  try {
@@ -6467,6 +6913,31 @@ var ReducerCtxImpl = class ReducerCtx {
6467
6913
  var callUserFunction = function __spacetimedb_end_short_backtrace(fn, ...args) {
6468
6914
  return fn(...args);
6469
6915
  };
6916
+ function runWithTx(makeCtx, body) {
6917
+ const run = () => {
6918
+ const timestamp = sys.procedure_start_mut_tx();
6919
+ try {
6920
+ return body(makeCtx(new Timestamp(timestamp)));
6921
+ } catch (e) {
6922
+ sys.procedure_abort_mut_tx();
6923
+ throw e;
6924
+ }
6925
+ };
6926
+ let res = run();
6927
+ try {
6928
+ sys.procedure_commit_mut_tx();
6929
+ return res;
6930
+ } catch {
6931
+ }
6932
+ console.warn("committing anonymous transaction failed");
6933
+ res = run();
6934
+ try {
6935
+ sys.procedure_commit_mut_tx();
6936
+ return res;
6937
+ } catch (e) {
6938
+ throw new Error("transaction retry failed again", { cause: e });
6939
+ }
6940
+ }
6470
6941
  var makeHooks = (schema2) => new ModuleHooksImpl(schema2);
6471
6942
  var ModuleHooksImpl = class {
6472
6943
  #schema;
@@ -6583,9 +7054,61 @@ var ModuleHooksImpl = class {
6583
7054
  () => this.#dbView
6584
7055
  );
6585
7056
  }
7057
+ __call_http_handler__(id, timestamp, request, body) {
7058
+ const moduleCtx = this.#schema;
7059
+ const handler = moduleCtx.httpHandlers[id];
7060
+ const ctx = new HandlerContextImpl(
7061
+ new Timestamp(timestamp),
7062
+ () => this.#dbView
7063
+ );
7064
+ const requestMetadata = HttpRequest.deserialize(new BinaryReader(request));
7065
+ const response = callUserFunction(
7066
+ handler,
7067
+ ctx,
7068
+ requestFromWire(requestMetadata, body)
7069
+ );
7070
+ const [responseMetadata, responseBody] = responseIntoWire(response);
7071
+ const responseBuf = new BinaryWriter(
7072
+ bsatnBaseSize(moduleCtx.typespace, HttpResponse.algebraicType)
7073
+ );
7074
+ HttpResponse.serialize(responseBuf, responseMetadata);
7075
+ return [responseBuf.getBuffer(), responseBody];
7076
+ }
6586
7077
  };
6587
7078
  var BINARY_WRITER = new BinaryWriter(0);
6588
7079
  var BINARY_READER = new BinaryReader(new Uint8Array());
7080
+ var HandlerContextImpl = class {
7081
+ constructor(timestamp, dbView) {
7082
+ this.timestamp = timestamp;
7083
+ this.#dbView = dbView;
7084
+ }
7085
+ #identity;
7086
+ #uuidCounter;
7087
+ #random;
7088
+ #dbView;
7089
+ http = httpClient;
7090
+ get identity() {
7091
+ return this.#identity ??= new Identity(sys.identity());
7092
+ }
7093
+ get random() {
7094
+ return this.#random ??= makeRandom(this.timestamp);
7095
+ }
7096
+ withTx(body) {
7097
+ return runWithTx(
7098
+ (timestamp) => new ReducerCtxImpl(Identity.zero(), timestamp, null, this.#dbView()),
7099
+ body
7100
+ );
7101
+ }
7102
+ newUuidV4() {
7103
+ const bytes = this.random.fill(new Uint8Array(16));
7104
+ return Uuid.fromRandomBytesV4(bytes);
7105
+ }
7106
+ newUuidV7() {
7107
+ const bytes = this.random.fill(new Uint8Array(4));
7108
+ const counter = this.#uuidCounter ??= { value: 0 };
7109
+ return Uuid.fromCounterV7(counter, this.timestamp, bytes);
7110
+ }
7111
+ };
6589
7112
  function makeTableView(typespace, table2) {
6590
7113
  const table_id = sys.table_id_from_name(table2.sourceName);
6591
7114
  const rowType = typespace.types[table2.productTypeRef];
@@ -7034,109 +7557,10 @@ var IteratorHandle = class _IteratorHandle {
7034
7557
 
7035
7558
  // src/server/http_internal.ts
7036
7559
  var { freeze: freeze2 } = Object;
7037
- var textEncoder = new TextEncoder();
7038
- var textDecoder = new TextDecoder(
7039
- "utf-8"
7040
- /* { fatal: true } */
7041
- );
7042
- function deserializeHeaders(headers) {
7043
- return new Headers(
7044
- headers.entries.map(({ name, value }) => [
7045
- name,
7046
- textDecoder.decode(value)
7047
- ])
7048
- );
7049
- }
7050
- var makeResponse = Symbol("makeResponse");
7051
- var SyncResponse = class _SyncResponse {
7052
- #body;
7053
- #inner;
7054
- constructor(body, init) {
7055
- if (body == null) {
7056
- this.#body = null;
7057
- } else if (typeof body === "string") {
7058
- this.#body = body;
7059
- } else {
7060
- this.#body = new Uint8Array(body).buffer;
7061
- }
7062
- this.#inner = {
7063
- headers: new Headers(init?.headers),
7064
- status: init?.status ?? 200,
7065
- statusText: init?.statusText ?? "",
7066
- type: "default",
7067
- url: null,
7068
- aborted: false
7069
- };
7070
- }
7071
- static [makeResponse](body, inner) {
7072
- const me = new _SyncResponse(body);
7073
- me.#inner = inner;
7074
- return me;
7075
- }
7076
- get headers() {
7077
- return this.#inner.headers;
7078
- }
7079
- get status() {
7080
- return this.#inner.status;
7081
- }
7082
- get statusText() {
7083
- return this.#inner.statusText;
7084
- }
7085
- get ok() {
7086
- return 200 <= this.#inner.status && this.#inner.status <= 299;
7087
- }
7088
- get url() {
7089
- return this.#inner.url ?? "";
7090
- }
7091
- get type() {
7092
- return this.#inner.type;
7093
- }
7094
- arrayBuffer() {
7095
- return this.bytes().buffer;
7096
- }
7097
- bytes() {
7098
- if (this.#body == null) {
7099
- return new Uint8Array();
7100
- } else if (typeof this.#body === "string") {
7101
- return textEncoder.encode(this.#body);
7102
- } else {
7103
- return new Uint8Array(this.#body);
7104
- }
7105
- }
7106
- json() {
7107
- return JSON.parse(this.text());
7108
- }
7109
- text() {
7110
- if (this.#body == null) {
7111
- return "";
7112
- } else if (typeof this.#body === "string") {
7113
- return this.#body;
7114
- } else {
7115
- return textDecoder.decode(this.#body);
7116
- }
7117
- }
7118
- };
7119
7560
  var requestBaseSize = bsatnBaseSize({ types: [] }, HttpRequest.algebraicType);
7120
- var methods = /* @__PURE__ */ new Map([
7121
- ["GET", { tag: "Get" }],
7122
- ["HEAD", { tag: "Head" }],
7123
- ["POST", { tag: "Post" }],
7124
- ["PUT", { tag: "Put" }],
7125
- ["DELETE", { tag: "Delete" }],
7126
- ["CONNECT", { tag: "Connect" }],
7127
- ["OPTIONS", { tag: "Options" }],
7128
- ["TRACE", { tag: "Trace" }],
7129
- ["PATCH", { tag: "Patch" }]
7130
- ]);
7131
7561
  function fetch(url, init = {}) {
7132
- const method = methods.get(init.method?.toUpperCase() ?? "GET") ?? {
7133
- tag: "Extension",
7134
- value: init.method
7135
- };
7136
- const headers = {
7137
- // anys because the typings are wonky - see comment in SyncResponse.constructor
7138
- entries: headersToList(new Headers(init.headers)).flatMap(([k, v]) => Array.isArray(v) ? v.map((v2) => [k, v2]) : [[k, v]]).map(([name, value]) => ({ name, value: textEncoder.encode(value) }))
7139
- };
7562
+ const method = serializeMethod(init.method);
7563
+ const headers = serializeHeaders(new Headers(init.headers));
7140
7564
  const uri = "" + url;
7141
7565
  const request = freeze2({
7142
7566
  method,
@@ -7159,7 +7583,8 @@ function fetch(url, init = {}) {
7159
7583
  status: response.code,
7160
7584
  statusText: (0, import_statuses.default)(response.code),
7161
7585
  headers: deserializeHeaders(response.headers),
7162
- aborted: false
7586
+ aborted: false,
7587
+ version: response.version
7163
7588
  });
7164
7589
  }
7165
7590
  freeze2(fetch);
@@ -7244,35 +7669,15 @@ var ProcedureCtxImpl = class ProcedureCtx {
7244
7669
  return httpClient;
7245
7670
  }
7246
7671
  withTx(body) {
7247
- const run = () => {
7248
- const timestamp = sys.procedure_start_mut_tx();
7249
- try {
7250
- const ctx = new TransactionCtxImpl(
7251
- this.sender,
7252
- new Timestamp(timestamp),
7253
- this.connectionId,
7254
- this.#dbView()
7255
- );
7256
- return body(ctx);
7257
- } catch (e) {
7258
- sys.procedure_abort_mut_tx();
7259
- throw e;
7260
- }
7261
- };
7262
- let res = run();
7263
- try {
7264
- sys.procedure_commit_mut_tx();
7265
- return res;
7266
- } catch {
7267
- }
7268
- console.warn("committing anonymous transaction failed");
7269
- res = run();
7270
- try {
7271
- sys.procedure_commit_mut_tx();
7272
- return res;
7273
- } catch (e) {
7274
- throw new Error("transaction retry failed again", { cause: e });
7275
- }
7672
+ return runWithTx(
7673
+ (timestamp) => new TransactionCtxImpl(
7674
+ this.sender,
7675
+ timestamp,
7676
+ this.connectionId,
7677
+ this.#dbView()
7678
+ ),
7679
+ body
7680
+ );
7276
7681
  }
7277
7682
  newUuidV4() {
7278
7683
  const bytes = this.random.fill(new Uint8Array(16));
@@ -7343,16 +7748,20 @@ function registerReducer(ctx, exportName, params, fn, opts, lifecycle) {
7343
7748
  var SchemaInner = class extends ModuleContext {
7344
7749
  schemaType;
7345
7750
  existingFunctions = /* @__PURE__ */ new Set();
7751
+ existingHttpHandlers = /* @__PURE__ */ new Set();
7346
7752
  reducers = [];
7347
7753
  procedures = [];
7348
7754
  views = [];
7349
7755
  anonViews = [];
7756
+ httpHandlers = [];
7350
7757
  /**
7351
7758
  * Maps ReducerExport objects to the name of the reducer.
7352
7759
  * Used for resolving the reducers of scheduled tables.
7353
7760
  */
7354
7761
  functionExports = /* @__PURE__ */ new Map();
7762
+ httpHandlerExports = /* @__PURE__ */ new Map();
7355
7763
  pendingSchedules = [];
7764
+ pendingHttpRoutes = [];
7356
7765
  constructor(getSchemaType) {
7357
7766
  super();
7358
7767
  this.schemaType = getSchemaType(this);
@@ -7360,11 +7769,19 @@ var SchemaInner = class extends ModuleContext {
7360
7769
  defineFunction(name) {
7361
7770
  if (this.existingFunctions.has(name)) {
7362
7771
  throw new TypeError(
7363
- `There is already a reducer or procedure with the name '${name}'`
7772
+ `There is already a reducer, procedure, or view with the name '${name}'`
7364
7773
  );
7365
7774
  }
7366
7775
  this.existingFunctions.add(name);
7367
7776
  }
7777
+ defineHttpHandler(name) {
7778
+ if (this.existingHttpHandlers.has(name)) {
7779
+ throw new TypeError(
7780
+ `There is already an HTTP handler with the name '${name}'`
7781
+ );
7782
+ }
7783
+ this.existingHttpHandlers.add(name);
7784
+ }
7368
7785
  resolveSchedules() {
7369
7786
  for (const { reducer, scheduleAtCol, tableName } of this.pendingSchedules) {
7370
7787
  const functionName = this.functionExports.get(reducer());
@@ -7380,6 +7797,21 @@ var SchemaInner = class extends ModuleContext {
7380
7797
  });
7381
7798
  }
7382
7799
  }
7800
+ resolveHttpRoutes() {
7801
+ for (const route of this.pendingHttpRoutes) {
7802
+ const handlerFunction = this.httpHandlerExports.get(route.handler);
7803
+ if (handlerFunction === void 0) {
7804
+ throw new TypeError(
7805
+ `HTTP route for path '${route.path}' refers to a handler that was not exported.`
7806
+ );
7807
+ }
7808
+ this.moduleDef.httpRoutes.push({
7809
+ handlerFunction,
7810
+ method: route.method,
7811
+ path: route.path
7812
+ });
7813
+ }
7814
+ }
7383
7815
  };
7384
7816
  var Schema = class {
7385
7817
  #ctx;
@@ -7399,6 +7831,7 @@ var Schema = class {
7399
7831
  moduleExport[registerExport](registeredSchema, name);
7400
7832
  }
7401
7833
  registeredSchema.resolveSchedules();
7834
+ registeredSchema.resolveHttpRoutes();
7402
7835
  return makeHooks(registeredSchema);
7403
7836
  }
7404
7837
  get schemaType() {
@@ -7514,6 +7947,21 @@ var Schema = class {
7514
7947
  }
7515
7948
  return makeProcedureExport(this.#ctx, opts, params, ret, fn);
7516
7949
  }
7950
+ httpHandler(...args) {
7951
+ let opts, fn;
7952
+ switch (args.length) {
7953
+ case 1:
7954
+ [fn] = args;
7955
+ break;
7956
+ case 2:
7957
+ [opts, fn] = args;
7958
+ break;
7959
+ }
7960
+ return makeHttpHandlerExport(this.#ctx, opts, fn);
7961
+ }
7962
+ httpRouter(router) {
7963
+ return makeHttpRouterExport(this.#ctx, router);
7964
+ }
7517
7965
  /**
7518
7966
  * Bundle multiple reducers, procedures, etc into one value to export.
7519
7967
  * The name they will be exported with is their corresponding key in the `exports` argument.
@@ -7678,6 +8126,6 @@ statuses/index.js:
7678
8126
  *)
7679
8127
  */
7680
8128
 
7681
- export { ArrayBuilder, ArrayColumnBuilder, BoolBuilder, BoolColumnBuilder, BooleanExpr, ByteArrayBuilder, ByteArrayColumnBuilder, CaseConversionPolicy, ColumnBuilder, ColumnExpression, ConnectionIdBuilder, ConnectionIdColumnBuilder, F32Builder, F32ColumnBuilder, F64Builder, F64ColumnBuilder, I128Builder, I128ColumnBuilder, I16Builder, I16ColumnBuilder, I256Builder, I256ColumnBuilder, I32Builder, I32ColumnBuilder, I64Builder, I64ColumnBuilder, I8Builder, I8ColumnBuilder, IdentityBuilder, IdentityColumnBuilder, OptionBuilder, OptionColumnBuilder, ProductBuilder, ProductColumnBuilder, Range, RefBuilder, ResultBuilder, ResultColumnBuilder, RowBuilder, ScheduleAtBuilder, ScheduleAtColumnBuilder, SenderError, SimpleSumBuilder, SimpleSumColumnBuilder, SpacetimeHostError, StringBuilder, StringColumnBuilder, SumBuilder, SumColumnBuilder, TimeDurationBuilder, TimeDurationColumnBuilder, TimestampBuilder, TimestampColumnBuilder, TypeBuilder, U128Builder, U128ColumnBuilder, U16Builder, U16ColumnBuilder, U256Builder, U256ColumnBuilder, U32Builder, U32ColumnBuilder, U64Builder, U64ColumnBuilder, U8Builder, U8ColumnBuilder, UuidBuilder, UuidColumnBuilder, and, createTableRefFromDef, errors, evaluateBooleanExpr, getQueryAccessorName, getQueryTableName, getQueryWhereClause, isRowTypedQuery, isTypedQuery, literal, makeQueryBuilder, not, or, schema, t, table, toCamelCase, toComparableValue, toSql };
8129
+ export { ArrayBuilder, ArrayColumnBuilder, BoolBuilder, BoolColumnBuilder, BooleanExpr, ByteArrayBuilder, ByteArrayColumnBuilder, CaseConversionPolicy, ColumnBuilder, ColumnExpression, ConnectionIdBuilder, ConnectionIdColumnBuilder, F32Builder, F32ColumnBuilder, F64Builder, F64ColumnBuilder, I128Builder, I128ColumnBuilder, I16Builder, I16ColumnBuilder, I256Builder, I256ColumnBuilder, I32Builder, I32ColumnBuilder, I64Builder, I64ColumnBuilder, I8Builder, I8ColumnBuilder, IdentityBuilder, IdentityColumnBuilder, OptionBuilder, OptionColumnBuilder, ProductBuilder, ProductColumnBuilder, Range, RefBuilder, Request, ResultBuilder, ResultColumnBuilder, Router, RowBuilder, ScheduleAtBuilder, ScheduleAtColumnBuilder, SenderError, SimpleSumBuilder, SimpleSumColumnBuilder, SpacetimeHostError, StringBuilder, StringColumnBuilder, SumBuilder, SumColumnBuilder, SyncResponse, TimeDurationBuilder, TimeDurationColumnBuilder, TimestampBuilder, TimestampColumnBuilder, TypeBuilder, U128Builder, U128ColumnBuilder, U16Builder, U16ColumnBuilder, U256Builder, U256ColumnBuilder, U32Builder, U32ColumnBuilder, U64Builder, U64ColumnBuilder, U8Builder, U8ColumnBuilder, UuidBuilder, UuidColumnBuilder, and, createTableRefFromDef, errors, evaluateBooleanExpr, getQueryAccessorName, getQueryTableName, getQueryWhereClause, isRowTypedQuery, isTypedQuery, literal, makeQueryBuilder, not, or, schema, t, table, toCamelCase, toComparableValue, toSql };
7682
8130
  //# sourceMappingURL=index.mjs.map
7683
8131
  //# sourceMappingURL=index.mjs.map