@getstrata/bootstrap 0.2.23 → 0.2.25

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/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  // @bun
2
+ // ../../src/bootstrap/public-api.ts
3
+ import { appSchedule as appSchedule2, runDueScheduledTasks as runDueScheduledTasks2, Schedule as Schedule2 } from "@getstrata/core/scheduler/schedule";
4
+
2
5
  // ../../src/core/scheduler/schedule.ts
3
6
  class Schedule {
4
7
  tasks = [];
@@ -515,179 +518,9 @@ import {
515
518
  resolveApplicationQueue,
516
519
  setActiveApplicationContext
517
520
  } from "@getstrata/core/runtime/applicationRegistry.ts";
518
- // ../../src/core/crypto/nonCryptographicHash.ts
519
- function nonCryptographicDigest(input) {
520
- return Bun.hash(input).toString(16);
521
- }
522
-
523
- // ../../src/core/http/etag.ts
524
- function isEtagEnabled() {
525
- return (process.env.FEATURE_ETAG ?? "true") !== "false";
526
- }
527
- function formatWeakEtag(digest) {
528
- return `W/"${digest}"`;
529
- }
530
- function computeEtagFromJson(data) {
531
- const digest = nonCryptographicDigest(JSON.stringify(data)).slice(0, 32);
532
- return formatWeakEtag(digest);
533
- }
534
- function etagFromResource(resource) {
535
- const version = resource.updated_at ?? resource.created_at ?? "";
536
- const versionText = version instanceof Date ? version.toISOString() : version ? String(version) : "0";
537
- const digest = nonCryptographicDigest(`${String(resource.id ?? "0")}:${versionText}`).slice(0, 32);
538
- return formatWeakEtag(digest);
539
- }
540
- function normalizeEtag(value) {
541
- return value.trim();
542
- }
543
- function etagValuesMatch(left, right) {
544
- return normalizeEtag(left) === normalizeEtag(right);
545
- }
546
- function parseEtagList(header) {
547
- if (!header) {
548
- return [];
549
- }
550
- return header.split(",").map((value) => normalizeEtag(value)).filter(Boolean);
551
- }
552
- function ifNoneMatchSatisfied(request, etag) {
553
- const header = request.headers.get("if-none-match");
554
- if (!header) {
555
- return false;
556
- }
557
- if (header.trim() === "*") {
558
- return true;
559
- }
560
- return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
561
- }
562
- function ifMatchSatisfied(request, etag) {
563
- const header = request.headers.get("if-match");
564
- if (!header) {
565
- return false;
566
- }
567
- if (header.trim() === "*") {
568
- return true;
569
- }
570
- return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
571
- }
572
- function assertIfMatch(request, etag, options = {}) {
573
- const header = request.headers.get("if-match");
574
- if (!header) {
575
- if (options.required) {
576
- throw new PreconditionFailedError("If-Match header is required.");
577
- }
578
- return;
579
- }
580
- if (!ifMatchSatisfied(request, etag)) {
581
- throw new PreconditionFailedError("Resource ETag does not match If-Match.");
582
- }
583
- }
584
- function applyEtagHeaders(headers, etag) {
585
- const next = new Headers(headers);
586
- next.set("ETag", etag);
587
- next.set("Cache-Control", "private, must-revalidate");
588
- next.append("Vary", "Authorization");
589
- next.append("Vary", "X-Tenant-Id");
590
- return next;
591
- }
592
- function notModifiedResponse(etag) {
593
- return new Response(null, {
594
- status: 304,
595
- headers: applyEtagHeaders(new Headers, etag)
596
- });
597
- }
598
- function applyConditionalGet(request, response, etag) {
599
- if (!isEtagEnabled()) {
600
- return response;
601
- }
602
- if (ifNoneMatchSatisfied(request, etag)) {
603
- return notModifiedResponse(etag);
604
- }
605
- const headers = applyEtagHeaders(new Headers(response.headers), etag);
606
- return new Response(response.body, {
607
- status: response.status,
608
- statusText: response.statusText,
609
- headers
610
- });
611
- }
612
-
613
- // ../../src/core/http/conditionalResponse.ts
614
- function jsonResponse(data, init = {}) {
615
- return Response.json(data, {
616
- status: init.status ?? 200,
617
- headers: init.headers
618
- });
619
- }
620
- function conditionalJsonResponse(request, data, init = {}) {
621
- if (!request || !isEtagEnabled()) {
622
- return jsonResponse(data, init);
623
- }
624
- const etag = computeEtagFromJson(data);
625
- if (ifNoneMatchSatisfied(request, etag)) {
626
- return notModifiedResponse(etag);
627
- }
628
- const response = jsonResponse(data, init);
629
- const headers = new Headers(response.headers);
630
- headers.set("ETag", etag);
631
- headers.set("Cache-Control", "private, must-revalidate");
632
- headers.append("Vary", "Authorization");
633
- headers.append("Vary", "X-Tenant-Id");
634
- return new Response(response.body, {
635
- status: response.status,
636
- statusText: response.statusText,
637
- headers
638
- });
639
- }
640
-
641
- // ../../src/core/http/middleware.ts
642
- function isRouteHandler(value) {
643
- return typeof value === "function";
644
- }
645
- function isMethodRouteMap(value) {
646
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
647
- return false;
648
- }
649
- const entries = Object.entries(value);
650
- return entries.length > 0 && entries.every(([, handler]) => isRouteHandler(handler));
651
- }
652
- function composeMiddleware(...middleware) {
653
- return (handler) => {
654
- return async (request) => {
655
- let index = 0;
656
- const dispatch = async () => {
657
- if (index >= middleware.length) {
658
- return await handler(request);
659
- }
660
- const current = middleware[index];
661
- index += 1;
662
- if (!current) {
663
- return await handler(request);
664
- }
665
- return await current(request, dispatch);
666
- };
667
- return await dispatch();
668
- };
669
- };
670
- }
671
- function wrapRouteHandler(handler, middleware) {
672
- if (isMethodRouteMap(handler)) {
673
- const wrapped = {};
674
- for (const [method, routeHandler] of Object.entries(handler)) {
675
- wrapped[method] = composeMiddleware(...middleware)(routeHandler);
676
- }
677
- return wrapped;
678
- }
679
- if (isRouteHandler(handler)) {
680
- return composeMiddleware(...middleware)(handler);
681
- }
682
- return handler;
683
- }
684
- function applyMiddlewareToRoutes(routes, middleware) {
685
- const wrapped = {};
686
- for (const [path, routeHandler] of Object.entries(routes)) {
687
- wrapped[path] = wrapRouteHandler(routeHandler, middleware);
688
- }
689
- return wrapped;
690
- }
521
+ // ../../src/bootstrap/buildModuleRoutes.ts
522
+ import { conditionalJsonResponse } from "@getstrata/core/http";
523
+ import { applyMiddlewareToRoutes } from "@getstrata/core/http/middleware";
691
524
 
692
525
  // ../../src/bootstrap/httpKernel.ts
693
526
  import {
@@ -1090,6 +923,28 @@ function buildModuleRoutes(dependencies, options = {}) {
1090
923
  const prefixedModuleRoutes = prefixRouteMap(apiPrefix, moduleRoutes);
1091
924
  return applyMiddlewareToRoutes(registerOpenApiRouteMap(prefixedModuleRoutes, ["global", "api"]), middleware);
1092
925
  }
926
+ // ../../src/bootstrap/buildWebModuleRoutes.ts
927
+ import { applyMiddlewareToRoutes as applyMiddlewareToRoutes2 } from "@getstrata/core/http/middleware";
928
+ function buildWebModuleRoutes(dependencies, options = {}) {
929
+ const { modules = discoverModules(), clearRegistry = true, seedRoutes = {} } = options;
930
+ if (clearRegistry) {
931
+ routeRegistry.clear();
932
+ }
933
+ const kernel = createHttpKernel(dependencies);
934
+ const middleware = [...kernel.globalMiddleware(), ...kernel.group("web")];
935
+ const moduleRoutes = { ...seedRoutes };
936
+ for (const module of modules) {
937
+ if (!module.webRoutes) {
938
+ continue;
939
+ }
940
+ Object.assign(moduleRoutes, module.webRoutes({
941
+ dependencies,
942
+ cachedJson: async () => new Response(""),
943
+ kernel
944
+ }));
945
+ }
946
+ return applyMiddlewareToRoutes2(registerOpenApiRouteMap(moduleRoutes, ["global", "web"]), middleware);
947
+ }
1093
948
  // ../../src/bootstrap/cache/modelCacheTags.ts
1094
949
  function cacheTagsForModelWrite(tableName, action) {
1095
950
  const module = discoverModules().find((entry) => entry.tableName === tableName);
@@ -1217,7 +1072,6 @@ var db = new Proxy(function database() {}, {
1217
1072
  return typeof value === "function" ? value.bind(connection) : value;
1218
1073
  }
1219
1074
  });
1220
- var connection_default = db;
1221
1075
 
1222
1076
  // ../../src/modules/user/apiTokenTable.ts
1223
1077
  import { defineTable } from "@getstrata/core/database";
@@ -2049,9 +1903,6 @@ var queueConfig = {
2049
1903
  };
2050
1904
 
2051
1905
  // ../../src/core/config/envSchema.ts
2052
- function defineEnvSchema(schema) {
2053
- return schema;
2054
- }
2055
1906
  function validateEnv(schema, env = process.env) {
2056
1907
  const resolved = {};
2057
1908
  for (const [name, rule] of Object.entries(schema)) {
@@ -2080,6 +1931,7 @@ function validateEnv(schema, env = process.env) {
2080
1931
  }
2081
1932
 
2082
1933
  // ../../src/bootstrap/env.ts
1934
+ import { defineEnvSchema } from "@getstrata/core/config/envSchema";
2083
1935
  var appEnvSchema = defineEnvSchema({
2084
1936
  DATABASE_URL: { required: true, pattern: /^postgres(ql)?:\/\// },
2085
1937
  PORT: {
@@ -4371,9 +4223,6 @@ function resolveApplicationAuth2() {
4371
4223
  function resolveApplicationPolicyGate2() {
4372
4224
  return requireActiveApplicationContext().container.resolve(CORE_POLICY_GATE_TOKEN2);
4373
4225
  }
4374
- function resolveApplicationDependencies2() {
4375
- return requireActiveApplicationContext().dependencies;
4376
- }
4377
4226
 
4378
4227
  // ../../src/bootstrap/queue/defaultJobs.ts
4379
4228
  function registerDefaultJobs() {
@@ -4560,16 +4409,6 @@ class EtaViewEngine {
4560
4409
  });
4561
4410
  }
4562
4411
  }
4563
- // ../../src/core/view/htmlResponse.ts
4564
- function htmlResponse(html, init = {}) {
4565
- return new Response(html, {
4566
- status: init.status ?? 200,
4567
- statusText: init.statusText,
4568
- headers: {
4569
- "Content-Type": "text/html; charset=utf-8"
4570
- }
4571
- });
4572
- }
4573
4412
  // ../../src/core/http/cookies.ts
4574
4413
  function readRequestCookie(request, name) {
4575
4414
  const cookies = request.cookies;
@@ -4858,50 +4697,25 @@ function createAppContext() {
4858
4697
  }
4859
4698
  // ../../src/bootstrap/createWebRoutes.ts
4860
4699
  import { join as join5 } from "path";
4700
+ import { htmlResponse as htmlResponse2 } from "@getstrata/core/view";
4861
4701
  function registerRoute(method, path, middleware) {
4862
4702
  routeRegistry.register({ method, path, middleware });
4863
4703
  }
4864
- function registerRouteMap(routes, middleware) {
4865
- const registered = {};
4866
- for (const [path, handler] of Object.entries(routes)) {
4867
- if (handler && typeof handler === "object" && !Array.isArray(handler)) {
4868
- const methodMap = handler;
4869
- registered[path] = methodMap;
4870
- for (const method of Object.keys(methodMap)) {
4871
- registerRoute(method.toUpperCase(), path, middleware);
4872
- }
4873
- continue;
4874
- }
4875
- registered[path] = handler;
4876
- registerRoute("GET", path, middleware);
4877
- }
4878
- return registered;
4879
- }
4880
4704
  function createWebRoutes(dependencies) {
4881
- const kernel = createHttpKernel(dependencies);
4882
- const middleware = [...kernel.globalMiddleware(), ...kernel.group("web")];
4883
- const moduleRoutes = {
4884
- "/": () => Response.redirect("/organizations", 302)
4885
- };
4886
- registerRoute("GET", "/", ["global", "web"]);
4887
- for (const module of discoverModules()) {
4888
- if (!module.webRoutes) {
4889
- continue;
4705
+ const wrappedRoutes = buildWebModuleRoutes(dependencies, {
4706
+ clearRegistry: false,
4707
+ seedRoutes: {
4708
+ "/": () => Response.redirect("/organizations", 302)
4890
4709
  }
4891
- Object.assign(moduleRoutes, module.webRoutes({
4892
- dependencies,
4893
- cachedJson: async () => htmlResponse(""),
4894
- kernel
4895
- }));
4896
- }
4897
- const wrappedRoutes = applyMiddlewareToRoutes(registerRouteMap(moduleRoutes, ["global", "web"]), middleware);
4710
+ });
4711
+ registerRoute("GET", "/", ["global", "web"]);
4898
4712
  wrappedRoutes["/assets/*"] = async (request) => {
4899
4713
  registerRoute("GET", "/assets/*", ["global", "web"]);
4900
4714
  const pathname = new URL(request.url).pathname;
4901
4715
  const relativePath = pathname.replace(/^\//, "");
4902
4716
  const file = Bun.file(join5(process.cwd(), "public", relativePath));
4903
4717
  if (!await file.exists()) {
4904
- return htmlResponse("Not Found", { status: 404 });
4718
+ return htmlResponse2("Not Found", { status: 404 });
4905
4719
  }
4906
4720
  return new Response(file);
4907
4721
  };
@@ -4921,6 +4735,97 @@ function mergeWebRoutes(dependencies, routes) {
4921
4735
  function createAppDependencies() {
4922
4736
  return createAppContext().dependencies;
4923
4737
  }
4738
+ // ../../src/core/crypto/nonCryptographicHash.ts
4739
+ function nonCryptographicDigest(input) {
4740
+ return Bun.hash(input).toString(16);
4741
+ }
4742
+
4743
+ // ../../src/core/http/etag.ts
4744
+ function isEtagEnabled() {
4745
+ return (process.env.FEATURE_ETAG ?? "true") !== "false";
4746
+ }
4747
+ function formatWeakEtag(digest) {
4748
+ return `W/"${digest}"`;
4749
+ }
4750
+ function etagFromResource(resource) {
4751
+ const version = resource.updated_at ?? resource.created_at ?? "";
4752
+ const versionText = version instanceof Date ? version.toISOString() : version ? String(version) : "0";
4753
+ const digest = nonCryptographicDigest(`${String(resource.id ?? "0")}:${versionText}`).slice(0, 32);
4754
+ return formatWeakEtag(digest);
4755
+ }
4756
+ function normalizeEtag(value) {
4757
+ return value.trim();
4758
+ }
4759
+ function etagValuesMatch(left, right) {
4760
+ return normalizeEtag(left) === normalizeEtag(right);
4761
+ }
4762
+ function parseEtagList(header) {
4763
+ if (!header) {
4764
+ return [];
4765
+ }
4766
+ return header.split(",").map((value) => normalizeEtag(value)).filter(Boolean);
4767
+ }
4768
+ function ifNoneMatchSatisfied(request, etag) {
4769
+ const header = request.headers.get("if-none-match");
4770
+ if (!header) {
4771
+ return false;
4772
+ }
4773
+ if (header.trim() === "*") {
4774
+ return true;
4775
+ }
4776
+ return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
4777
+ }
4778
+ function ifMatchSatisfied(request, etag) {
4779
+ const header = request.headers.get("if-match");
4780
+ if (!header) {
4781
+ return false;
4782
+ }
4783
+ if (header.trim() === "*") {
4784
+ return true;
4785
+ }
4786
+ return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
4787
+ }
4788
+ function assertIfMatch(request, etag, options = {}) {
4789
+ const header = request.headers.get("if-match");
4790
+ if (!header) {
4791
+ if (options.required) {
4792
+ throw new PreconditionFailedError("If-Match header is required.");
4793
+ }
4794
+ return;
4795
+ }
4796
+ if (!ifMatchSatisfied(request, etag)) {
4797
+ throw new PreconditionFailedError("Resource ETag does not match If-Match.");
4798
+ }
4799
+ }
4800
+ function applyEtagHeaders(headers, etag) {
4801
+ const next = new Headers(headers);
4802
+ next.set("ETag", etag);
4803
+ next.set("Cache-Control", "private, must-revalidate");
4804
+ next.append("Vary", "Authorization");
4805
+ next.append("Vary", "X-Tenant-Id");
4806
+ return next;
4807
+ }
4808
+ function notModifiedResponse(etag) {
4809
+ return new Response(null, {
4810
+ status: 304,
4811
+ headers: applyEtagHeaders(new Headers, etag)
4812
+ });
4813
+ }
4814
+ function applyConditionalGet(request, response, etag) {
4815
+ if (!isEtagEnabled()) {
4816
+ return response;
4817
+ }
4818
+ if (ifNoneMatchSatisfied(request, etag)) {
4819
+ return notModifiedResponse(etag);
4820
+ }
4821
+ const headers = applyEtagHeaders(new Headers(response.headers), etag);
4822
+ return new Response(response.body, {
4823
+ status: response.status,
4824
+ statusText: response.statusText,
4825
+ headers
4826
+ });
4827
+ }
4828
+
4924
4829
  // ../../src/core/tenant/tenantContext.ts
4925
4830
  var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
4926
4831
 
@@ -4980,148 +4885,8 @@ function securedBindRouteModelByKey(param, resolver, authorization, handler) {
4980
4885
  return response;
4981
4886
  };
4982
4887
  }
4983
- // ../../src/core/auth/accessControl.ts
4984
- var ROLE_RANK = {
4985
- member: 1,
4986
- admin: 2,
4987
- owner: 3
4988
- };
4989
- function isGlobalAdmin(user) {
4990
- return user?.role === "admin";
4991
- }
4992
- function hasMinimumOrgRole(role, minimum) {
4993
- if (!role) {
4994
- return false;
4995
- }
4996
- return ROLE_RANK[role] >= ROLE_RANK[minimum];
4997
- }
4998
- function resolveUserId(user) {
4999
- const userId = typeof user.id === "number" ? user.id : Number(user.id);
5000
- if (!Number.isInteger(userId) || userId <= 0) {
5001
- throw new ForbiddenError("Invalid authenticated user.");
5002
- }
5003
- return userId;
5004
- }
5005
-
5006
- // ../../src/modules/organization/memberRepository.ts
5007
- class OrganizationMemberRepository {
5008
- constructor() {}
5009
- async findMembership(userId, organizationId) {
5010
- const rows = await connection_default`
5011
- SELECT id, organization_id, user_id, role, created_at
5012
- FROM organization_member
5013
- WHERE user_id = ${userId} AND organization_id = ${organizationId}
5014
- LIMIT 1
5015
- `;
5016
- return rows[0] ?? null;
5017
- }
5018
- async listForUser(userId) {
5019
- return await connection_default`
5020
- SELECT id, organization_id, user_id, role, created_at
5021
- FROM organization_member
5022
- WHERE user_id = ${userId}
5023
- ORDER BY organization_id
5024
- `;
5025
- }
5026
- async listForOrganization(organizationId) {
5027
- return await connection_default`
5028
- SELECT id, organization_id, user_id, role, created_at
5029
- FROM organization_member
5030
- WHERE organization_id = ${organizationId}
5031
- ORDER BY id
5032
- `;
5033
- }
5034
- async addMember(input) {
5035
- const rows = await connection_default`
5036
- INSERT INTO organization_member (organization_id, user_id, role)
5037
- VALUES (${input.organizationId}, ${input.userId}, ${input.role ?? "member"})
5038
- RETURNING id, organization_id, user_id, role, created_at
5039
- `;
5040
- const row = rows[0];
5041
- if (!row) {
5042
- throw new Error("Organization member insert did not return a row.");
5043
- }
5044
- return row;
5045
- }
5046
- async removeMember(organizationId, userId) {
5047
- const rows = await connection_default`
5048
- DELETE FROM organization_member
5049
- WHERE organization_id = ${organizationId} AND user_id = ${userId}
5050
- RETURNING id
5051
- `;
5052
- return rows.length > 0;
5053
- }
5054
- }
5055
- var memberRepository_default = OrganizationMemberRepository;
5056
-
5057
- // ../../src/core/auth/membershipContext.ts
5058
- var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
5059
- var membershipRepository = new memberRepository_default;
5060
-
5061
- // ../../src/core/auth/membershipService.ts
5062
- class MembershipService {
5063
- members;
5064
- constructor(members = membershipRepository) {
5065
- this.members = members;
5066
- }
5067
- async listOrganizationIdsForUser(userId) {
5068
- const memberships = await this.members.listForUser(userId);
5069
- return memberships.map((membership) => membership.organization_id);
5070
- }
5071
- async getOrgRole(userId, organizationId) {
5072
- const membership = await this.members.findMembership(userId, organizationId);
5073
- return membership?.role ?? null;
5074
- }
5075
- async requireOrgAccess(organizationId, minimumRole = "member", user = currentAuthUser()) {
5076
- if (!user) {
5077
- throw new ForbiddenError("Authentication required.");
5078
- }
5079
- if (isGlobalAdmin(user)) {
5080
- return "owner";
5081
- }
5082
- const role = await this.getOrgRole(resolveUserId(user), organizationId);
5083
- if (!role || !hasMinimumOrgRole(role, minimumRole)) {
5084
- throw new ForbiddenError("Organization membership required.");
5085
- }
5086
- return role;
5087
- }
5088
- async filterAccessibleOrganizationIds(organizationIds, user = currentAuthUser()) {
5089
- if (!user) {
5090
- return [];
5091
- }
5092
- if (isGlobalAdmin(user)) {
5093
- return organizationIds;
5094
- }
5095
- const allowed = new Set(await this.listOrganizationIdsForUser(resolveUserId(user)));
5096
- return organizationIds.filter((organizationId) => allowed.has(organizationId));
5097
- }
5098
- async addOwnerOnOrganizationCreate(organizationId, userId) {
5099
- await this.members.addMember({
5100
- organizationId,
5101
- userId,
5102
- role: "owner"
5103
- });
5104
- }
5105
- listMembersForOrganization(organizationId) {
5106
- return this.members.listForOrganization(organizationId);
5107
- }
5108
- addMember(input) {
5109
- return this.members.addMember(input);
5110
- }
5111
- removeMember(organizationId, userId) {
5112
- return this.members.removeMember(organizationId, userId);
5113
- }
5114
- }
5115
- var membershipService_default = MembershipService;
5116
-
5117
- // ../../src/core/auth/resolveMembershipService.ts
5118
- function resolveMembershipService() {
5119
- const dependencies = resolveApplicationDependencies2();
5120
- if (dependencies.container.has("core.membership")) {
5121
- return dependencies.container.resolve("core.membership");
5122
- }
5123
- return new membershipService_default;
5124
- }
4888
+ // ../../src/bootstrap/membershipService.ts
4889
+ import { resolveMembershipService } from "@getstrata/core/auth/membershipService";
5125
4890
  // ../../src/core/http/csrfProtection.ts
5126
4891
  var DEFAULT_CSRF_TTL_MS = 60 * 60 * 1000;
5127
4892
  function createCsrfProtection(secret, options = {}) {
@@ -5216,7 +4981,7 @@ function createRouteKernel(dependencies) {
5216
4981
  return createHttpKernel(dependencies);
5217
4982
  }
5218
4983
  // ../../src/bootstrap/web/server.ts
5219
- function wrapRouteHandler2(handler) {
4984
+ function wrapRouteHandler(handler) {
5220
4985
  return async (request) => {
5221
4986
  const response = await handler(request);
5222
4987
  return response ?? new Response("Not Found", { status: 404 });
@@ -5226,7 +4991,7 @@ function convertAppRoutesToBunRoutes(routes) {
5226
4991
  const bunRoutes = {};
5227
4992
  for (const [path, handler] of Object.entries(routes)) {
5228
4993
  if (typeof handler === "function") {
5229
- bunRoutes[path] = { GET: wrapRouteHandler2(handler) };
4994
+ bunRoutes[path] = { GET: wrapRouteHandler(handler) };
5230
4995
  continue;
5231
4996
  }
5232
4997
  if (handler && typeof handler === "object" && !Array.isArray(handler)) {
@@ -5235,7 +5000,7 @@ function convertAppRoutesToBunRoutes(routes) {
5235
5000
  if (typeof methodHandler !== "function") {
5236
5001
  continue;
5237
5002
  }
5238
- methods[method.toUpperCase()] = wrapRouteHandler2(methodHandler);
5003
+ methods[method.toUpperCase()] = wrapRouteHandler(methodHandler);
5239
5004
  }
5240
5005
  if (Object.keys(methods).length > 0) {
5241
5006
  bunRoutes[path] = methods;
@@ -5350,7 +5115,7 @@ export {
5350
5115
  securedBindRouteModel,
5351
5116
  scheduleRunCommand,
5352
5117
  runProviderPhase,
5353
- runDueScheduledTasks,
5118
+ runDueScheduledTasks2 as runDueScheduledTasks,
5354
5119
  routeRegistry,
5355
5120
  routeParams,
5356
5121
  resolveService,
@@ -5382,12 +5147,13 @@ export {
5382
5147
  configureModulesDirectory,
5383
5148
  collectProviders,
5384
5149
  cacheTagsForModelWrite,
5150
+ buildWebModuleRoutes,
5385
5151
  buildModuleRoutes,
5386
5152
  assertProductionSecrets,
5387
5153
  assertAppDependenciesComplete,
5388
- appSchedule,
5154
+ appSchedule2 as appSchedule,
5389
5155
  ServiceContainer,
5390
- Schedule,
5156
+ Schedule2 as Schedule,
5391
5157
  RouteRegistry,
5392
5158
  REDIS_URL_CONFIG_KEY,
5393
5159
  DEFAULT_APP_PORT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Strata application bootstrap — HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,6 +25,11 @@
25
25
  "import": "./dist/entries/buildModuleRoutes.js",
26
26
  "default": "./dist/entries/buildModuleRoutes.js"
27
27
  },
28
+ "./buildWebModuleRoutes": {
29
+ "types": "./dist/bootstrap/buildWebModuleRoutes.d.ts",
30
+ "import": "./dist/entries/buildWebModuleRoutes.js",
31
+ "default": "./dist/entries/buildWebModuleRoutes.js"
32
+ },
28
33
  "./cache/modelCacheTags": {
29
34
  "types": "./dist/bootstrap/cache/modelCacheTags.d.ts",
30
35
  "import": "./dist/entries/cache/modelCacheTags.js",
@@ -137,14 +142,14 @@
137
142
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta --external @getstrata/core",
138
143
  "build:types": "tsc -p tsconfig.types.json && bun ../../scripts/prune-bootstrap-dist-types.ts",
139
144
  "prepublishOnly": "bun run build",
140
- "build:subpaths": "bun build entries/applicationRegistry.ts entries/buildModuleRoutes.ts entries/cache/modelCacheTags.ts entries/config.ts entries/context.ts entries/contracts.ts entries/createWebRoutes.ts entries/dependencies.ts entries/discoverModules.ts entries/http/securedRouteModelBinding.ts entries/httpKernel.ts entries/membershipService.ts entries/queue/defaultJobs.ts entries/providers.ts entries/providers/view.ts entries/routeRegistry.ts entries/secretsGuard.ts entries/web/forms.ts entries/web/routing.ts entries/web/server.ts entries/web/session.ts entries/web/slug.ts --outdir dist --root . --target bun --external bun --external eta --external @getstrata/core",
145
+ "build:subpaths": "bun build entries/applicationRegistry.ts entries/buildModuleRoutes.ts entries/buildWebModuleRoutes.ts entries/cache/modelCacheTags.ts entries/config.ts entries/context.ts entries/contracts.ts entries/createWebRoutes.ts entries/dependencies.ts entries/discoverModules.ts entries/http/securedRouteModelBinding.ts entries/httpKernel.ts entries/membershipService.ts entries/queue/defaultJobs.ts entries/providers.ts entries/providers/view.ts entries/routeRegistry.ts entries/secretsGuard.ts entries/web/forms.ts entries/web/routing.ts entries/web/server.ts entries/web/session.ts entries/web/slug.ts --outdir dist --root . --target bun --external bun --external eta --external @getstrata/core",
141
146
  "build:shims": "true"
142
147
  },
143
148
  "publishConfig": {
144
149
  "access": "public"
145
150
  },
146
151
  "peerDependencies": {
147
- "@getstrata/core": "^0.5.31",
152
+ "@getstrata/core": "^0.5.33",
148
153
  "typescript": "^5.9.0"
149
154
  }
150
155
  }