@emeryld/rrroutes-contract 2.7.1 → 2.7.3
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 +92 -0
- package/dist/export/defaultViewerTemplate.d.ts +1 -0
- package/dist/export/exportFinalizedLeaves.d.ts +13 -1
- package/dist/export/index.d.ts +1 -0
- package/dist/index.cjs +179 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +178 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/tools/finalized-leaves-viewer.html +739 -0
package/dist/index.mjs
CHANGED
|
@@ -677,6 +677,79 @@ function flattenLeafSchemas(leaf) {
|
|
|
677
677
|
// src/export/exportFinalizedLeaves.ts
|
|
678
678
|
import fs from "fs/promises";
|
|
679
679
|
import path from "path";
|
|
680
|
+
import { spawn } from "child_process";
|
|
681
|
+
|
|
682
|
+
// src/export/defaultViewerTemplate.ts
|
|
683
|
+
var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
684
|
+
<html lang="en">
|
|
685
|
+
<head>
|
|
686
|
+
<meta charset="UTF-8" />
|
|
687
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
688
|
+
<title>Finalized Leaves Viewer</title>
|
|
689
|
+
<style>
|
|
690
|
+
:root {
|
|
691
|
+
--bg: #f5f7fb;
|
|
692
|
+
--surface: #ffffff;
|
|
693
|
+
--border: #d6ddea;
|
|
694
|
+
--text: #172033;
|
|
695
|
+
--muted: #5b6680;
|
|
696
|
+
--accent: #1858c6;
|
|
697
|
+
}
|
|
698
|
+
body {
|
|
699
|
+
margin: 0;
|
|
700
|
+
font-family: 'Iosevka Web', 'SFMono-Regular', Menlo, Consolas, monospace;
|
|
701
|
+
color: var(--text);
|
|
702
|
+
background: linear-gradient(180deg, var(--bg), #eef2fa);
|
|
703
|
+
}
|
|
704
|
+
.wrap { max-width: 1100px; margin: 0 auto; padding: 20px; }
|
|
705
|
+
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px; }
|
|
706
|
+
.meta { color: var(--muted); font-size: 12px; }
|
|
707
|
+
#results { margin-top: 12px; display: grid; gap: 8px; }
|
|
708
|
+
details { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; }
|
|
709
|
+
summary { cursor: pointer; font-weight: 700; color: var(--accent); }
|
|
710
|
+
pre { margin: 10px 0 0; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #fafcff; }
|
|
711
|
+
</style>
|
|
712
|
+
</head>
|
|
713
|
+
<body>
|
|
714
|
+
<div class="wrap">
|
|
715
|
+
<h1>Finalized Leaves Viewer (Baked)</h1>
|
|
716
|
+
<div class="card">
|
|
717
|
+
<div id="status" class="meta">Waiting for baked payload...</div>
|
|
718
|
+
</div>
|
|
719
|
+
<div id="results"></div>
|
|
720
|
+
</div>
|
|
721
|
+
|
|
722
|
+
<!--__FINALIZED_LEAVES_BAKED_PAYLOAD__-->
|
|
723
|
+
|
|
724
|
+
<script>
|
|
725
|
+
const statusEl = document.getElementById('status')
|
|
726
|
+
const resultsEl = document.getElementById('results')
|
|
727
|
+
const payload = window.__FINALIZED_LEAVES_PAYLOAD
|
|
728
|
+
|
|
729
|
+
if (!payload || !Array.isArray(payload.leaves)) {
|
|
730
|
+
statusEl.textContent = 'No baked payload found in this HTML file.'
|
|
731
|
+
} else {
|
|
732
|
+
statusEl.textContent = 'Loaded baked payload with ' + payload.leaves.length + ' routes.'
|
|
733
|
+
|
|
734
|
+
payload.leaves.forEach((leaf) => {
|
|
735
|
+
const details = document.createElement('details')
|
|
736
|
+
const summary = document.createElement('summary')
|
|
737
|
+
summary.textContent = String(leaf.method || '').toUpperCase() + ' ' + (leaf.path || '')
|
|
738
|
+
|
|
739
|
+
const pre = document.createElement('pre')
|
|
740
|
+
pre.textContent = JSON.stringify(leaf, null, 2)
|
|
741
|
+
|
|
742
|
+
details.appendChild(summary)
|
|
743
|
+
details.appendChild(pre)
|
|
744
|
+
resultsEl.appendChild(details)
|
|
745
|
+
})
|
|
746
|
+
}
|
|
747
|
+
</script>
|
|
748
|
+
</body>
|
|
749
|
+
</html>
|
|
750
|
+
`;
|
|
751
|
+
|
|
752
|
+
// src/export/exportFinalizedLeaves.ts
|
|
680
753
|
function isRegistry(value) {
|
|
681
754
|
return typeof value === "object" && value !== null && "all" in value && "byKey" in value;
|
|
682
755
|
}
|
|
@@ -722,13 +795,109 @@ function buildMeta() {
|
|
|
722
795
|
}
|
|
723
796
|
};
|
|
724
797
|
}
|
|
725
|
-
|
|
798
|
+
var BAKED_PAYLOAD_MARKER = "<!--__FINALIZED_LEAVES_BAKED_PAYLOAD__-->";
|
|
799
|
+
function escapePayloadForInlineScript(payload) {
|
|
800
|
+
return JSON.stringify(payload).replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--");
|
|
801
|
+
}
|
|
802
|
+
function injectPayloadIntoViewerHtml(htmlTemplate, payload) {
|
|
803
|
+
const payloadScript = `${BAKED_PAYLOAD_MARKER}
|
|
804
|
+
<script id="finalized-leaves-baked-payload">window.__FINALIZED_LEAVES_PAYLOAD = ${escapePayloadForInlineScript(
|
|
805
|
+
payload
|
|
806
|
+
)};</script>`;
|
|
807
|
+
if (htmlTemplate.includes(BAKED_PAYLOAD_MARKER)) {
|
|
808
|
+
return htmlTemplate.replace(BAKED_PAYLOAD_MARKER, payloadScript);
|
|
809
|
+
}
|
|
810
|
+
if (htmlTemplate.includes("</body>")) {
|
|
811
|
+
return htmlTemplate.replace("</body>", `${payloadScript}
|
|
812
|
+
</body>`);
|
|
813
|
+
}
|
|
814
|
+
return `${payloadScript}
|
|
815
|
+
${htmlTemplate}`;
|
|
816
|
+
}
|
|
817
|
+
async function resolveViewerTemplatePath(viewerTemplateFile) {
|
|
818
|
+
if (viewerTemplateFile) {
|
|
819
|
+
const resolved = path.resolve(viewerTemplateFile);
|
|
820
|
+
await fs.access(resolved);
|
|
821
|
+
return resolved;
|
|
822
|
+
}
|
|
823
|
+
const candidates = [
|
|
824
|
+
path.resolve(
|
|
825
|
+
process.cwd(),
|
|
826
|
+
"node_modules/@emeryld/rrroutes-contract/tools/finalized-leaves-viewer.html"
|
|
827
|
+
),
|
|
828
|
+
path.resolve(
|
|
829
|
+
process.cwd(),
|
|
830
|
+
"tools/finalized-leaves-viewer.html"
|
|
831
|
+
),
|
|
832
|
+
path.resolve(
|
|
833
|
+
process.cwd(),
|
|
834
|
+
"packages/contract/tools/finalized-leaves-viewer.html"
|
|
835
|
+
)
|
|
836
|
+
];
|
|
837
|
+
for (const candidate of candidates) {
|
|
838
|
+
try {
|
|
839
|
+
await fs.access(candidate);
|
|
840
|
+
return candidate;
|
|
841
|
+
} catch {
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return void 0;
|
|
845
|
+
}
|
|
846
|
+
async function writeJsonExport(payload, outFile) {
|
|
726
847
|
const resolved = path.resolve(outFile);
|
|
727
848
|
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
728
849
|
await fs.writeFile(resolved, `${JSON.stringify(payload, null, 2)}
|
|
729
850
|
`, "utf8");
|
|
730
851
|
return resolved;
|
|
731
852
|
}
|
|
853
|
+
async function writeBakedHtmlExport(payload, htmlFile, viewerTemplateFile) {
|
|
854
|
+
const templatePath = await resolveViewerTemplatePath(viewerTemplateFile);
|
|
855
|
+
const template = templatePath ? await fs.readFile(templatePath, "utf8") : DEFAULT_VIEWER_TEMPLATE;
|
|
856
|
+
const baked = injectPayloadIntoViewerHtml(template, payload);
|
|
857
|
+
const resolved = path.resolve(htmlFile);
|
|
858
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
859
|
+
await fs.writeFile(resolved, baked, "utf8");
|
|
860
|
+
return resolved;
|
|
861
|
+
}
|
|
862
|
+
async function openHtmlInBrowser(filePath) {
|
|
863
|
+
const resolved = path.resolve(filePath);
|
|
864
|
+
const platform = process.platform;
|
|
865
|
+
if (platform === "darwin") {
|
|
866
|
+
spawn("open", [resolved], { detached: true, stdio: "ignore" }).unref();
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
if (platform === "win32") {
|
|
870
|
+
spawn("cmd", ["/c", "start", "", resolved], {
|
|
871
|
+
detached: true,
|
|
872
|
+
stdio: "ignore"
|
|
873
|
+
}).unref();
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
spawn("xdg-open", [resolved], { detached: true, stdio: "ignore" }).unref();
|
|
877
|
+
}
|
|
878
|
+
async function writeFinalizedLeavesExport(payload, outFileOrOptions) {
|
|
879
|
+
const options = typeof outFileOrOptions === "string" ? { outFile: outFileOrOptions } : outFileOrOptions;
|
|
880
|
+
const written = {};
|
|
881
|
+
if (options.outFile) {
|
|
882
|
+
written.outFile = await writeJsonExport(payload, options.outFile);
|
|
883
|
+
}
|
|
884
|
+
if (options.htmlFile) {
|
|
885
|
+
written.htmlFile = await writeBakedHtmlExport(
|
|
886
|
+
payload,
|
|
887
|
+
options.htmlFile,
|
|
888
|
+
options.viewerTemplateFile
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
if (options.openOnFinish) {
|
|
892
|
+
if (!written.htmlFile) {
|
|
893
|
+
throw new Error(
|
|
894
|
+
"openOnFinish requires htmlFile. Provide htmlFile to open the baked viewer."
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
await openHtmlInBrowser(written.htmlFile);
|
|
898
|
+
}
|
|
899
|
+
return written;
|
|
900
|
+
}
|
|
732
901
|
async function exportFinalizedLeaves(input, options = {}) {
|
|
733
902
|
const leaves = getLeaves(input);
|
|
734
903
|
const serializedLeaves = serializeLeavesContract(leaves, options);
|
|
@@ -740,8 +909,13 @@ async function exportFinalizedLeaves(input, options = {}) {
|
|
|
740
909
|
leaves: serializedLeaves,
|
|
741
910
|
schemaFlatByLeaf
|
|
742
911
|
};
|
|
743
|
-
if (options.outFile) {
|
|
744
|
-
await writeFinalizedLeavesExport(payload,
|
|
912
|
+
if (options.outFile || options.htmlFile) {
|
|
913
|
+
await writeFinalizedLeavesExport(payload, {
|
|
914
|
+
outFile: options.outFile,
|
|
915
|
+
htmlFile: options.htmlFile,
|
|
916
|
+
viewerTemplateFile: options.viewerTemplateFile,
|
|
917
|
+
openOnFinish: options.openOnFinish
|
|
918
|
+
});
|
|
745
919
|
}
|
|
746
920
|
return payload;
|
|
747
921
|
}
|
|
@@ -794,6 +968,7 @@ async function runExportFinalizedLeavesCli(argv) {
|
|
|
794
968
|
};
|
|
795
969
|
}
|
|
796
970
|
export {
|
|
971
|
+
DEFAULT_VIEWER_TEMPLATE,
|
|
797
972
|
buildCacheKey,
|
|
798
973
|
buildLowProfileLeaf,
|
|
799
974
|
clearSchemaIntrospectionHandlers,
|