bun-plugin-stx 0.2.65 → 0.2.67
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-ap5bhr00.js → chunk-wv4bcqba.js} +12 -8
- package/dist/serve.d.ts +1 -0
- package/dist/serve.js +173 -38
- package/package.json +3 -3
|
@@ -913,7 +913,18 @@ else {
|
|
|
913
913
|
// re-execute because each page has its own setup.
|
|
914
914
|
var h=hashScript(code);
|
|
915
915
|
var isSetup=code.indexOf('__stx_setup_')!==-1;
|
|
916
|
-
|
|
916
|
+
// Compute isAlreadyScoped first so the dedup check can exempt
|
|
917
|
+
// self-contained IIFE scripts. data-stx-scoped scope IIFEs
|
|
918
|
+
// (the (function(){ ... })() shape) must re-execute on every
|
|
919
|
+
// navigation. They own their own scope and won't collide
|
|
920
|
+
// with prior runs, AND _cleanupContainer deleted their
|
|
921
|
+
// entry from window.stx._scopes on the way out. Without
|
|
922
|
+
// this exemption, navigating away and back leaves the page
|
|
923
|
+
// leaf with no scope registered: bindIf/@event never re-bind
|
|
924
|
+
// and all :if branches stay visible at once.
|
|
925
|
+
// Mirrors the full-doc swap path's runAlways escape below.
|
|
926
|
+
var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;
|
|
927
|
+
if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){
|
|
917
928
|
log('[router] skipping already-executed script (hash dedup)');
|
|
918
929
|
return;
|
|
919
930
|
}
|
|
@@ -923,13 +934,6 @@ else {
|
|
|
923
934
|
if(hasImport){
|
|
924
935
|
ns.type='module';
|
|
925
936
|
}
|
|
926
|
-
// Wrap in block scope to prevent const/let collisions on re-navigation.
|
|
927
|
-
// Without this, navigating away and back throws "Identifier has already
|
|
928
|
-
// been declared" because the previous page's top-level const/let are
|
|
929
|
-
// still in the global scope. Skip the wrap for module scripts \u2014
|
|
930
|
-
// they get their own scope from ESM, and top-level 'import' is
|
|
931
|
-
// illegal inside a block, which would throw SyntaxError.
|
|
932
|
-
var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;
|
|
933
937
|
ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
|
|
934
938
|
ns.setAttribute('data-stx-page','');
|
|
935
939
|
document.body.appendChild(ns);
|
package/dist/serve.d.ts
CHANGED
package/dist/serve.js
CHANGED
|
@@ -8730,11 +8730,61 @@ var staticContentTypes = {
|
|
|
8730
8730
|
otf: "font/otf",
|
|
8731
8731
|
eot: "application/vnd.ms-fontobject"
|
|
8732
8732
|
};
|
|
8733
|
+
var bundledAssetExtensions = new Set(["ts", "tsx", "mts", "cts"]);
|
|
8734
|
+
function isBundledAssetExtension(ext) {
|
|
8735
|
+
return Boolean(ext && bundledAssetExtensions.has(ext));
|
|
8736
|
+
}
|
|
8737
|
+
function getAssetExtension(pathname) {
|
|
8738
|
+
return pathname.split(".").pop()?.toLowerCase();
|
|
8739
|
+
}
|
|
8740
|
+
function isSafeAssetPath(pathname) {
|
|
8741
|
+
if (pathname.includes("\x00"))
|
|
8742
|
+
return false;
|
|
8743
|
+
return !pathname.split("/").some((segment) => segment === "..");
|
|
8744
|
+
}
|
|
8745
|
+
function assetRequestPaths(pathname) {
|
|
8746
|
+
const paths = [
|
|
8747
|
+
pathname,
|
|
8748
|
+
pathname.replace(/^\/assets\//, "/resources/assets/"),
|
|
8749
|
+
pathname.replace(/^\/resources\/assets\//, "/assets/")
|
|
8750
|
+
];
|
|
8751
|
+
return [...new Set(paths)];
|
|
8752
|
+
}
|
|
8753
|
+
async function bundleBrowserAsset(entrypoint) {
|
|
8754
|
+
const result = await Bun.build({
|
|
8755
|
+
entrypoints: [entrypoint],
|
|
8756
|
+
format: "esm",
|
|
8757
|
+
minify: false,
|
|
8758
|
+
packages: "bundle",
|
|
8759
|
+
sourcemap: "inline",
|
|
8760
|
+
target: "browser"
|
|
8761
|
+
});
|
|
8762
|
+
if (!result.success) {
|
|
8763
|
+
const message = result.logs.map((log4) => log4.message).join(`
|
|
8764
|
+
`) || "Unable to build TypeScript asset";
|
|
8765
|
+
return new Response(message, {
|
|
8766
|
+
status: 500,
|
|
8767
|
+
headers: {
|
|
8768
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
8769
|
+
"Cache-Control": "no-cache"
|
|
8770
|
+
}
|
|
8771
|
+
});
|
|
8772
|
+
}
|
|
8773
|
+
return new Response(await result.outputs[0].text(), {
|
|
8774
|
+
headers: {
|
|
8775
|
+
"Content-Type": "application/javascript",
|
|
8776
|
+
"Cache-Control": "no-cache"
|
|
8777
|
+
}
|
|
8778
|
+
});
|
|
8779
|
+
}
|
|
8733
8780
|
async function serve(options) {
|
|
8734
8781
|
const stxConfig = await loadConfig6({
|
|
8735
8782
|
name: "stx",
|
|
8783
|
+
alias: ["ui"],
|
|
8736
8784
|
cwd: process19.cwd(),
|
|
8737
|
-
defaultConfig: defaultStxConfig
|
|
8785
|
+
defaultConfig: defaultStxConfig,
|
|
8786
|
+
checkEnv: false,
|
|
8787
|
+
verbose: false
|
|
8738
8788
|
});
|
|
8739
8789
|
try {
|
|
8740
8790
|
const stxMod = options.stxModule ? options.stxModule : await defaultStxModule;
|
|
@@ -8770,6 +8820,32 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8770
8820
|
const routes = new Map;
|
|
8771
8821
|
let sourceFiles = null;
|
|
8772
8822
|
let assetsInitialized = false;
|
|
8823
|
+
const htmlCache = new Map;
|
|
8824
|
+
const crosswindCssCache = new Map;
|
|
8825
|
+
const hmrClients = new Set;
|
|
8826
|
+
const hmrEncoder = new TextEncoder;
|
|
8827
|
+
function broadcastHmr(event) {
|
|
8828
|
+
const payload = hmrEncoder.encode(`data: ${JSON.stringify(event)}
|
|
8829
|
+
|
|
8830
|
+
`);
|
|
8831
|
+
for (const c of hmrClients) {
|
|
8832
|
+
try {
|
|
8833
|
+
c.enqueue(payload);
|
|
8834
|
+
} catch {
|
|
8835
|
+
hmrClients.delete(c);
|
|
8836
|
+
}
|
|
8837
|
+
}
|
|
8838
|
+
}
|
|
8839
|
+
const HMR_CLIENT_SCRIPT = `<script data-stx-hmr>(()=>{if(window.__stxHmr)return;window.__stxHmr=1;function bust(){var ls=document.querySelectorAll('link[rel="stylesheet"]');for(var i=0;i<ls.length;i++){var l=ls[i];var u=new URL(l.href,location.href);u.searchParams.set('v',Date.now().toString(36));l.href=u.toString()}}var es=new EventSource('/_stx/hmr');es.onmessage=function(e){try{var m=JSON.parse(e.data);if(m.type==='reload')location.reload();else if(m.type==='css')bust()}catch(_){}};es.onerror=function(){if(es.readyState===2){setTimeout(function(){location.reload()},400)}}})()</script>`;
|
|
8840
|
+
function injectHmrClient(html) {
|
|
8841
|
+
if (!html)
|
|
8842
|
+
return html;
|
|
8843
|
+
const closeBody = html.lastIndexOf("</body>");
|
|
8844
|
+
if (closeBody === -1) {
|
|
8845
|
+
return html + HMR_CLIENT_SCRIPT;
|
|
8846
|
+
}
|
|
8847
|
+
return html.slice(0, closeBody) + HMR_CLIENT_SCRIPT + html.slice(closeBody);
|
|
8848
|
+
}
|
|
8773
8849
|
const watchersStarted = new Set;
|
|
8774
8850
|
const startWatcher = (dir) => {
|
|
8775
8851
|
if (watchersStarted.has(dir))
|
|
@@ -8779,15 +8855,34 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8779
8855
|
if (!filename)
|
|
8780
8856
|
return;
|
|
8781
8857
|
const f = String(filename);
|
|
8782
|
-
|
|
8858
|
+
const isStxLike = f.endsWith(".stx") || f.endsWith(".md") || f.endsWith(".html");
|
|
8859
|
+
const isCss = f.endsWith(".css");
|
|
8860
|
+
const isAsset = isCss || f.endsWith(".js") || f.endsWith(".ts") || f.endsWith(".tsx") || f.endsWith(".mts") || f.endsWith(".cts") || f.endsWith(".jpg") || f.endsWith(".jpeg") || f.endsWith(".png") || f.endsWith(".gif") || f.endsWith(".svg") || f.endsWith(".webp") || f.endsWith(".avif") || f.endsWith(".woff") || f.endsWith(".woff2") || f.endsWith(".ttf") || f.endsWith(".otf");
|
|
8861
|
+
if (!isStxLike && !isAsset)
|
|
8783
8862
|
return;
|
|
8784
|
-
|
|
8785
|
-
|
|
8863
|
+
if (isStxLike) {
|
|
8864
|
+
sourceFiles = null;
|
|
8865
|
+
routes.clear();
|
|
8866
|
+
htmlCache.clear();
|
|
8867
|
+
crosswindCssCache.clear();
|
|
8868
|
+
}
|
|
8869
|
+
broadcastHmr(isCss && !isStxLike ? { type: "css", file: f } : { type: "reload", file: f });
|
|
8786
8870
|
});
|
|
8787
8871
|
watcher.on("error", () => {});
|
|
8788
8872
|
watchersStarted.add(dir);
|
|
8789
8873
|
} catch {}
|
|
8790
8874
|
};
|
|
8875
|
+
for (const dir of [
|
|
8876
|
+
componentsDir,
|
|
8877
|
+
layoutsDir,
|
|
8878
|
+
partialsDir,
|
|
8879
|
+
publicDir,
|
|
8880
|
+
"resources/assets"
|
|
8881
|
+
]) {
|
|
8882
|
+
if (!dir)
|
|
8883
|
+
continue;
|
|
8884
|
+
startWatcher(dir);
|
|
8885
|
+
}
|
|
8791
8886
|
const pageMiddlewareByPath = new Map;
|
|
8792
8887
|
const pageMiddlewarePatterns = [];
|
|
8793
8888
|
function compileRoutePattern(urlPath) {
|
|
@@ -8971,10 +9066,11 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8971
9066
|
pageMiddlewarePatterns.length = 0;
|
|
8972
9067
|
await Promise.all(files.map((f) => detectPageMiddleware(f)));
|
|
8973
9068
|
try {
|
|
8974
|
-
const { Router } = await import("./chunk-
|
|
9069
|
+
const { Router } = await import("./chunk-wv4bcqba.js");
|
|
8975
9070
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
8976
9071
|
const router = new Router(process19.cwd(), { pagesDirs });
|
|
8977
|
-
|
|
9072
|
+
if (!options.quiet)
|
|
9073
|
+
console.log(`[stx] Generated ${router.routes.length} routes \u2192 .stx/routes.ts`);
|
|
8978
9074
|
} catch (e) {}
|
|
8979
9075
|
return files;
|
|
8980
9076
|
}
|
|
@@ -8992,7 +9088,6 @@ After installing: bun add bun-plugin-stx`);
|
|
|
8992
9088
|
}
|
|
8993
9089
|
} catch {}
|
|
8994
9090
|
}
|
|
8995
|
-
const htmlCache = new Map;
|
|
8996
9091
|
async function templateSignatureFresh(sig) {
|
|
8997
9092
|
for (const [p, expected] of sig) {
|
|
8998
9093
|
const stat4 = await nodeFs.stat(p).catch(() => null);
|
|
@@ -9015,7 +9110,6 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9015
9110
|
let crosswindModule = null;
|
|
9016
9111
|
let crosswindLoadAttempted = false;
|
|
9017
9112
|
let crosswindUserConfigPromise = null;
|
|
9018
|
-
const crosswindCssCache = new Map;
|
|
9019
9113
|
async function loadCrosswind() {
|
|
9020
9114
|
if (crosswindLoadAttempted)
|
|
9021
9115
|
return crosswindModule;
|
|
@@ -9071,7 +9165,9 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9071
9165
|
crosswindUserConfigPromise = loadConfig6({
|
|
9072
9166
|
name: "crosswind",
|
|
9073
9167
|
cwd: process19.cwd(),
|
|
9074
|
-
defaultConfig: {}
|
|
9168
|
+
defaultConfig: {},
|
|
9169
|
+
checkEnv: false,
|
|
9170
|
+
verbose: false
|
|
9075
9171
|
});
|
|
9076
9172
|
}
|
|
9077
9173
|
const userConfig = await crosswindUserConfigPromise;
|
|
@@ -9292,9 +9388,20 @@ After installing: bun add bun-plugin-stx`);
|
|
|
9292
9388
|
const regexPatterns = [
|
|
9293
9389
|
new RegExp(`^${routePattern}$`)
|
|
9294
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
|
+
}
|
|
9295
9396
|
if (fileRouteBase.startsWith("pages/")) {
|
|
9296
|
-
const
|
|
9397
|
+
const prettyBase = fileRouteBase.slice(6);
|
|
9398
|
+
const prettyPattern = prettyBase.replace(/\[([^\]]+)\]/g, "([^/]+)").replace(/\//g, "\\/");
|
|
9297
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
|
+
}
|
|
9298
9405
|
}
|
|
9299
9406
|
for (const regex of regexPatterns) {
|
|
9300
9407
|
const match = normalizedPath.match(regex);
|
|
@@ -9472,6 +9579,7 @@ ${html}`;
|
|
|
9472
9579
|
try {
|
|
9473
9580
|
_server = bunServe({
|
|
9474
9581
|
port: actualPort,
|
|
9582
|
+
idleTimeout: 0,
|
|
9475
9583
|
async fetch(req) {
|
|
9476
9584
|
const url = new URL(req.url);
|
|
9477
9585
|
let path = url.pathname;
|
|
@@ -9490,6 +9598,40 @@ ${html}`;
|
|
|
9490
9598
|
if (req.method === "OPTIONS") {
|
|
9491
9599
|
return new Response(null, { headers: corsHeaders });
|
|
9492
9600
|
}
|
|
9601
|
+
if (path === "/_stx/hmr") {
|
|
9602
|
+
let controller;
|
|
9603
|
+
let keepalive;
|
|
9604
|
+
const stream = new ReadableStream({
|
|
9605
|
+
start(c) {
|
|
9606
|
+
controller = c;
|
|
9607
|
+
hmrClients.add(c);
|
|
9608
|
+
c.enqueue(hmrEncoder.encode(`data: {"type":"connected"}
|
|
9609
|
+
|
|
9610
|
+
`));
|
|
9611
|
+
keepalive = setInterval(() => {
|
|
9612
|
+
try {
|
|
9613
|
+
c.enqueue(hmrEncoder.encode(`: keepalive
|
|
9614
|
+
|
|
9615
|
+
`));
|
|
9616
|
+
} catch {}
|
|
9617
|
+
}, 15000);
|
|
9618
|
+
},
|
|
9619
|
+
cancel() {
|
|
9620
|
+
if (keepalive)
|
|
9621
|
+
clearInterval(keepalive);
|
|
9622
|
+
if (controller)
|
|
9623
|
+
hmrClients.delete(controller);
|
|
9624
|
+
}
|
|
9625
|
+
});
|
|
9626
|
+
return new Response(stream, {
|
|
9627
|
+
headers: {
|
|
9628
|
+
"Content-Type": "text/event-stream",
|
|
9629
|
+
"Cache-Control": "no-cache, no-transform",
|
|
9630
|
+
Connection: "keep-alive",
|
|
9631
|
+
"X-Accel-Buffering": "no"
|
|
9632
|
+
}
|
|
9633
|
+
});
|
|
9634
|
+
}
|
|
9493
9635
|
if (options.onRequest) {
|
|
9494
9636
|
const customResponse = await options.onRequest(req);
|
|
9495
9637
|
if (customResponse)
|
|
@@ -9737,7 +9879,7 @@ ${pageSetupScripts.join(`
|
|
|
9737
9879
|
runtimeCount++;
|
|
9738
9880
|
return runtimeCount === 1 ? match : "";
|
|
9739
9881
|
});
|
|
9740
|
-
return new Response(cleaned, {
|
|
9882
|
+
return new Response(injectHmrClient(cleaned), {
|
|
9741
9883
|
headers: {
|
|
9742
9884
|
"Content-Type": "text/html; charset=utf-8",
|
|
9743
9885
|
"Cache-Control": "no-cache",
|
|
@@ -9747,7 +9889,7 @@ ${pageSetupScripts.join(`
|
|
|
9747
9889
|
}
|
|
9748
9890
|
const contentWithExt = await getRoute(`${path}.html`);
|
|
9749
9891
|
if (contentWithExt) {
|
|
9750
|
-
return new Response(contentWithExt, {
|
|
9892
|
+
return new Response(injectHmrClient(contentWithExt), {
|
|
9751
9893
|
headers: {
|
|
9752
9894
|
"Content-Type": "text/html; charset=utf-8",
|
|
9753
9895
|
"Cache-Control": "no-cache",
|
|
@@ -9772,34 +9914,26 @@ ${pageSetupScripts.join(`
|
|
|
9772
9914
|
}
|
|
9773
9915
|
if (path.startsWith("/assets/") || path.startsWith("/resources/assets/")) {
|
|
9774
9916
|
await ensureAssets();
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
path
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9917
|
+
let assetPathname;
|
|
9918
|
+
try {
|
|
9919
|
+
assetPathname = decodeURIComponent(path);
|
|
9920
|
+
} catch {
|
|
9921
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
9922
|
+
}
|
|
9923
|
+
if (!isSafeAssetPath(assetPathname))
|
|
9924
|
+
return new Response("Invalid asset path", { status: 400 });
|
|
9925
|
+
for (const assetPath of assetRequestPaths(assetPathname)) {
|
|
9781
9926
|
try {
|
|
9782
|
-
const filePath = `.${assetPath}
|
|
9927
|
+
const filePath = nodePath.resolve(process19.cwd(), `.${assetPath}`);
|
|
9783
9928
|
const file = Bun.file(filePath);
|
|
9784
9929
|
if (await file.exists()) {
|
|
9785
|
-
const ext = assetPath
|
|
9786
|
-
if (ext
|
|
9787
|
-
|
|
9788
|
-
loader: "ts"
|
|
9789
|
-
});
|
|
9790
|
-
const code = await file.text();
|
|
9791
|
-
const transpiled = transpiler.transformSync(code);
|
|
9792
|
-
return new Response(transpiled, {
|
|
9793
|
-
headers: {
|
|
9794
|
-
"Content-Type": "application/javascript",
|
|
9795
|
-
"Cache-Control": "no-cache"
|
|
9796
|
-
}
|
|
9797
|
-
});
|
|
9798
|
-
}
|
|
9930
|
+
const ext = getAssetExtension(assetPath);
|
|
9931
|
+
if (isBundledAssetExtension(ext))
|
|
9932
|
+
return bundleBrowserAsset(filePath);
|
|
9799
9933
|
return new Response(file, {
|
|
9800
9934
|
headers: {
|
|
9801
9935
|
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
9802
|
-
"Cache-Control": "
|
|
9936
|
+
"Cache-Control": "no-cache"
|
|
9803
9937
|
}
|
|
9804
9938
|
});
|
|
9805
9939
|
}
|
|
@@ -9829,7 +9963,7 @@ ${pageSetupScripts.join(`
|
|
|
9829
9963
|
return new Response(file, {
|
|
9830
9964
|
headers: {
|
|
9831
9965
|
"Content-Type": staticContentTypes[ext || ""] || "application/octet-stream",
|
|
9832
|
-
"Cache-Control": "
|
|
9966
|
+
"Cache-Control": "no-cache"
|
|
9833
9967
|
}
|
|
9834
9968
|
});
|
|
9835
9969
|
}
|
|
@@ -9860,7 +9994,7 @@ ${pageSetupScripts.join(`
|
|
|
9860
9994
|
}
|
|
9861
9995
|
const routesList = availableRoutes.join(`
|
|
9862
9996
|
`);
|
|
9863
|
-
return new Response(`
|
|
9997
|
+
return new Response(injectHmrClient(`
|
|
9864
9998
|
<!DOCTYPE html>
|
|
9865
9999
|
<html>
|
|
9866
10000
|
<head>
|
|
@@ -9887,7 +10021,7 @@ ${pageSetupScripts.join(`
|
|
|
9887
10021
|
<ul>${routesList}</ul>
|
|
9888
10022
|
</body>
|
|
9889
10023
|
</html>
|
|
9890
|
-
|
|
10024
|
+
`), {
|
|
9891
10025
|
status: 404,
|
|
9892
10026
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
9893
10027
|
});
|
|
@@ -9963,5 +10097,6 @@ if (import.meta.main) {
|
|
|
9963
10097
|
main().catch(console.error);
|
|
9964
10098
|
}
|
|
9965
10099
|
export {
|
|
9966
|
-
serve
|
|
10100
|
+
serve,
|
|
10101
|
+
bundleBrowserAsset
|
|
9967
10102
|
};
|
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.67",
|
|
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.67",
|
|
77
|
+
"stx-router": "0.2.67"
|
|
78
78
|
}
|
|
79
79
|
}
|