@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/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
- async function writeFinalizedLeavesExport(payload, outFile) {
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, options.outFile);
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,