bun-plugin-stx 0.2.67 → 0.2.69
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/{chunk-wv4bcqba.js → chunk-sm4gbwrd.js} +58 -9
- package/dist/serve.d.ts +14 -0
- package/dist/serve.js +485 -383
- package/package.json +3 -3
|
@@ -583,10 +583,11 @@ function getRouterScript() {
|
|
|
583
583
|
return `
|
|
584
584
|
;(function(){
|
|
585
585
|
'use strict';
|
|
586
|
-
|
|
586
|
+
var ROUTER_REV=3;
|
|
587
|
+
if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;
|
|
587
588
|
|
|
588
589
|
// \u2500\u2500 Configuration \u2500\u2500
|
|
589
|
-
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false};
|
|
590
|
+
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50};
|
|
590
591
|
var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
|
|
591
592
|
var containerSel=o.container;
|
|
592
593
|
var debug=!!o.debug;
|
|
@@ -646,6 +647,40 @@ function getRouterScript() {
|
|
|
646
647
|
var prefetching={};
|
|
647
648
|
var isNavigating=false;
|
|
648
649
|
|
|
650
|
+
// LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix
|
|
651
|
+
// (stacksjs/stx#1719) these were plain objects with no eviction, so
|
|
652
|
+
// hover-prefetch on a sidebar of 200 internal links retained ~10MB
|
|
653
|
+
// of HTML strings for the page's lifetime. cacheOrder is a most-
|
|
654
|
+
// recently-used-last list of keys; setCache pushes new entries and
|
|
655
|
+
// evicts the oldest when length exceeds o.prefetchCacheMax (default
|
|
656
|
+
// 50). touchCache promotes a key on cache-hit. We use a plain array
|
|
657
|
+
// rather than a Map to keep the diff localized and the runtime
|
|
658
|
+
// hand-minified shape consistent.
|
|
659
|
+
var cacheOrder=[];
|
|
660
|
+
function setCache(key,html,layout,group){
|
|
661
|
+
if(!(key in cache))cacheOrder.push(key);
|
|
662
|
+
cache[key]=html;
|
|
663
|
+
layoutCache[key]=layout;
|
|
664
|
+
layoutGroupCache[key]=group;
|
|
665
|
+
while(cacheOrder.length>o.prefetchCacheMax){
|
|
666
|
+
var oldest=cacheOrder.shift();
|
|
667
|
+
delete cache[oldest];
|
|
668
|
+
delete layoutCache[oldest];
|
|
669
|
+
delete layoutGroupCache[oldest];
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function touchCache(key){
|
|
673
|
+
var i=cacheOrder.indexOf(key);
|
|
674
|
+
if(i>=0){cacheOrder.splice(i,1);cacheOrder.push(key)}
|
|
675
|
+
}
|
|
676
|
+
function evictCache(key){
|
|
677
|
+
var i=cacheOrder.indexOf(key);
|
|
678
|
+
if(i>=0)cacheOrder.splice(i,1);
|
|
679
|
+
delete cache[key];
|
|
680
|
+
delete layoutCache[key];
|
|
681
|
+
delete layoutGroupCache[key];
|
|
682
|
+
}
|
|
683
|
+
|
|
649
684
|
// Extract top-level CSS blocks (rules AND @media blocks with nested braces).
|
|
650
685
|
// The old regex ([^{}]+{[^{}]*}) silently dropped @media blocks because
|
|
651
686
|
// it couldn't handle nested braces \u2014 causing responsive classes (lg:, md:)
|
|
@@ -713,6 +748,12 @@ function getRouterScript() {
|
|
|
713
748
|
var u=new URL(url,location.origin);
|
|
714
749
|
return u.pathname+u.search;
|
|
715
750
|
}
|
|
751
|
+
// Keep SPA fetches inside the active locale (/en/... when viewing English).
|
|
752
|
+
function withCurrentLocale(href){
|
|
753
|
+
var d=window.__stxI18n;
|
|
754
|
+
if(d&&typeof d.localizeHref==='function')return d.localizeHref(href);
|
|
755
|
+
return href;
|
|
756
|
+
}
|
|
716
757
|
function defaultLayoutGroup(layout){
|
|
717
758
|
if(!layout)return 'app';
|
|
718
759
|
var clean=String(layout).replace(/\\\\/g,'/');
|
|
@@ -756,6 +797,9 @@ function getRouterScript() {
|
|
|
756
797
|
}
|
|
757
798
|
|
|
758
799
|
function navigate(url,pushState,force){
|
|
800
|
+
// Lang-picker passes force=true with an already-localized path (/en/...).
|
|
801
|
+
// Re-localizing would map it back to the *current* locale and no-op.
|
|
802
|
+
if(!force) url=withCurrentLocale(url);
|
|
759
803
|
log('[router] navigate() called:',url,'isNavigating:',isNavigating);
|
|
760
804
|
if(isNavigating)return;
|
|
761
805
|
var t=new URL(url,location.origin);
|
|
@@ -788,6 +832,9 @@ function getRouterScript() {
|
|
|
788
832
|
function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}
|
|
789
833
|
|
|
790
834
|
if(o.cache&&cache[targetPath]&&!force){
|
|
835
|
+
// Cache hit \u2192 promote in LRU order so this entry survives eviction
|
|
836
|
+
// longer than other entries that haven't been touched.
|
|
837
|
+
touchCache(targetPath);
|
|
791
838
|
if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){
|
|
792
839
|
// Layout changed \u2014 fetch full page and do full document swap
|
|
793
840
|
log('[router] cache hit but layout changed \u2014 fetching full page');
|
|
@@ -807,7 +854,7 @@ function getRouterScript() {
|
|
|
807
854
|
done();
|
|
808
855
|
}
|
|
809
856
|
else {
|
|
810
|
-
if(force&&cache[targetPath])
|
|
857
|
+
if(force&&cache[targetPath])evictCache(targetPath);
|
|
811
858
|
// First fetch as fragment (SPA mode) when the configured container is
|
|
812
859
|
// the page content. Custom app-shell containers need full documents so
|
|
813
860
|
// the router does not inject a <main> fragment into the wrong element.
|
|
@@ -833,7 +880,7 @@ else {
|
|
|
833
880
|
}).then(function(result){
|
|
834
881
|
if(!result)return;
|
|
835
882
|
if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;
|
|
836
|
-
if(o.cache)
|
|
883
|
+
if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup);
|
|
837
884
|
swap(result.html,targetPath,pushState,targetHash);
|
|
838
885
|
}).catch(function(err){
|
|
839
886
|
console.error('[router] fetch error:',err);
|
|
@@ -1207,6 +1254,7 @@ else {
|
|
|
1207
1254
|
if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;
|
|
1208
1255
|
if(link.target==='_blank')return false;
|
|
1209
1256
|
if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;
|
|
1257
|
+
if(href.startsWith('?'))return true;
|
|
1210
1258
|
if(href===location.pathname)return false;
|
|
1211
1259
|
if(!getContainer())return false;
|
|
1212
1260
|
return true;
|
|
@@ -1233,7 +1281,7 @@ else {
|
|
|
1233
1281
|
e.preventDefault();
|
|
1234
1282
|
e.stopPropagation();
|
|
1235
1283
|
log('[router] navigating to:',href);
|
|
1236
|
-
navigate(href);
|
|
1284
|
+
navigate(withCurrentLocale(href));
|
|
1237
1285
|
},true);
|
|
1238
1286
|
|
|
1239
1287
|
// \u2500\u2500 Back/forward \u2500\u2500
|
|
@@ -1247,7 +1295,7 @@ else {
|
|
|
1247
1295
|
if(!e.target||!e.target.closest)return;
|
|
1248
1296
|
var link=e.target.closest('[data-stx-link]');
|
|
1249
1297
|
if(!link)return;
|
|
1250
|
-
var href=link.getAttribute('href');
|
|
1298
|
+
var href=withCurrentLocale(link.getAttribute('href'));
|
|
1251
1299
|
var key=cacheKey(href);
|
|
1252
1300
|
if(cache[key]||prefetching[key])return;
|
|
1253
1301
|
prefetching[key]=true;
|
|
@@ -1258,7 +1306,7 @@ else {
|
|
|
1258
1306
|
var pGroup=r.headers.get('X-STX-Layout-Group')||'';
|
|
1259
1307
|
return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});
|
|
1260
1308
|
}).then(function(result){
|
|
1261
|
-
if(o.cache)
|
|
1309
|
+
if(o.cache)setCache(key,result.html,result.layout,result.layoutGroup);
|
|
1262
1310
|
}).catch(function(){}).finally(function(){delete prefetching[key]});
|
|
1263
1311
|
},true);
|
|
1264
1312
|
}
|
|
@@ -1355,15 +1403,16 @@ else {
|
|
|
1355
1403
|
var key=cacheKey(url);
|
|
1356
1404
|
if(!cache[key]){
|
|
1357
1405
|
var wantsFragment=shouldUseFragmentResponse();
|
|
1358
|
-
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){
|
|
1406
|
+
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){setCache(key,result.html,result.layout,result.layoutGroup)}).catch(function(){});
|
|
1359
1407
|
}
|
|
1360
1408
|
},
|
|
1361
|
-
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk]},
|
|
1409
|
+
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk];cacheOrder.length=0},
|
|
1362
1410
|
cache:cache,
|
|
1363
1411
|
swap:swap,
|
|
1364
1412
|
updateNav:updateNav
|
|
1365
1413
|
};
|
|
1366
1414
|
|
|
1415
|
+
router.__rev=ROUTER_REV;
|
|
1367
1416
|
window.__stxRouter=router;
|
|
1368
1417
|
window.stxRouter=router;
|
|
1369
1418
|
if(window.stx)window.stx.router=router;
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the candidate regexes for a dynamic route file (stacksjs/stx#1927).
|
|
3
|
+
*
|
|
4
|
+
* `fileRouteBase` is the route path with the file extension already
|
|
5
|
+
* stripped (e.g. `judges/[id]/profile/index`). Returns one regex per
|
|
6
|
+
* acceptable URL shape:
|
|
7
|
+
* - the literal path with `[param]` → `([^/]+)`
|
|
8
|
+
* - the same with a trailing `/index` removed, so
|
|
9
|
+
* `judges/[id]/profile/index` also matches `judges/35/profile`
|
|
10
|
+
* (without this, nested dynamic routes 404 on hard reload)
|
|
11
|
+
* - both of the above again with a leading `pages/` stripped, for
|
|
12
|
+
* projects that nest routable views under `pages/`
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildDynamicRouteRegexes(fileRouteBase: string): RegExp[];
|
|
1
15
|
export declare function bundleBrowserAsset(entrypoint: string): Promise<Response>;
|
|
2
16
|
/**
|
|
3
17
|
* Start the STX development server
|
package/dist/serve.js
CHANGED
|
@@ -7039,6 +7039,7 @@ class Logger3 {
|
|
|
7039
7039
|
}
|
|
7040
7040
|
}
|
|
7041
7041
|
var logger3 = new Logger3("stacks");
|
|
7042
|
+
|
|
7042
7043
|
class BunfigError2 extends Error {
|
|
7043
7044
|
timestamp;
|
|
7044
7045
|
context;
|
|
@@ -8693,6 +8694,21 @@ var defaultGeneratedDir4 = resolve12(process20.cwd(), "src/generated");
|
|
|
8693
8694
|
|
|
8694
8695
|
// src/serve.ts
|
|
8695
8696
|
var defaultStxModule = import("@stacksjs/stx");
|
|
8697
|
+
function buildDynamicRouteRegexes(fileRouteBase) {
|
|
8698
|
+
const toPattern = (p) => p.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
8699
|
+
const patterns = [new RegExp(`^${toPattern(fileRouteBase)}$`)];
|
|
8700
|
+
const noIndex = fileRouteBase.replace(/\/index$/, "");
|
|
8701
|
+
if (noIndex !== fileRouteBase)
|
|
8702
|
+
patterns.push(new RegExp(`^${toPattern(noIndex)}$`));
|
|
8703
|
+
if (fileRouteBase.startsWith("pages/")) {
|
|
8704
|
+
const pretty = fileRouteBase.slice(6);
|
|
8705
|
+
patterns.push(new RegExp(`^${toPattern(pretty)}$`));
|
|
8706
|
+
const prettyNoIndex = pretty.replace(/\/index$/, "");
|
|
8707
|
+
if (prettyNoIndex !== pretty)
|
|
8708
|
+
patterns.push(new RegExp(`^${toPattern(prettyNoIndex)}$`));
|
|
8709
|
+
}
|
|
8710
|
+
return patterns;
|
|
8711
|
+
}
|
|
8696
8712
|
var defaultStxConfig = {
|
|
8697
8713
|
partialsDir: "partials",
|
|
8698
8714
|
componentsDir: "components",
|
|
@@ -8766,14 +8782,14 @@ async function bundleBrowserAsset(entrypoint) {
|
|
|
8766
8782
|
status: 500,
|
|
8767
8783
|
headers: {
|
|
8768
8784
|
"Content-Type": "text/plain; charset=utf-8",
|
|
8769
|
-
"Cache-Control": "no-
|
|
8785
|
+
"Cache-Control": "no-store"
|
|
8770
8786
|
}
|
|
8771
8787
|
});
|
|
8772
8788
|
}
|
|
8773
8789
|
return new Response(await result.outputs[0].text(), {
|
|
8774
8790
|
headers: {
|
|
8775
8791
|
"Content-Type": "application/javascript",
|
|
8776
|
-
"Cache-Control": "no-
|
|
8792
|
+
"Cache-Control": "no-store"
|
|
8777
8793
|
}
|
|
8778
8794
|
});
|
|
8779
8795
|
}
|
|
@@ -8819,8 +8835,10 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8819
8835
|
}
|
|
8820
8836
|
const routes = new Map;
|
|
8821
8837
|
let sourceFiles = null;
|
|
8838
|
+
let discoveredPagePaths = null;
|
|
8822
8839
|
let assetsInitialized = false;
|
|
8823
8840
|
const htmlCache = new Map;
|
|
8841
|
+
const ENABLE_HTML_CACHE = false;
|
|
8824
8842
|
const crosswindCssCache = new Map;
|
|
8825
8843
|
const hmrClients = new Set;
|
|
8826
8844
|
const hmrEncoder = new TextEncoder;
|
|
@@ -8991,6 +9009,31 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8991
9009
|
const authRedirectTo = authConfig?.redirectTo ?? "/login";
|
|
8992
9010
|
const authHome = authConfig?.home ?? "/";
|
|
8993
9011
|
const extraProtectedPrefixes = authConfig?.protectedPaths ?? [];
|
|
9012
|
+
let activeServeLocale = null;
|
|
9013
|
+
let activeServeSearch = "";
|
|
9014
|
+
function injectServeLocaleContext(context) {
|
|
9015
|
+
if (!activeServeLocale)
|
|
9016
|
+
return;
|
|
9017
|
+
const loc = activeServeLocale;
|
|
9018
|
+
context.locale = loc;
|
|
9019
|
+
context.t = (key, values) => {
|
|
9020
|
+
const translate = globalThis.t;
|
|
9021
|
+
return typeof translate === "function" ? translate(key, values, loc) : key;
|
|
9022
|
+
};
|
|
9023
|
+
}
|
|
9024
|
+
function injectServeRequestContext(context) {
|
|
9025
|
+
if (!activeServeSearch)
|
|
9026
|
+
return;
|
|
9027
|
+
context.__stxServeSearch = activeServeSearch;
|
|
9028
|
+
globalThis.__stxServeSearch = activeServeSearch;
|
|
9029
|
+
}
|
|
9030
|
+
function htmlCacheKey(filePath) {
|
|
9031
|
+
const loc = i18nConfig ? activeServeLocale ?? i18nConfig.defaultLocale : "";
|
|
9032
|
+
const search = activeServeSearch || "";
|
|
9033
|
+
if (!loc && !search)
|
|
9034
|
+
return filePath;
|
|
9035
|
+
return `${filePath}\x00${loc}\x00${search}`;
|
|
9036
|
+
}
|
|
8994
9037
|
const builtInMiddleware = authConfig === null ? {} : {
|
|
8995
9038
|
auth: (_req, ctx) => {
|
|
8996
9039
|
const tok = ctx.cookies[authCookieName];
|
|
@@ -9027,6 +9070,37 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9027
9070
|
return out;
|
|
9028
9071
|
}
|
|
9029
9072
|
const globalMiddlewareNames = options.globalMiddleware ?? [];
|
|
9073
|
+
function stxViewFileToRoutePath(filePath) {
|
|
9074
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
9075
|
+
for (const pattern of patterns) {
|
|
9076
|
+
const normPattern = pattern.replace(/\\/g, "/").replace(/\/$/, "");
|
|
9077
|
+
if (!normalized.startsWith(`${normPattern}/`))
|
|
9078
|
+
continue;
|
|
9079
|
+
let rel = normalized.slice(normPattern.length + 1).replace(/\.(stx|md|html)$/, "");
|
|
9080
|
+
if (rel.includes("["))
|
|
9081
|
+
return null;
|
|
9082
|
+
if (rel === "index" || rel === "")
|
|
9083
|
+
return "/";
|
|
9084
|
+
if (rel.endsWith("/index"))
|
|
9085
|
+
rel = rel.slice(0, -6) || "index";
|
|
9086
|
+
return rel === "index" ? "/" : `/${rel}`;
|
|
9087
|
+
}
|
|
9088
|
+
return null;
|
|
9089
|
+
}
|
|
9090
|
+
function rebuildDiscoveredPagePaths(files) {
|
|
9091
|
+
const known = new Set(["/"]);
|
|
9092
|
+
for (const filePath of files) {
|
|
9093
|
+
const routePath = stxViewFileToRoutePath(filePath);
|
|
9094
|
+
if (routePath)
|
|
9095
|
+
known.add(routePath);
|
|
9096
|
+
}
|
|
9097
|
+
const pages = options.site?.pages;
|
|
9098
|
+
if (pages) {
|
|
9099
|
+
for (const p of Object.keys(pages))
|
|
9100
|
+
known.add(p.startsWith("/") ? p : `/${p}`);
|
|
9101
|
+
}
|
|
9102
|
+
discoveredPagePaths = known;
|
|
9103
|
+
}
|
|
9030
9104
|
async function discoverFiles() {
|
|
9031
9105
|
if (sourceFiles !== null)
|
|
9032
9106
|
return sourceFiles;
|
|
@@ -9062,11 +9136,12 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9062
9136
|
}
|
|
9063
9137
|
}
|
|
9064
9138
|
sourceFiles = files;
|
|
9139
|
+
rebuildDiscoveredPagePaths(files);
|
|
9065
9140
|
pageMiddlewareByPath.clear();
|
|
9066
9141
|
pageMiddlewarePatterns.length = 0;
|
|
9067
9142
|
await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
9068
9143
|
try {
|
|
9069
|
-
const { Router } = await import("./chunk-
|
|
9144
|
+
const { Router } = await import("./chunk-sm4gbwrd.js");
|
|
9070
9145
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
9071
9146
|
const router = new Router(process19.cwd(), { pagesDirs });
|
|
9072
9147
|
if (!options.quiet)
|
|
@@ -9235,10 +9310,14 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9235
9310
|
}
|
|
9236
9311
|
}
|
|
9237
9312
|
async function processTemplate(filePath) {
|
|
9238
|
-
const cachedEntry = htmlCache.get(filePath);
|
|
9239
|
-
if (cachedEntry && await templateSignatureFresh(cachedEntry.signature))
|
|
9240
|
-
return cachedEntry.html;
|
|
9241
9313
|
const content = await Bun.file(filePath).text();
|
|
9314
|
+
const skipCacheHint = /(?:^|[^\w$])__stx_skip_cache\s*=\s*true/.test(content);
|
|
9315
|
+
if (ENABLE_HTML_CACHE && !skipCacheHint) {
|
|
9316
|
+
const cacheKey = htmlCacheKey(filePath);
|
|
9317
|
+
const cachedEntry = htmlCache.get(cacheKey);
|
|
9318
|
+
if (cachedEntry && await templateSignatureFresh(cachedEntry.signature))
|
|
9319
|
+
return cachedEntry.html;
|
|
9320
|
+
}
|
|
9242
9321
|
const serverScriptRegex = /<script\b([^>]*)\bserver\b[^>]*>([\s\S]*?)<\/script>/gi;
|
|
9243
9322
|
const serverScripts = [];
|
|
9244
9323
|
let scriptMatch;
|
|
@@ -9252,6 +9331,8 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9252
9331
|
};
|
|
9253
9332
|
const { processDirectives, extractVariables, defaultConfig: defaultConfig4, generateSpaShell, injectRouterScript, resetHead } = await stxModule;
|
|
9254
9333
|
resetHead();
|
|
9334
|
+
injectServeLocaleContext(context);
|
|
9335
|
+
injectServeRequestContext(context);
|
|
9255
9336
|
for (const scriptBody of serverScripts) {
|
|
9256
9337
|
await extractVariables(scriptBody, context, filePath);
|
|
9257
9338
|
}
|
|
@@ -9284,7 +9365,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9284
9365
|
let output = templateContent;
|
|
9285
9366
|
const dependencies = new Set;
|
|
9286
9367
|
output = await processDirectives(output, context, filePath, config5, dependencies);
|
|
9287
|
-
output = await injectRouterScript(output);
|
|
9368
|
+
output = await injectRouterScript(output, getRouterInjectOptions());
|
|
9288
9369
|
const directiveTemplateRe = /@for|:for|@if|:if|x-for|x-if/;
|
|
9289
9370
|
const hasDirectiveTemplates = /<template\s[^>]*(?:@for|:for|@if|:if|x-for|x-if)/.test(output);
|
|
9290
9371
|
if (hasDirectiveTemplates) {
|
|
@@ -9298,9 +9379,9 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9298
9379
|
} else {
|
|
9299
9380
|
output = output.replace(/<template[^>]*>/gi, "").replace(/<\/template>/gi, "");
|
|
9300
9381
|
}
|
|
9301
|
-
if (!context.__stx_skip_cache) {
|
|
9382
|
+
if (ENABLE_HTML_CACHE && !context.__stx_skip_cache && !skipCacheHint) {
|
|
9302
9383
|
const signature = await buildTemplateSignature(filePath, dependencies);
|
|
9303
|
-
htmlCache.set(filePath, { html: output, signature });
|
|
9384
|
+
htmlCache.set(htmlCacheKey(filePath), { html: output, signature });
|
|
9304
9385
|
}
|
|
9305
9386
|
return output;
|
|
9306
9387
|
}
|
|
@@ -9384,25 +9465,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9384
9465
|
}
|
|
9385
9466
|
}
|
|
9386
9467
|
const fileRouteBase = relativeFilePath.replace(/\.(stx|md|html)$/, "");
|
|
9387
|
-
const
|
|
9388
|
-
const regexPatterns = [
|
|
9389
|
-
new RegExp(`^${routePattern}$`)
|
|
9390
|
-
];
|
|
9391
|
-
const fileRouteNoIndex = fileRouteBase.replace(/\/index$/, "");
|
|
9392
|
-
if (fileRouteNoIndex !== fileRouteBase) {
|
|
9393
|
-
const noIndexPattern = fileRouteNoIndex.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9394
|
-
regexPatterns.push(new RegExp(`^${noIndexPattern}$`));
|
|
9395
|
-
}
|
|
9396
|
-
if (fileRouteBase.startsWith("pages/")) {
|
|
9397
|
-
const prettyBase = fileRouteBase.slice(6);
|
|
9398
|
-
const prettyPattern = prettyBase.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9399
|
-
regexPatterns.push(new RegExp(`^${prettyPattern}$`));
|
|
9400
|
-
const prettyNoIndex = prettyBase.replace(/\/index$/, "");
|
|
9401
|
-
if (prettyNoIndex !== prettyBase) {
|
|
9402
|
-
const prettyNoIndexPattern = prettyNoIndex.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9403
|
-
regexPatterns.push(new RegExp(`^${prettyNoIndexPattern}$`));
|
|
9404
|
-
}
|
|
9405
|
-
}
|
|
9468
|
+
const regexPatterns = buildDynamicRouteRegexes(fileRouteBase);
|
|
9406
9469
|
for (const regex of regexPatterns) {
|
|
9407
9470
|
const match = normalizedPath.match(regex);
|
|
9408
9471
|
if (match) {
|
|
@@ -9435,6 +9498,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9435
9498
|
}
|
|
9436
9499
|
const { processDirectives, extractVariables, defaultConfig: defaultConfig4, injectRouterScript: injectRouter, resetHead: resetHeadDyn } = await stxModule;
|
|
9437
9500
|
resetHeadDyn();
|
|
9501
|
+
injectServeLocaleContext(context);
|
|
9438
9502
|
for (const scriptBody of dynServerScripts) {
|
|
9439
9503
|
await extractVariables(scriptBody, context, filePath);
|
|
9440
9504
|
}
|
|
@@ -9474,7 +9538,7 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9474
9538
|
${output}`;
|
|
9475
9539
|
}
|
|
9476
9540
|
}
|
|
9477
|
-
output = await injectRouter(output);
|
|
9541
|
+
output = await injectRouter(output, getRouterInjectOptions());
|
|
9478
9542
|
const directiveTemplateRe2 = /@for|:for|@if|:if|x-for|x-if/;
|
|
9479
9543
|
output = output.replace(/<template(?:\s[^>]*)?>|<\/template>/gi, (match) => {
|
|
9480
9544
|
if (directiveTemplateRe2.test(match))
|
|
@@ -9512,6 +9576,28 @@ ${output}`;
|
|
|
9512
9576
|
}
|
|
9513
9577
|
return { locale: i18nConfig.defaultLocale, path: pathname };
|
|
9514
9578
|
}
|
|
9579
|
+
function getRouterInjectOptions() {
|
|
9580
|
+
return {
|
|
9581
|
+
router: {
|
|
9582
|
+
interceptAllLinks: true,
|
|
9583
|
+
container: "main",
|
|
9584
|
+
...siteConfig?.router ?? {},
|
|
9585
|
+
...stxConfig.router ?? {}
|
|
9586
|
+
}
|
|
9587
|
+
};
|
|
9588
|
+
}
|
|
9589
|
+
function ensureRouterConfig(html) {
|
|
9590
|
+
const routerOpts = getRouterInjectOptions().router;
|
|
9591
|
+
const block = `<script>window.__stxRouterConfig=${JSON.stringify(routerOpts)};</script>
|
|
9592
|
+
`;
|
|
9593
|
+
const configTagRe = /<script>window\.__stxRouterConfig=\{[^<]*\}<\/script>\s*/i;
|
|
9594
|
+
if (configTagRe.test(html))
|
|
9595
|
+
return html.replace(configTagRe, block);
|
|
9596
|
+
if (/<\/body>/i.test(html))
|
|
9597
|
+
return html.replace(/<\/body>/i, `${block}</body>`);
|
|
9598
|
+
return `${html}
|
|
9599
|
+
${block}`;
|
|
9600
|
+
}
|
|
9515
9601
|
async function applyI18nToResponse(res, locale, normalizedPath) {
|
|
9516
9602
|
if (!siteStxPromise)
|
|
9517
9603
|
return res;
|
|
@@ -9542,8 +9628,8 @@ ${output}`;
|
|
|
9542
9628
|
html = `${groupMeta}
|
|
9543
9629
|
${html}`;
|
|
9544
9630
|
}
|
|
9545
|
-
|
|
9546
|
-
|
|
9631
|
+
const knownPaths = discoveredPagePaths ?? new Set;
|
|
9632
|
+
if (i18nConfig && locale !== i18nConfig.defaultLocale && knownPaths.size > 0) {
|
|
9547
9633
|
html = html.replace(/<a\b([^>]*?)\bhref="(\/[^"]*)"([^>]*)>/gi, (full, before, href, after) => {
|
|
9548
9634
|
if (href.startsWith("//"))
|
|
9549
9635
|
return full;
|
|
@@ -9562,6 +9648,7 @@ ${html}`;
|
|
|
9562
9648
|
}
|
|
9563
9649
|
if (i18nConfig && stx.applyTranslations)
|
|
9564
9650
|
html = stx.applyTranslations(html, i18nConfig, locale);
|
|
9651
|
+
html = ensureRouterConfig(html);
|
|
9565
9652
|
const headers = new Headers(res.headers);
|
|
9566
9653
|
headers.delete("content-length");
|
|
9567
9654
|
return new Response(html, {
|
|
@@ -9590,411 +9677,421 @@ ${html}`;
|
|
|
9590
9677
|
if (stripped.path !== url.pathname) {
|
|
9591
9678
|
const rewritten = new URL(req.url);
|
|
9592
9679
|
rewritten.pathname = stripped.path;
|
|
9593
|
-
|
|
9680
|
+
const headers = new Headers(req.headers);
|
|
9681
|
+
headers.set("x-stx-locale", stripped.locale);
|
|
9682
|
+
req = new Request(rewritten, { headers, method: req.method, body: req.body, redirect: req.redirect, duplex: "half" });
|
|
9594
9683
|
path = stripped.path;
|
|
9684
|
+
} else if (i18nLocale) {
|
|
9685
|
+
const headers = new Headers(req.headers);
|
|
9686
|
+
headers.set("x-stx-locale", i18nLocale);
|
|
9687
|
+
req = new Request(req, { headers });
|
|
9595
9688
|
}
|
|
9596
9689
|
}
|
|
9597
9690
|
const _i18nResp = await (async () => {
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
c
|
|
9691
|
+
activeServeLocale = i18nLocale;
|
|
9692
|
+
activeServeSearch = url.search;
|
|
9693
|
+
try {
|
|
9694
|
+
if (req.method === "OPTIONS") {
|
|
9695
|
+
return new Response(null, { headers: corsHeaders });
|
|
9696
|
+
}
|
|
9697
|
+
if (path === "/_stx/hmr") {
|
|
9698
|
+
let controller;
|
|
9699
|
+
let keepalive;
|
|
9700
|
+
const stream = new ReadableStream({
|
|
9701
|
+
start(c) {
|
|
9702
|
+
controller = c;
|
|
9703
|
+
hmrClients.add(c);
|
|
9704
|
+
c.enqueue(hmrEncoder.encode(`data: {"type":"connected"}
|
|
9609
9705
|
|
|
9610
9706
|
`));
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9707
|
+
keepalive = setInterval(() => {
|
|
9708
|
+
try {
|
|
9709
|
+
c.enqueue(hmrEncoder.encode(`: keepalive
|
|
9614
9710
|
|
|
9615
9711
|
`));
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
}
|
|
9635
|
-
if (options.onRequest) {
|
|
9636
|
-
const customResponse = await options.onRequest(req);
|
|
9637
|
-
if (customResponse)
|
|
9638
|
-
return customResponse;
|
|
9639
|
-
}
|
|
9640
|
-
const route = resolveRouteMiddleware(path);
|
|
9641
|
-
const extraNames = extraProtectedPrefixes.some((p) => path === p || path.startsWith(p.endsWith("/") ? p : `${p}/`)) ? ["auth"] : [];
|
|
9642
|
-
const requested = [...globalMiddlewareNames, ...route.names, ...extraNames];
|
|
9643
|
-
if (requested.length > 0) {
|
|
9644
|
-
const expanded = expandMiddlewareNames(requested);
|
|
9645
|
-
const cookies = parseCookies(req);
|
|
9646
|
-
const ctx = {
|
|
9647
|
-
path,
|
|
9648
|
-
url,
|
|
9649
|
-
params: route.params,
|
|
9650
|
-
cookies,
|
|
9651
|
-
redirect: (to, status = 302) => {
|
|
9652
|
-
const sep = to.includes("?") ? "&" : "?";
|
|
9653
|
-
const next = encodeURIComponent(path + (url.search || ""));
|
|
9654
|
-
return new Response(null, {
|
|
9655
|
-
status,
|
|
9656
|
-
headers: { Location: `${to}${sep}next=${next}` }
|
|
9657
|
-
});
|
|
9658
|
-
}
|
|
9659
|
-
};
|
|
9660
|
-
for (const entry of expanded) {
|
|
9661
|
-
const colon = entry.indexOf(":");
|
|
9662
|
-
const name = colon === -1 ? entry : entry.slice(0, colon);
|
|
9663
|
-
const args = colon === -1 ? [] : entry.slice(colon + 1).split(",");
|
|
9664
|
-
const handler = middlewareRegistry[name];
|
|
9665
|
-
if (!handler) {
|
|
9666
|
-
console.warn(`[stx serve] unknown middleware "${name}" on ${path}`);
|
|
9667
|
-
continue;
|
|
9668
|
-
}
|
|
9669
|
-
const result = await handler(req, ctx, ...args);
|
|
9670
|
-
if (result instanceof Response)
|
|
9671
|
-
return result;
|
|
9712
|
+
} catch {}
|
|
9713
|
+
}, 15000);
|
|
9714
|
+
},
|
|
9715
|
+
cancel() {
|
|
9716
|
+
if (keepalive)
|
|
9717
|
+
clearInterval(keepalive);
|
|
9718
|
+
if (controller)
|
|
9719
|
+
hmrClients.delete(controller);
|
|
9720
|
+
}
|
|
9721
|
+
});
|
|
9722
|
+
return new Response(stream, {
|
|
9723
|
+
headers: {
|
|
9724
|
+
"Content-Type": "text/event-stream",
|
|
9725
|
+
"Cache-Control": "no-cache, no-transform",
|
|
9726
|
+
Connection: "keep-alive",
|
|
9727
|
+
"X-Accel-Buffering": "no"
|
|
9728
|
+
}
|
|
9729
|
+
});
|
|
9672
9730
|
}
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
return routeHandler(req);
|
|
9731
|
+
if (options.onRequest) {
|
|
9732
|
+
const customResponse = await options.onRequest(req);
|
|
9733
|
+
if (customResponse)
|
|
9734
|
+
return customResponse;
|
|
9678
9735
|
}
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
const
|
|
9682
|
-
if (
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9736
|
+
const route = resolveRouteMiddleware(path);
|
|
9737
|
+
const extraNames = extraProtectedPrefixes.some((p) => path === p || path.startsWith(p.endsWith("/") ? p : `${p}/`)) ? ["auth"] : [];
|
|
9738
|
+
const requested = [...globalMiddlewareNames, ...route.names, ...extraNames];
|
|
9739
|
+
if (requested.length > 0) {
|
|
9740
|
+
const expanded = expandMiddlewareNames(requested);
|
|
9741
|
+
const cookies = parseCookies(req);
|
|
9742
|
+
const ctx = {
|
|
9743
|
+
path,
|
|
9744
|
+
url,
|
|
9745
|
+
params: route.params,
|
|
9746
|
+
cookies,
|
|
9747
|
+
redirect: (to, status = 302) => {
|
|
9748
|
+
const sep = to.includes("?") ? "&" : "?";
|
|
9749
|
+
const next = encodeURIComponent(path + (url.search || ""));
|
|
9750
|
+
return new Response(null, {
|
|
9751
|
+
status,
|
|
9752
|
+
headers: { Location: `${to}${sep}next=${next}` }
|
|
9753
|
+
});
|
|
9754
|
+
}
|
|
9755
|
+
};
|
|
9756
|
+
for (const entry of expanded) {
|
|
9757
|
+
const colon = entry.indexOf(":");
|
|
9758
|
+
const name = colon === -1 ? entry : entry.slice(0, colon);
|
|
9759
|
+
const args = colon === -1 ? [] : entry.slice(colon + 1).split(",");
|
|
9760
|
+
const handler = middlewareRegistry[name];
|
|
9761
|
+
if (!handler) {
|
|
9762
|
+
console.warn(`[stx serve] unknown middleware "${name}" on ${path}`);
|
|
9763
|
+
continue;
|
|
9764
|
+
}
|
|
9765
|
+
const result = await handler(req, ctx, ...args);
|
|
9766
|
+
if (result instanceof Response)
|
|
9767
|
+
return result;
|
|
9702
9768
|
}
|
|
9703
9769
|
}
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
if (!tableName) {
|
|
9709
|
-
return new Response(JSON.stringify({ error: "Table name required" }), {
|
|
9710
|
-
status: 400,
|
|
9711
|
-
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9712
|
-
});
|
|
9770
|
+
if (options.routes) {
|
|
9771
|
+
const routeHandler = options.routes[path];
|
|
9772
|
+
if (routeHandler) {
|
|
9773
|
+
return routeHandler(req);
|
|
9713
9774
|
}
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9775
|
+
}
|
|
9776
|
+
if (path.startsWith("/_stx/component/")) {
|
|
9777
|
+
const componentName = decodeURIComponent(path.slice("/_stx/component/".length));
|
|
9778
|
+
if (componentName) {
|
|
9779
|
+
try {
|
|
9780
|
+
const { processDirectives, defaultConfig: defaultConfig4 } = await stxModule;
|
|
9781
|
+
const componentTemplate = `<${componentName} />`;
|
|
9782
|
+
const componentOpts = {
|
|
9783
|
+
...defaultConfig4,
|
|
9784
|
+
...componentsDir && { componentsDir },
|
|
9785
|
+
...layoutsDir && { layoutsDir },
|
|
9786
|
+
...partialsDir && { partialsDir },
|
|
9787
|
+
autoShell: false
|
|
9788
|
+
};
|
|
9789
|
+
const html = await processDirectives(componentTemplate, {}, `${componentsDir}/${componentName}.stx`, componentOpts, new Set);
|
|
9790
|
+
return new Response(html, {
|
|
9791
|
+
headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders }
|
|
9792
|
+
});
|
|
9793
|
+
} catch (e) {
|
|
9794
|
+
return new Response(`<div class="stx-async-error">${e.message}</div>`, {
|
|
9795
|
+
status: 500,
|
|
9796
|
+
headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders }
|
|
9797
|
+
});
|
|
9798
|
+
}
|
|
9719
9799
|
}
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
const dbPath = nodePath.resolve(process19.cwd(), "database/stacks.sqlite");
|
|
9723
|
-
const db = new Database(dbPath);
|
|
9800
|
+
}
|
|
9801
|
+
if (path.startsWith("/api/data/") && req.method === "POST") {
|
|
9724
9802
|
try {
|
|
9725
|
-
const
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
if (body[col] !== undefined && body[col] !== "") {
|
|
9732
|
-
columns.push(col);
|
|
9733
|
-
placeholders.push("?");
|
|
9734
|
-
values.push(body[col]);
|
|
9735
|
-
}
|
|
9736
|
-
}
|
|
9737
|
-
const now = new Date().toISOString();
|
|
9738
|
-
if (tableInfo.some((c) => c.name === "created_at")) {
|
|
9739
|
-
columns.push("created_at");
|
|
9740
|
-
placeholders.push("?");
|
|
9741
|
-
values.push(now);
|
|
9742
|
-
}
|
|
9743
|
-
if (tableInfo.some((c) => c.name === "updated_at")) {
|
|
9744
|
-
columns.push("updated_at");
|
|
9745
|
-
placeholders.push("?");
|
|
9746
|
-
values.push(now);
|
|
9803
|
+
const tableName = path.replace("/api/data/", "").split("/")[0];
|
|
9804
|
+
if (!tableName) {
|
|
9805
|
+
return new Response(JSON.stringify({ error: "Table name required" }), {
|
|
9806
|
+
status: 400,
|
|
9807
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9808
|
+
});
|
|
9747
9809
|
}
|
|
9748
|
-
if (
|
|
9749
|
-
return new Response(JSON.stringify({ error: "
|
|
9810
|
+
if (!/^[a-zA-Z_]\w*$/.test(tableName)) {
|
|
9811
|
+
return new Response(JSON.stringify({ error: "Invalid table name" }), {
|
|
9750
9812
|
status: 400,
|
|
9751
9813
|
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9752
9814
|
});
|
|
9753
9815
|
}
|
|
9754
|
-
const
|
|
9755
|
-
const
|
|
9756
|
-
const
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
status: 500,
|
|
9771
|
-
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9772
|
-
});
|
|
9773
|
-
} finally {
|
|
9774
|
-
db.close();
|
|
9775
|
-
}
|
|
9776
|
-
} catch {}
|
|
9777
|
-
}
|
|
9778
|
-
if (path === "/index")
|
|
9779
|
-
path = "/";
|
|
9780
|
-
if (path === "/") {
|
|
9781
|
-
const indexContent = await getRoute("/");
|
|
9782
|
-
if (!indexContent) {
|
|
9783
|
-
const homeContent = await getRoute("/home");
|
|
9784
|
-
if (homeContent) {
|
|
9785
|
-
return new Response(null, {
|
|
9786
|
-
status: 302,
|
|
9787
|
-
headers: {
|
|
9788
|
-
Location: "/home",
|
|
9789
|
-
...corsHeaders
|
|
9816
|
+
const body = await req.json();
|
|
9817
|
+
const { Database } = await import("bun:sqlite");
|
|
9818
|
+
const dbPath = nodePath.resolve(process19.cwd(), "database/stacks.sqlite");
|
|
9819
|
+
const db = new Database(dbPath);
|
|
9820
|
+
try {
|
|
9821
|
+
const tableInfo = db.query(`PRAGMA table_info("${tableName}")`).all();
|
|
9822
|
+
const validColumns = tableInfo.map((c) => c.name).filter((n) => n !== "id" && n !== "created_at" && n !== "updated_at");
|
|
9823
|
+
const columns = [];
|
|
9824
|
+
const placeholders = [];
|
|
9825
|
+
const values = [];
|
|
9826
|
+
for (const col of validColumns) {
|
|
9827
|
+
if (body[col] !== undefined && body[col] !== "") {
|
|
9828
|
+
columns.push(col);
|
|
9829
|
+
placeholders.push("?");
|
|
9830
|
+
values.push(body[col]);
|
|
9831
|
+
}
|
|
9790
9832
|
}
|
|
9791
|
-
|
|
9833
|
+
const now = new Date().toISOString();
|
|
9834
|
+
if (tableInfo.some((c) => c.name === "created_at")) {
|
|
9835
|
+
columns.push("created_at");
|
|
9836
|
+
placeholders.push("?");
|
|
9837
|
+
values.push(now);
|
|
9838
|
+
}
|
|
9839
|
+
if (tableInfo.some((c) => c.name === "updated_at")) {
|
|
9840
|
+
columns.push("updated_at");
|
|
9841
|
+
placeholders.push("?");
|
|
9842
|
+
values.push(now);
|
|
9843
|
+
}
|
|
9844
|
+
if (columns.length === 0) {
|
|
9845
|
+
return new Response(JSON.stringify({ error: "No valid fields provided" }), {
|
|
9846
|
+
status: 400,
|
|
9847
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9848
|
+
});
|
|
9849
|
+
}
|
|
9850
|
+
const query = `INSERT INTO "${tableName}" (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`;
|
|
9851
|
+
const stmt = db.prepare(query);
|
|
9852
|
+
const result = stmt.run(...values);
|
|
9853
|
+
return new Response(JSON.stringify({
|
|
9854
|
+
success: true,
|
|
9855
|
+
id: result.lastInsertRowid,
|
|
9856
|
+
message: "Record created successfully"
|
|
9857
|
+
}), {
|
|
9858
|
+
status: 201,
|
|
9859
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9860
|
+
});
|
|
9861
|
+
} catch (error) {
|
|
9862
|
+
console.error("API Error:", error);
|
|
9863
|
+
return new Response(JSON.stringify({
|
|
9864
|
+
error: error.message || "Failed to create record"
|
|
9865
|
+
}), {
|
|
9866
|
+
status: 500,
|
|
9867
|
+
headers: { "Content-Type": "application/json", ...corsHeaders }
|
|
9868
|
+
});
|
|
9869
|
+
} finally {
|
|
9870
|
+
db.close();
|
|
9871
|
+
}
|
|
9872
|
+
} catch {}
|
|
9873
|
+
}
|
|
9874
|
+
if (path === "/index")
|
|
9875
|
+
path = "/";
|
|
9876
|
+
if (path === "/") {
|
|
9877
|
+
const indexContent = await getRoute("/");
|
|
9878
|
+
if (!indexContent) {
|
|
9879
|
+
const homeContent = await getRoute("/home");
|
|
9880
|
+
if (homeContent) {
|
|
9881
|
+
return new Response(null, {
|
|
9882
|
+
status: 302,
|
|
9883
|
+
headers: {
|
|
9884
|
+
Location: "/home",
|
|
9885
|
+
...corsHeaders
|
|
9886
|
+
}
|
|
9887
|
+
});
|
|
9888
|
+
}
|
|
9792
9889
|
}
|
|
9793
9890
|
}
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9891
|
+
const content = await getRoute(path);
|
|
9892
|
+
if (content) {
|
|
9893
|
+
const isSpaNav = req.headers.get("X-STX-Router") === "true";
|
|
9894
|
+
if (isSpaNav) {
|
|
9895
|
+
const layoutMatch = content.match(/<!-- stx-layout: ([^ ]+) -->/);
|
|
9896
|
+
const pageLayout = layoutMatch ? layoutMatch[1] : "default";
|
|
9897
|
+
const groupMetaMatch = content.match(/<meta\b[^>]*name=["']stx-layout-group["'][^>]*content=["']([^"']+)["'][^>]*>/i);
|
|
9898
|
+
const pageLayoutGroup = i18nConfig && i18nLocale ? `i18n:${i18nLocale}` : groupMetaMatch?.[1] || deriveLayoutGroup(pageLayout);
|
|
9899
|
+
let fragment = content;
|
|
9900
|
+
const headStyles = [];
|
|
9901
|
+
const headMatch = content.match(/<head\b[^>]*>([\s\S]*?)<\/head>/i);
|
|
9902
|
+
if (headMatch) {
|
|
9903
|
+
const headContent = headMatch[1];
|
|
9904
|
+
let styleMatch;
|
|
9905
|
+
const styleRe = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
|
|
9906
|
+
while ((styleMatch = styleRe.exec(headContent)) !== null) {
|
|
9907
|
+
if (styleMatch[0].includes("data-crosswind")) {
|
|
9908
|
+
const cssContent = styleMatch[0].replace(/<style[^>]*>/, "").replace(/<\/style>/, "");
|
|
9909
|
+
const hiddenRule = cssContent.indexOf("[hidden]");
|
|
9910
|
+
if (hiddenRule !== -1) {
|
|
9911
|
+
const afterPreflight = cssContent.indexOf("}", hiddenRule) + 1;
|
|
9912
|
+
const utilities = cssContent.slice(afterPreflight).trim();
|
|
9913
|
+
if (utilities) {
|
|
9914
|
+
headStyles.push(`<style data-crosswind="fragment">${utilities}</style>`);
|
|
9915
|
+
}
|
|
9818
9916
|
}
|
|
9917
|
+
continue;
|
|
9819
9918
|
}
|
|
9820
|
-
|
|
9919
|
+
headStyles.push(styleMatch[0]);
|
|
9821
9920
|
}
|
|
9822
|
-
headStyles.push(styleMatch[0]);
|
|
9823
9921
|
}
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9922
|
+
const mainOpenMatch = fragment.match(/<main\b[^>]*>/i);
|
|
9923
|
+
const mainCloseIdx = fragment.lastIndexOf("</main>");
|
|
9924
|
+
if (mainOpenMatch && mainCloseIdx !== -1) {
|
|
9925
|
+
const bodyMatch = content.match(/<body\b[^>]*>/i);
|
|
9926
|
+
if (bodyMatch) {
|
|
9927
|
+
const bodyStart = bodyMatch.index + bodyMatch[0].length;
|
|
9928
|
+
const mainIdx = mainOpenMatch.index;
|
|
9929
|
+
const beforeMain = content.slice(bodyStart, mainIdx);
|
|
9930
|
+
let bodyStyleMatch;
|
|
9931
|
+
const bodyStyleRe = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
|
|
9932
|
+
while ((bodyStyleMatch = bodyStyleRe.exec(beforeMain)) !== null) {
|
|
9933
|
+
headStyles.push(bodyStyleMatch[0]);
|
|
9934
|
+
}
|
|
9837
9935
|
}
|
|
9936
|
+
const mainStart = mainOpenMatch.index + mainOpenMatch[0].length;
|
|
9937
|
+
fragment = fragment.slice(mainStart, mainCloseIdx).trim();
|
|
9838
9938
|
}
|
|
9839
|
-
const
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
fragment = fragment.replace(/<script data-stx-scoped>\s*;?\(function\(\)\s*\{[\s\S]*?<\/script>/g, "");
|
|
9858
|
-
const clearStale = "<script data-stx-page>if(window.stx)window.stx._latestSetup=null;</script>";
|
|
9859
|
-
fragment = `${headStyles.join(`
|
|
9939
|
+
const pageSetupScripts = [];
|
|
9940
|
+
const allScriptRe = /<script\b[^>]*data-stx-scoped[^>]*>[\s\S]*?<\/script>/gi;
|
|
9941
|
+
let setupMatch;
|
|
9942
|
+
while ((setupMatch = allScriptRe.exec(content)) !== null) {
|
|
9943
|
+
const scriptContent = setupMatch[0];
|
|
9944
|
+
if (scriptContent.includes("__stx_early_mounts"))
|
|
9945
|
+
continue;
|
|
9946
|
+
if (scriptContent.includes("data-stx-reactive") && scriptContent.includes("window.__stx_reactive"))
|
|
9947
|
+
continue;
|
|
9948
|
+
pageSetupScripts.push(scriptContent);
|
|
9949
|
+
}
|
|
9950
|
+
const bridgeInitRe = /<script\b[^>]*data-stx-reactive[^>]*>(?![\s\S]*window\.__stx_reactive)[\s\S]*?<\/script>/gi;
|
|
9951
|
+
while ((setupMatch = bridgeInitRe.exec(content)) !== null) {
|
|
9952
|
+
pageSetupScripts.push(setupMatch[0]);
|
|
9953
|
+
}
|
|
9954
|
+
fragment = fragment.replace(/<script data-stx-scoped>\s*;?\(function\(\)\s*\{[\s\S]*?<\/script>/g, "");
|
|
9955
|
+
const clearStale = "<script data-stx-page>if(window.stx)window.stx._latestSetup=null;</script>";
|
|
9956
|
+
fragment = `${headStyles.join(`
|
|
9860
9957
|
`)}
|
|
9861
9958
|
${fragment}
|
|
9862
9959
|
${clearStale}
|
|
9863
9960
|
${pageSetupScripts.join(`
|
|
9864
9961
|
`)}`;
|
|
9865
|
-
|
|
9962
|
+
return new Response(fragment, {
|
|
9963
|
+
headers: {
|
|
9964
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
9965
|
+
"X-STX-Fragment": "true",
|
|
9966
|
+
"X-STX-Layout": pageLayout,
|
|
9967
|
+
"X-STX-Layout-Group": pageLayoutGroup,
|
|
9968
|
+
"Cache-Control": "no-store",
|
|
9969
|
+
...corsHeaders
|
|
9970
|
+
}
|
|
9971
|
+
});
|
|
9972
|
+
}
|
|
9973
|
+
let cleaned = content;
|
|
9974
|
+
let runtimeCount = 0;
|
|
9975
|
+
cleaned = content.replace(/<script data-stx-scoped>\(function\(\)\{'use strict';var cloakStyle[\s\S]*?<\/script>/g, (match) => {
|
|
9976
|
+
runtimeCount++;
|
|
9977
|
+
return runtimeCount === 1 ? match : "";
|
|
9978
|
+
});
|
|
9979
|
+
return new Response(injectHmrClient(cleaned), {
|
|
9866
9980
|
headers: {
|
|
9867
9981
|
"Content-Type": "text/html; charset=utf-8",
|
|
9868
|
-
"
|
|
9869
|
-
"X-STX-Layout": pageLayout,
|
|
9870
|
-
"X-STX-Layout-Group": pageLayoutGroup,
|
|
9871
|
-
"Cache-Control": "no-cache",
|
|
9982
|
+
"Cache-Control": "no-store",
|
|
9872
9983
|
...corsHeaders
|
|
9873
9984
|
}
|
|
9874
9985
|
});
|
|
9875
9986
|
}
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
"Cache-Control": "no-cache",
|
|
9886
|
-
...corsHeaders
|
|
9887
|
-
}
|
|
9888
|
-
});
|
|
9889
|
-
}
|
|
9890
|
-
const contentWithExt = await getRoute(`${path}.html`);
|
|
9891
|
-
if (contentWithExt) {
|
|
9892
|
-
return new Response(injectHmrClient(contentWithExt), {
|
|
9893
|
-
headers: {
|
|
9894
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
9895
|
-
"Cache-Control": "no-cache",
|
|
9896
|
-
...corsHeaders
|
|
9897
|
-
}
|
|
9898
|
-
});
|
|
9899
|
-
}
|
|
9900
|
-
if (path.startsWith("/chunk-") || path.endsWith(".js") || path.endsWith(".css")) {
|
|
9901
|
-
try {
|
|
9902
|
-
const buildFile = Bun.file(`.stx${path}`);
|
|
9903
|
-
if (await buildFile.exists()) {
|
|
9904
|
-
const ext = path.split(".").pop()?.toLowerCase();
|
|
9905
|
-
const contentType = ext === "css" ? "text/css" : ext === "js" ? "application/javascript" : "text/plain";
|
|
9906
|
-
return new Response(buildFile, {
|
|
9907
|
-
headers: {
|
|
9908
|
-
"Content-Type": contentType,
|
|
9909
|
-
"Cache-Control": "no-cache"
|
|
9910
|
-
}
|
|
9911
|
-
});
|
|
9912
|
-
}
|
|
9913
|
-
} catch {}
|
|
9914
|
-
}
|
|
9915
|
-
if (path.startsWith("/assets/") || path.startsWith("/resources/assets/")) {
|
|
9916
|
-
await ensureAssets();
|
|
9917
|
-
let assetPathname;
|
|
9918
|
-
try {
|
|
9919
|
-
assetPathname = decodeURIComponent(path);
|
|
9920
|
-
} catch {
|
|
9921
|
-
return new Response("Invalid asset path", { status: 400 });
|
|
9987
|
+
const contentWithExt = await getRoute(`${path}.html`);
|
|
9988
|
+
if (contentWithExt) {
|
|
9989
|
+
return new Response(injectHmrClient(contentWithExt), {
|
|
9990
|
+
headers: {
|
|
9991
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
9992
|
+
"Cache-Control": "no-store",
|
|
9993
|
+
...corsHeaders
|
|
9994
|
+
}
|
|
9995
|
+
});
|
|
9922
9996
|
}
|
|
9923
|
-
if (
|
|
9924
|
-
return new Response("Invalid asset path", { status: 400 });
|
|
9925
|
-
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
9997
|
+
if (path.startsWith("/chunk-") || path.endsWith(".js") || path.endsWith(".css")) {
|
|
9926
9998
|
try {
|
|
9927
|
-
const
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
const
|
|
9931
|
-
|
|
9932
|
-
return bundleBrowserAsset(filePath);
|
|
9933
|
-
return new Response(file, {
|
|
9999
|
+
const buildFile = Bun.file(`.stx${path}`);
|
|
10000
|
+
if (await buildFile.exists()) {
|
|
10001
|
+
const ext = path.split(".").pop()?.toLowerCase();
|
|
10002
|
+
const contentType = ext === "css" ? "text/css" : ext === "js" ? "application/javascript" : "text/plain";
|
|
10003
|
+
return new Response(buildFile, {
|
|
9934
10004
|
headers: {
|
|
9935
|
-
"Content-Type":
|
|
9936
|
-
"Cache-Control": "no-
|
|
10005
|
+
"Content-Type": contentType,
|
|
10006
|
+
"Cache-Control": "no-store"
|
|
9937
10007
|
}
|
|
9938
10008
|
});
|
|
9939
10009
|
}
|
|
10010
|
+
} catch {}
|
|
10011
|
+
}
|
|
10012
|
+
if (path.startsWith("/assets/") || path.startsWith("/resources/assets/")) {
|
|
10013
|
+
await ensureAssets();
|
|
10014
|
+
let assetPathname;
|
|
10015
|
+
try {
|
|
10016
|
+
assetPathname = decodeURIComponent(path);
|
|
9940
10017
|
} catch {
|
|
9941
|
-
|
|
10018
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
9942
10019
|
}
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
|
|
9946
|
-
const publicRoot = nodePath.resolve(process19.cwd(), publicDir);
|
|
9947
|
-
let safePathname;
|
|
9948
|
-
try {
|
|
9949
|
-
safePathname = decodeURIComponent(path);
|
|
9950
|
-
} catch {
|
|
9951
|
-
safePathname = path;
|
|
9952
|
-
}
|
|
9953
|
-
if (!safePathname.includes("\x00")) {
|
|
9954
|
-
const resolvedPath = nodePath.resolve(publicRoot, `.${safePathname}`);
|
|
9955
|
-
const isInsidePublicRoot = resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${nodePath.sep}`);
|
|
9956
|
-
if (isInsidePublicRoot) {
|
|
10020
|
+
if (!isSafeAssetPath(assetPathname))
|
|
10021
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
10022
|
+
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
9957
10023
|
try {
|
|
9958
|
-
const
|
|
10024
|
+
const filePath = nodePath.resolve(process19.cwd(), `.${assetPath}`);
|
|
10025
|
+
const file = Bun.file(filePath);
|
|
9959
10026
|
if (await file.exists()) {
|
|
9960
|
-
const
|
|
9961
|
-
if (
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
}
|
|
10027
|
+
const ext = getAssetExtension(assetPath);
|
|
10028
|
+
if (isBundledAssetExtension(ext))
|
|
10029
|
+
return bundleBrowserAsset(filePath);
|
|
10030
|
+
return new Response(file, {
|
|
10031
|
+
headers: {
|
|
10032
|
+
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
10033
|
+
"Cache-Control": "no-store"
|
|
10034
|
+
}
|
|
10035
|
+
});
|
|
9970
10036
|
}
|
|
9971
|
-
} catch {
|
|
10037
|
+
} catch {
|
|
10038
|
+
continue;
|
|
10039
|
+
}
|
|
9972
10040
|
}
|
|
9973
10041
|
}
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
10042
|
+
if ((req.method === "GET" || req.method === "HEAD") && path !== "/") {
|
|
10043
|
+
const publicRoot = nodePath.resolve(process19.cwd(), publicDir);
|
|
10044
|
+
let safePathname;
|
|
10045
|
+
try {
|
|
10046
|
+
safePathname = decodeURIComponent(path);
|
|
10047
|
+
} catch {
|
|
10048
|
+
safePathname = path;
|
|
10049
|
+
}
|
|
10050
|
+
if (!safePathname.includes("\x00")) {
|
|
10051
|
+
const resolvedPath = nodePath.resolve(publicRoot, `.${safePathname}`);
|
|
10052
|
+
const isInsidePublicRoot = resolvedPath === publicRoot || resolvedPath.startsWith(`${publicRoot}${nodePath.sep}`);
|
|
10053
|
+
if (isInsidePublicRoot) {
|
|
10054
|
+
try {
|
|
10055
|
+
const file = Bun.file(resolvedPath);
|
|
10056
|
+
if (await file.exists()) {
|
|
10057
|
+
const stat4 = await file.stat().catch(() => null);
|
|
10058
|
+
if (stat4 && !stat4.isDirectory()) {
|
|
10059
|
+
const ext = resolvedPath.split(".").pop()?.toLowerCase();
|
|
10060
|
+
return new Response(file, {
|
|
10061
|
+
headers: {
|
|
10062
|
+
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
10063
|
+
"Cache-Control": "no-store"
|
|
10064
|
+
}
|
|
10065
|
+
});
|
|
10066
|
+
}
|
|
10067
|
+
}
|
|
10068
|
+
} catch {}
|
|
10069
|
+
}
|
|
9987
10070
|
}
|
|
9988
10071
|
}
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
route2 = route2.slice(6);
|
|
10072
|
+
if (path === "/favicon.ico") {
|
|
10073
|
+
return new Response(null, { status: 204 });
|
|
9992
10074
|
}
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
10075
|
+
const files = await discoverFiles();
|
|
10076
|
+
const availableRoutes = [];
|
|
10077
|
+
for (const filePath of files) {
|
|
10078
|
+
let normalizedPath = filePath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
10079
|
+
for (const pattern of patterns) {
|
|
10080
|
+
const normalizedPattern = pattern.replace(/\\/g, "/").replace(/\/$/, "");
|
|
10081
|
+
if (normalizedPath.startsWith(`${normalizedPattern}/`)) {
|
|
10082
|
+
normalizedPath = normalizedPath.slice(normalizedPattern.length + 1);
|
|
10083
|
+
break;
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
let route2 = normalizedPath.replace(/\.(stx|md|html)$/, "");
|
|
10087
|
+
if (route2.startsWith("pages/")) {
|
|
10088
|
+
route2 = route2.slice(6);
|
|
10089
|
+
}
|
|
10090
|
+
availableRoutes.push(`<li><a href="/${route2}">/${route2}</a></li>`);
|
|
10091
|
+
}
|
|
10092
|
+
const routesList = availableRoutes.join(`
|
|
9996
10093
|
`);
|
|
9997
|
-
|
|
10094
|
+
return new Response(injectHmrClient(`
|
|
9998
10095
|
<!DOCTYPE html>
|
|
9999
10096
|
<html>
|
|
10000
10097
|
<head>
|
|
@@ -10022,9 +10119,13 @@ ${pageSetupScripts.join(`
|
|
|
10022
10119
|
</body>
|
|
10023
10120
|
</html>
|
|
10024
10121
|
`), {
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10122
|
+
status: 404,
|
|
10123
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
10124
|
+
});
|
|
10125
|
+
} finally {
|
|
10126
|
+
activeServeLocale = null;
|
|
10127
|
+
activeServeSearch = "";
|
|
10128
|
+
}
|
|
10028
10129
|
})();
|
|
10029
10130
|
return applyI18nToResponse(_i18nResp, i18nLocale ?? (i18nConfig?.defaultLocale ?? "en"), path);
|
|
10030
10131
|
}
|
|
@@ -10098,5 +10199,6 @@ if (import.meta.main) {
|
|
|
10098
10199
|
}
|
|
10099
10200
|
export {
|
|
10100
10201
|
serve,
|
|
10101
|
-
bundleBrowserAsset
|
|
10202
|
+
bundleBrowserAsset,
|
|
10203
|
+
buildDynamicRouteRegexes
|
|
10102
10204
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-plugin-stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.69",
|
|
5
5
|
"description": "A Bun plugin that allows for using Laravel Blade-like syntax.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"typecheck": "bun tsc --noEmit"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@stacksjs/stx": "0.2.
|
|
77
|
-
"stx-router": "0.2.
|
|
76
|
+
"@stacksjs/stx": "0.2.69",
|
|
77
|
+
"stx-router": "0.2.69"
|
|
78
78
|
}
|
|
79
79
|
}
|