@reckona/mreact-router 0.0.97 → 0.0.99
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/README.md +4 -0
- package/dist/actions.d.ts +7 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +26 -2
- package/dist/actions.js.map +1 -1
- package/dist/adapters/aws-lambda.d.ts.map +1 -1
- package/dist/adapters/aws-lambda.js +10 -7
- package/dist/adapters/aws-lambda.js.map +1 -1
- package/dist/build.d.ts +38 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1087 -304
- package/dist/build.js.map +1 -1
- package/dist/bundle-pipeline.d.ts +7 -0
- package/dist/bundle-pipeline.d.ts.map +1 -1
- package/dist/bundle-pipeline.js +121 -87
- package/dist/bundle-pipeline.js.map +1 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +10 -8
- package/dist/cache.js.map +1 -1
- package/dist/cli-options.d.ts +1 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +16 -0
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +6 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +113 -8
- package/dist/client.js.map +1 -1
- package/dist/csrf.d.ts.map +1 -1
- package/dist/csrf.js +7 -3
- package/dist/csrf.js.map +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +18 -1
- package/dist/http.js.map +1 -1
- package/dist/import-policy.d.ts.map +1 -1
- package/dist/import-policy.js +7 -3
- package/dist/import-policy.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/link.d.ts +5 -2
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +92 -3
- package/dist/link.js.map +1 -1
- package/dist/logger.d.ts +7 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/module-runner.d.ts +2 -0
- package/dist/module-runner.d.ts.map +1 -1
- package/dist/module-runner.js +1 -0
- package/dist/module-runner.js.map +1 -1
- package/dist/native-route-matcher.d.ts +2 -1
- package/dist/native-route-matcher.d.ts.map +1 -1
- package/dist/native-route-matcher.js +13 -2
- package/dist/native-route-matcher.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +117 -2
- package/dist/render.js.map +1 -1
- package/dist/route-source.d.ts +4 -4
- package/dist/route-source.d.ts.map +1 -1
- package/dist/route-source.js +8 -8
- package/dist/route-source.js.map +1 -1
- package/dist/route-styles.d.ts +6 -0
- package/dist/route-styles.d.ts.map +1 -1
- package/dist/route-styles.js +10 -1
- package/dist/route-styles.js.map +1 -1
- package/dist/serve.d.ts +5 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +7 -4
- package/dist/serve.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +42 -2
- package/src/adapters/aws-lambda.ts +33 -16
- package/src/build.ts +1578 -397
- package/src/bundle-pipeline.ts +136 -88
- package/src/cache.ts +13 -8
- package/src/cli-options.ts +20 -0
- package/src/cli.ts +1 -0
- package/src/client.ts +145 -14
- package/src/csrf.ts +8 -3
- package/src/http.ts +21 -1
- package/src/import-policy.ts +8 -3
- package/src/index.ts +5 -0
- package/src/link.ts +129 -8
- package/src/logger.ts +9 -1
- package/src/module-runner.ts +4 -0
- package/src/native-route-matcher.ts +13 -2
- package/src/render.ts +177 -2
- package/src/route-source.ts +8 -8
- package/src/route-styles.ts +17 -1
- package/src/serve.ts +11 -6
package/dist/render.js
CHANGED
|
@@ -683,6 +683,13 @@ async function renderAppRequestInternal(options) {
|
|
|
683
683
|
vitePlugins: options.vitePlugins,
|
|
684
684
|
});
|
|
685
685
|
html = injectHeadMetadata(html, metadata);
|
|
686
|
+
warnIfCspNonceWouldBlockInlineTags({
|
|
687
|
+
html,
|
|
688
|
+
logger: options.logger,
|
|
689
|
+
metadata,
|
|
690
|
+
request: options.request,
|
|
691
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
692
|
+
});
|
|
686
693
|
html = injectAuthSessionClaims(html, originalAnalysis.authIncludesClaims ? currentAuthClaims() : undefined);
|
|
687
694
|
html = injectQueryState(html, dehydrate(queryClient));
|
|
688
695
|
const response = withOptionalActionCookie(htmlResponse(`<!DOCTYPE html>${clientNavigationHeadTags({
|
|
@@ -897,6 +904,13 @@ async function renderAppRequestInternal(options) {
|
|
|
897
904
|
finishRenderTimingPhase(timing, phaseStartedAt, "metadataMs");
|
|
898
905
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
899
906
|
html = injectHeadMetadata(html, metadata);
|
|
907
|
+
warnIfCspNonceWouldBlockInlineTags({
|
|
908
|
+
html,
|
|
909
|
+
logger: options.logger,
|
|
910
|
+
metadata,
|
|
911
|
+
request: options.request,
|
|
912
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
913
|
+
});
|
|
900
914
|
html = injectAuthSessionClaims(html, originalAnalysis.authIncludesClaims ? currentAuthClaims() : undefined);
|
|
901
915
|
html = injectQueryState(html, dehydrate(queryClient));
|
|
902
916
|
const response = withOptionalActionCookie(htmlResponse(`<!DOCTYPE html>${clientNavigationHeadTags({
|
|
@@ -1513,6 +1527,7 @@ export async function bundleMiddlewareModuleCode(options) {
|
|
|
1513
1527
|
code: options.code,
|
|
1514
1528
|
filename: options.file,
|
|
1515
1529
|
platform: "node",
|
|
1530
|
+
root: options.importPolicy?.projectRoot,
|
|
1516
1531
|
plugins: [
|
|
1517
1532
|
fileImportMetaUrlPlugin(),
|
|
1518
1533
|
createAppRouterImportPolicyPlugin({
|
|
@@ -2633,13 +2648,113 @@ function hasGenerateMetadataExport(code) {
|
|
|
2633
2648
|
function usesRuntimeCacheControl(code) {
|
|
2634
2649
|
return /\bcacheControl\s*\(/.test(code);
|
|
2635
2650
|
}
|
|
2651
|
+
function warnIfCspNonceWouldBlockInlineTags(options) {
|
|
2652
|
+
if (options.serverModuleCacheVersion !== undefined ||
|
|
2653
|
+
process.env.NODE_ENV === "production" ||
|
|
2654
|
+
options.metadata?.csp?.disable === true ||
|
|
2655
|
+
options.metadata?.csp?.directives === undefined ||
|
|
2656
|
+
!/<(?:script|style)\b/i.test(options.html)) {
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
const scriptNonces = cspDirectiveNonces(options.metadata.csp, "script-src");
|
|
2660
|
+
const styleNonces = cspDirectiveNonces(options.metadata.csp, "style-src");
|
|
2661
|
+
if (scriptNonces.size === 0 && styleNonces.size === 0) {
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
for (const tag of inlineCspTags(options.html)) {
|
|
2665
|
+
if (tag.name === "script") {
|
|
2666
|
+
if (scriptNonces.size > 0 &&
|
|
2667
|
+
!scriptHasExternalSourceOrInertType(tag.attributes) &&
|
|
2668
|
+
tag.content.trim() !== "" &&
|
|
2669
|
+
!tagHasMatchingNonce(tag.attributes, scriptNonces)) {
|
|
2670
|
+
warnCspInlineNonceMismatch(options, "script-src", "script");
|
|
2671
|
+
}
|
|
2672
|
+
continue;
|
|
2673
|
+
}
|
|
2674
|
+
if (styleNonces.size > 0 &&
|
|
2675
|
+
tag.content.trim() !== "" &&
|
|
2676
|
+
!tagHasMatchingNonce(tag.attributes, styleNonces)) {
|
|
2677
|
+
warnCspInlineNonceMismatch(options, "style-src", "style");
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
function warnCspInlineNonceMismatch(options, directive, tag) {
|
|
2682
|
+
const message = tag === "script"
|
|
2683
|
+
? "mreact router: CSP script-src uses a nonce, but an inline <script> without a matching nonce will be blocked. Add the script through metadata.head with nonce: true, move it to an external script, or remove script-src for this route."
|
|
2684
|
+
: "mreact router: CSP style-src uses a nonce, but an inline <style> without a matching nonce will be blocked. Add the style through metadata.head with nonce: true, move it to an external stylesheet, or remove style-src for this route.";
|
|
2685
|
+
if (options.logger === undefined) {
|
|
2686
|
+
console.warn(message);
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
emitRouterLog(options.logger, "warn", {
|
|
2690
|
+
directive,
|
|
2691
|
+
path: new URL(options.request.url).pathname,
|
|
2692
|
+
tag,
|
|
2693
|
+
type: "router:csp:inline-nonce-warning",
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
function cspDirectiveNonces(csp, directive) {
|
|
2697
|
+
const values = csp.directives?.[directive];
|
|
2698
|
+
const nonces = new Set();
|
|
2699
|
+
if (values !== undefined && csp.nonce !== undefined) {
|
|
2700
|
+
nonces.add(csp.nonce);
|
|
2701
|
+
}
|
|
2702
|
+
for (const value of Array.isArray(values) ? values : values === undefined ? [] : [values]) {
|
|
2703
|
+
const match = /^'nonce-([^']+)'$/.exec(value);
|
|
2704
|
+
if (match?.[1] !== undefined) {
|
|
2705
|
+
nonces.add(match[1]);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return nonces;
|
|
2709
|
+
}
|
|
2710
|
+
function inlineCspTags(html) {
|
|
2711
|
+
const tags = [];
|
|
2712
|
+
const pattern = /<(script|style)\b([^>]*)>([\s\S]*?)<\/\1>/gi;
|
|
2713
|
+
let match;
|
|
2714
|
+
while ((match = pattern.exec(html)) !== null) {
|
|
2715
|
+
const name = match[1]?.toLowerCase();
|
|
2716
|
+
if (name !== "script" && name !== "style") {
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
tags.push({
|
|
2720
|
+
attributes: parseTagAttributes(match[2] ?? ""),
|
|
2721
|
+
content: match[3] ?? "",
|
|
2722
|
+
name,
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
return tags;
|
|
2726
|
+
}
|
|
2727
|
+
function parseTagAttributes(source) {
|
|
2728
|
+
const attributes = new Map();
|
|
2729
|
+
const pattern = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
2730
|
+
let match;
|
|
2731
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
2732
|
+
const name = match[1]?.toLowerCase();
|
|
2733
|
+
if (name === undefined) {
|
|
2734
|
+
continue;
|
|
2735
|
+
}
|
|
2736
|
+
attributes.set(name, match[2] ?? match[3] ?? match[4] ?? "");
|
|
2737
|
+
}
|
|
2738
|
+
return attributes;
|
|
2739
|
+
}
|
|
2740
|
+
function scriptHasExternalSourceOrInertType(attributes) {
|
|
2741
|
+
if (attributes.has("src")) {
|
|
2742
|
+
return true;
|
|
2743
|
+
}
|
|
2744
|
+
const type = attributes.get("type")?.trim().toLowerCase();
|
|
2745
|
+
return type === "application/json" || type === "application/ld+json";
|
|
2746
|
+
}
|
|
2747
|
+
function tagHasMatchingNonce(attributes, expectedNonces) {
|
|
2748
|
+
const nonce = attributes.get("nonce");
|
|
2749
|
+
return nonce !== undefined && expectedNonces.has(nonce);
|
|
2750
|
+
}
|
|
2636
2751
|
function injectQueryState(html, state) {
|
|
2637
2752
|
if (state.queries.length === 0) {
|
|
2638
2753
|
return html;
|
|
2639
2754
|
}
|
|
2640
2755
|
const script = `<script type="application/json" id="${__MREACT_QUERY_STATE_SCRIPT_ID}">${escapeJsonForHtml(JSON.stringify(state))}</script>`;
|
|
2641
2756
|
return /<\/body>/i.test(html)
|
|
2642
|
-
? html.replace(/<\/body>/i, `${script}</body>`)
|
|
2757
|
+
? html.replace(/<\/body>/i, () => `${script}</body>`)
|
|
2643
2758
|
: `${html}${script}`;
|
|
2644
2759
|
}
|
|
2645
2760
|
function injectAuthSessionClaims(html, claims) {
|
|
@@ -2648,7 +2763,7 @@ function injectAuthSessionClaims(html, claims) {
|
|
|
2648
2763
|
}
|
|
2649
2764
|
const script = `<script type="application/json" id="${authSessionScriptId}">${escapeJsonForHtml(JSON.stringify(claims))}</script>`;
|
|
2650
2765
|
return /<\/body>/i.test(html)
|
|
2651
|
-
? html.replace(/<\/body>/i, `${script}</body>`)
|
|
2766
|
+
? html.replace(/<\/body>/i, () => `${script}</body>`)
|
|
2652
2767
|
: `${html}${script}`;
|
|
2653
2768
|
}
|
|
2654
2769
|
function authIncludesClaims(code) {
|