@tinkerlandai/indiana-iam 0.4.0 → 0.5.0
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 +27 -4
- package/assets/viewer.js +60 -32
- package/package.json +8 -2
- package/src/analyze.mjs +199 -67
- package/src/anchors.mjs +2 -0
- package/src/build-styles.mjs +14 -9
- package/src/component-registry.mjs +1 -1
- package/src/document-material.mjs +180 -0
- package/src/dom-payload-json.mjs +2 -2
- package/src/dom-payload.mjs +3 -3
- package/src/framework-routes.mjs +1 -2
- package/src/generate.mjs +104 -24
- package/src/index.d.ts +0 -1
- package/src/index.mjs +1 -1
- package/src/json.mjs +2 -3
- package/src/library-entry.mjs +5 -2
- package/src/node-hashes.mjs +1 -1
- package/src/render-index.mjs +7 -7
- package/src/route-composition.mjs +5 -5
- package/src/route-tree.mjs +9 -9
- package/src/routing.mjs +7 -6
- package/src/schema-definition.mjs +13 -15
- package/src/selected-manifest.mjs +12 -0
- package/src/source-aliases.mjs +34 -0
- package/src/source-charts.mjs +15 -0
- package/src/source-css-modules.mjs +79 -0
- package/src/source-files.mjs +8 -7
- package/src/source-flowbite.mjs +60 -0
- package/src/source-i18next.mjs +22 -0
- package/src/source-mantine.mjs +61 -0
- package/src/source-mdx.mjs +92 -0
- package/src/source-media.mjs +12 -3
- package/src/source-styles.mjs +9 -0
- package/src/static-query.mjs +67 -7
- package/src/svg-preview.mjs +23 -0
- package/src/tanstack-routes.mjs +1 -1
- package/src/ui-structure.mjs +7 -18
- package/src/viewer-index.mjs +49 -0
package/README.md
CHANGED
|
@@ -119,7 +119,26 @@ and statically generated TanStack file-route trees registered with `createRouter
|
|
|
119
119
|
React Router supports imported static path constants, inline `Component` functions and source-proven lazy import forwarding.
|
|
120
120
|
Data-router `route.lazy` objects support a static module's named `Component`, or a source-proven mapper that captures
|
|
121
121
|
the component export. Mapper factories, imports and business loaders are never executed. Unknown overrides remain gaps.
|
|
122
|
-
MDX pages
|
|
122
|
+
Markdown/MDX pages, layouts and local imports retain original routes and source coordinates.
|
|
123
|
+
The pinned Markdown/MDX parsers project headings, lists, GFM tables/tasks, code, links, images and bounded source JSX.
|
|
124
|
+
Merchant MDX compilers/plugins, ESM side effects, arbitrary expressions and active HTML are never executed.
|
|
125
|
+
Generated Markdown markup stays preview-only; custom MDX component mappings and plugin transforms remain gaps.
|
|
126
|
+
CSS Modules retain source-literal default imports, deterministic local preview class names,
|
|
127
|
+
scoped selectors and static declarations, including standard media/supports/container rules.
|
|
128
|
+
Global selectors, resource loads, composition, nested preprocessing and animation definitions stay explicit gaps;
|
|
129
|
+
merchant PostCSS plugins and configuration are never executed. Compiled CSS remains a separate build input.
|
|
130
|
+
Mantine core forms, headings, containers, tables, images and controlled Modal/Drawer visibility have an inert
|
|
131
|
+
semantic HTML profile. Source labels, ordinary form constraints and safe default values are retained;
|
|
132
|
+
password values stay redacted. Theme resolution, responsive object props, portal positioning, focus handling
|
|
133
|
+
and library callbacks remain unobserved. Boolean `useDisclosure` without lifecycle callbacks, controlled Tabs,
|
|
134
|
+
Drawer/Modal close controls and native Accordion folding have bounded local previews. This profile does not
|
|
135
|
+
import Mantine or run its hooks.
|
|
136
|
+
Async source helpers and TanStack queries can expose source-declared literal fixtures, including a source-proven
|
|
137
|
+
timer-only delay. IAM interprets their AST without executing functions, awaiting timers or invoking query callbacks.
|
|
138
|
+
Safe-integer locale formatting uses deterministic English grouping in the source preview.
|
|
139
|
+
Declared `initialData` retains its source initial state; loading/error/network outcomes otherwise stay unknown.
|
|
140
|
+
Only the known frontend `services/api` source path is included; framework API routes, server trees and credentials
|
|
141
|
+
stay outside the scan. Remote data, mutations, impure helpers and ambiguous bindings remain unobserved.
|
|
123
142
|
TanStack reads the registered generated tree, including pathless layouts, index routes and simple `$param` paths;
|
|
124
143
|
custom history/basepath and complex parameter patterns require separate adapters. Loaders, guards, retry/reload behavior
|
|
125
144
|
and runtime data remain unobserved. Unsupported optional React paths are isolated as gaps.
|
|
@@ -138,11 +157,15 @@ Source-proven `useSyncExternalStore` matchMedia getters retain both typed viewpo
|
|
|
138
157
|
viewport. Subscriptions are not run. UI preference cookies with an explicit source SSR guard use an empty-cookie
|
|
139
158
|
preview; auth/session cookies and arbitrary stores stay unobserved. Pure lazy local-state initializers are interpreted
|
|
140
159
|
under the same bounded rules; merchant callbacks and browser storage are never executed or read.
|
|
141
|
-
|
|
160
|
+
IAM has no configured page, node or total artifact byte limit. The exporter retains every supported route.
|
|
142
161
|
Large artifacts store sizeable HTML/preorder-map pairs as `domPayload` (`gzip-json-v1`), while keeping `html: ""`
|
|
143
162
|
and `nodes: []` in that document's wire record. This lossless storage encoding preserves all source IDs, DOM values,
|
|
144
|
-
HTML/DOM hashes and targets. Route navigation stores the exact interaction-array index instead of repeating its ID; validation checks node, route, destination and parameters. Source files retain their full hashes; component/element records cover reachable route compositions and shared fragments. Text node IDs that follow the original parent/index identity formula are derived locally instead of repeated; explicit exceptions remain stored, and every resolved source-map hash is checked. The document hash pins its encoded storage body; decoding is local and
|
|
145
|
-
|
|
163
|
+
HTML/DOM hashes and targets. Route navigation stores the exact interaction-array index instead of repeating its ID; validation checks node, route, destination and parameters. Source files retain their full hashes; component/element records cover reachable route compositions and shared fragments. Text node IDs that follow the original parent/index identity formula are derived locally instead of repeated; explicit exceptions remain stored, and every resolved source-map hash is checked. The document hash pins its encoded storage body; decoding is local and checks the exact declared size and hashes. Exported page HTML is expanded.
|
|
164
|
+
The standalone viewer embeds only its route directory and hash-pinned document references. It reads and expands
|
|
165
|
+
the selected document on demand, cancels superseded reads and retains one expanded page. Derived page files
|
|
166
|
+
come from the complete validated IAM; the complete JSON remains the source of release identity.
|
|
167
|
+
Serve the output directory over loopback HTTP to open `index.html`; browser `file://` restrictions prevent
|
|
168
|
+
on-demand reads of its document files. Serving the exported files does not run the App or its backend.
|
|
146
169
|
Use the matching 0.4 generator, validator and viewer together. Indiana ingestion/viewer sources must be updated
|
|
147
170
|
before uploading compressed artifacts; publishing this npm package alone does not deploy Indiana. The offline
|
|
148
171
|
viewer uses the browser's built-in `DecompressionStream` and never fetches a decoder or merchant content.
|
package/assets/viewer.js
CHANGED
|
@@ -119,6 +119,11 @@ async function mountIamViewer(m, consoleContext) {
|
|
|
119
119
|
}
|
|
120
120
|
document.body.dataset.panel = "canvas";
|
|
121
121
|
$("viewport").value = String(viewport);
|
|
122
|
+
const inlineDocuments=new Map(m.documents.map(document=>[document.documentKey,document]));
|
|
123
|
+
const localDocumentFiles=new Map((m.documentFiles??[]).map(file=>[file.documentKey,file]));
|
|
124
|
+
const directoryInteractions=m.interactions;
|
|
125
|
+
const expandedDocuments=new WeakSet();
|
|
126
|
+
let localRead=null;
|
|
122
127
|
const changes = new Map(), baseline = new Map(), originalValues = new Map(), localValues = new Map(), savedViews = new Map(), seededViews = new Set();
|
|
123
128
|
let baselineLoaded = false;
|
|
124
129
|
let pendingDocument=null,projectionReadOnly=false,retryOpen=null;
|
|
@@ -208,7 +213,7 @@ async function mountIamViewer(m, consoleContext) {
|
|
|
208
213
|
$("commit").textContent = "";
|
|
209
214
|
$("page-count").textContent = m.pages.length;
|
|
210
215
|
$("summary").textContent =
|
|
211
|
-
`${m.pages.length} ${tr("页面")} · ${m.components.length} ${tr("组件")} · ${consoleContext?.catalog?.nodeCount??m.documents.reduce((n, d) => n + d.nodes.length, 0)} ${tr("DOM 节点")}`;
|
|
216
|
+
`${m.pages.length} ${tr("页面")} · ${m.summary?.components??m.components.length} ${tr("组件")} · ${consoleContext?.catalog?.nodeCount??m.summary?.nodes??m.documents.reduce((n, d) => n + d.nodes.length, 0)} ${tr("DOM 节点")}`;
|
|
212
217
|
$("gap-count").textContent = `${consoleContext?.catalog?.gapCount??m.coverage.gaps.length} ${iamEnglish ? "coverage gaps" : "项缺口已记录"}`;
|
|
213
218
|
$("coverage-description").textContent =
|
|
214
219
|
tr("这是从 App 源码生成的 HTML/CSS/JS 投影。未运行原应用;动态数据、鉴权状态、字体与第三方组件可能不完整。只有明确提取的界面状态可模拟。");
|
|
@@ -299,13 +304,49 @@ async function mountIamViewer(m, consoleContext) {
|
|
|
299
304
|
"<\/script>" + (keyboard ? "<script>" + keyboard.js.replaceAll("</script", "<\\/script") + "<\/script>" : "") + "</body></html>"
|
|
300
305
|
);
|
|
301
306
|
}
|
|
302
|
-
function openPage(next, targetUrl = next.route, renderKey, historyMove=null) {
|
|
307
|
+
async function openPage(next, targetUrl = next.route, renderKey, historyMove=null) {
|
|
303
308
|
if (consoleContext) targetUrl = viewUrl(targetUrl);
|
|
304
309
|
retryOpen={pageKey:next.pageKey,targetUrl,renderKey,historyMove};
|
|
305
310
|
if(consoleContext)consoleContext.documentReady=false;
|
|
306
311
|
const nextRender=m.routeTree.renders.find(render=>render.routeKey===next.pageKey&&render.renderKey===(renderKey??m.routeTree.routes.find(route=>route.routeKey===next.pageKey).initialRenderKey));
|
|
307
312
|
if (!nextRender) throw Error("Unknown preview render");
|
|
308
|
-
|
|
313
|
+
let nextDocument=byId(m.documents,"documentKey",nextRender.documentKey);
|
|
314
|
+
if(!consoleContext || consoleContext.mode!=="documents"){
|
|
315
|
+
localRead?.abort();localRead=null;pendingDocument=null;
|
|
316
|
+
if(!nextDocument||!expandedDocuments.has(nextDocument)){
|
|
317
|
+
const request={pageKey:next.pageKey,renderKey:nextRender.renderKey};
|
|
318
|
+
const controller=new AbortController();localRead=controller;pendingDocument=request;
|
|
319
|
+
$("document-status").textContent=tr("正在打开页面资料…");$("export").disabled=true;
|
|
320
|
+
try{
|
|
321
|
+
const file=localDocumentFiles.get(nextRender.documentKey);
|
|
322
|
+
if(file){
|
|
323
|
+
if(!/^document_[a-f0-9]{64}$/.test(file.documentKey)||!/^[a-f0-9]{64}$/.test(file.sha256)||!Number.isSafeInteger(file.bytes)||file.bytes<1)throw Error("Invalid local document reference");
|
|
324
|
+
const response=await fetch(new URL('documents/'+file.documentKey+'/material.json',location.href),{signal:controller.signal});
|
|
325
|
+
if(!response.ok)throw Error("Local document unavailable");
|
|
326
|
+
const bytes=await response.arrayBuffer();
|
|
327
|
+
if(bytes.byteLength!==file.bytes)throw Error("Local document size mismatch");
|
|
328
|
+
const actual=[...new Uint8Array(await crypto.subtle.digest('SHA-256',bytes))].map(b=>b.toString(16).padStart(2,'0')).join('');
|
|
329
|
+
if(actual!==file.sha256)throw Error("Local document digest mismatch");
|
|
330
|
+
const material=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(bytes));
|
|
331
|
+
const stored=material.document;
|
|
332
|
+
if(stored.documentKey!==nextRender.documentKey||stored.pageKey!==next.pageKey||stored.hashes.document!==nextRender.documentHash)throw Error("Local document scope mismatch");
|
|
333
|
+
const expanded=await expandNodeHashes(stored,request);
|
|
334
|
+
if(pendingDocument!==request)return;
|
|
335
|
+
for(const key of ['elements','styles','assets','scripts','components','interactions'])m[key]=material[key];
|
|
336
|
+
activeGraph=material.routeTree;m.routeTree={...activeGraph,routes:m.routeTree.routes,renders:m.routeTree.renders};expandedDocuments.add(expanded);m.documents=[expanded];
|
|
337
|
+
}else{
|
|
338
|
+
const stored=inlineDocuments.get(nextRender.documentKey);if(!stored)throw Error("Missing local document");
|
|
339
|
+
const expanded=await expandNodeHashes(stored,request);
|
|
340
|
+
if(pendingDocument!==request)return;
|
|
341
|
+
expandedDocuments.add(expanded);m.documents=[expanded];
|
|
342
|
+
}
|
|
343
|
+
pendingDocument=null;localRead=null;nextDocument=m.documents[0];
|
|
344
|
+
}catch(error){
|
|
345
|
+
if(pendingDocument===request){pendingDocument=null;localRead=null;$("document-status").textContent=tr("页面资料校验失败,请重新打开此版本。");toast(tr("页面资料校验失败,请重新打开此版本。"));}
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
309
350
|
if(!nextDocument&&consoleContext?.mode==="documents"){
|
|
310
351
|
pendingDocument={requestId:crypto.randomUUID(),pageKey:next.pageKey,renderKey:nextRender.renderKey,targetUrl,historyMove,documentKey:nextRender.documentKey,documentHash:nextRender.documentHash};
|
|
311
352
|
$("document-status").textContent=tr("正在打开页面资料…");$("export").disabled=true;
|
|
@@ -493,12 +534,12 @@ async function mountIamViewer(m, consoleContext) {
|
|
|
493
534
|
function checkedDomPayload(document) {
|
|
494
535
|
const payload = document.domPayload;
|
|
495
536
|
if (!payload) return null;
|
|
496
|
-
if (document.html !== "" || !Array.isArray(document.nodes) || document.nodes.length || Object.keys(payload).sort().join(",") !== "bytes,data,encoding" || payload.encoding !== "gzip-json-v1" || !Number.isSafeInteger(payload.bytes) || payload.bytes < 1 ||
|
|
537
|
+
if (document.html !== "" || !Array.isArray(document.nodes) || document.nodes.length || Object.keys(payload).sort().join(",") !== "bytes,data,encoding" || payload.encoding !== "gzip-json-v1" || !Number.isSafeInteger(payload.bytes) || payload.bytes < 1 || typeof payload.data !== "string" || !payload.data.length || payload.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(payload.data)) throw Error("Invalid compressed DOM payload");
|
|
497
538
|
return payload;
|
|
498
539
|
}
|
|
499
540
|
function parsedDomPayload(text) {
|
|
500
541
|
const value = JSON.parse(text);
|
|
501
|
-
if (!value || Object.keys(value).sort().join(",") !== "html,nodes" || typeof value.html !== "string" ||
|
|
542
|
+
if (!value || Object.keys(value).sort().join(",") !== "html,nodes" || typeof value.html !== "string" || !value.html.isWellFormed() || !Array.isArray(value.nodes) || JSON.stringify(value) !== text) throw Error("Invalid expanded DOM payload");
|
|
502
543
|
return value;
|
|
503
544
|
}
|
|
504
545
|
async function documentDomBrowser(document) {
|
|
@@ -529,7 +570,7 @@ async function documentDomBrowser(document) {
|
|
|
529
570
|
/* END COMPRESSED DOM */
|
|
530
571
|
/* BEGIN HTML DOM DERIVATION */
|
|
531
572
|
function expandDomNodes(document, body, deriveTextId) {
|
|
532
|
-
if(document.nodeEncoding!=='html-preorder-v1'||document.nodeHashMode!=='derived-v1'||!document.sourceMappingsHash
|
|
573
|
+
if(document.nodeEncoding!=='html-preorder-v1'||document.nodeHashMode!=='derived-v1'||!document.sourceMappingsHash)throw Error('Invalid HTML-derived DOM mode');
|
|
533
574
|
let index=0;const result=[];
|
|
534
575
|
function visit(node,parentId,depth,childIndex=0){
|
|
535
576
|
if(depth>128||![1,3].includes(node.nodeType))throw Error('Invalid HTML-derived DOM structure');
|
|
@@ -553,11 +594,11 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
553
594
|
const {domPayload, ...plain} = document;
|
|
554
595
|
document = {...plain, ...decoded};
|
|
555
596
|
}
|
|
556
|
-
if(!Array.isArray(document.nodes)
|
|
597
|
+
if(!Array.isArray(document.nodes))throw Error('Node hash budget');
|
|
557
598
|
const body=new DOMParser().parseFromString('<!doctype html><html><body>'+document.html+'</body></html>','text/html').body,textIds=new Map(),pendingText=[];
|
|
558
599
|
let preorder=0;
|
|
559
600
|
const collectText=(node,parentId,childIndex=0,depth=0)=>{
|
|
560
|
-
if(!node||depth>128
|
|
601
|
+
if(!node||depth>128)throw Error('DOM derivation budget');
|
|
561
602
|
const ref=document.nodes[preorder++];if(!ref)throw Error('Missing source node');
|
|
562
603
|
const id=node.nodeType===1?node.getAttribute('data-iam-node'):ref.nodeId;
|
|
563
604
|
if(node.nodeType===3&&!id)pendingText.push({parentId,index:childIndex,key:parentId+':'+childIndex});
|
|
@@ -595,12 +636,12 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
595
636
|
const request=pendingDocument;
|
|
596
637
|
try{
|
|
597
638
|
const projection=data.projection;
|
|
598
|
-
if(projection?.format!=="indiana-document-projection"||projection.schemaVersion!==2||projection.versionId!==consoleContext.versionId||projection.manifestDigest!==consoleContext.manifestDigest||projection.pageKey!==request.pageKey||projection.renderKey!==(request.renderKey??null)||projection.documentKey!==request.documentKey||projection.documentHash!==request.documentHash||typeof projection.payload!=="string"
|
|
639
|
+
if(projection?.format!=="indiana-document-projection"||projection.schemaVersion!==2||projection.versionId!==consoleContext.versionId||projection.manifestDigest!==consoleContext.manifestDigest||projection.pageKey!==request.pageKey||projection.renderKey!==(request.renderKey??null)||projection.documentKey!==request.documentKey||projection.documentHash!==request.documentHash||typeof projection.payload!=="string")throw Error("Projection scope mismatch");
|
|
599
640
|
const material=JSON.parse(projection.payload),document=material.document;
|
|
600
641
|
const {hashes,...body}=document;
|
|
601
642
|
if(document.documentKey!==request.documentKey||document.pageKey!==request.pageKey||hashes.document!==request.documentHash||await hash("iam/document/v1\0"+canonical(body))!==request.documentHash)throw Error("Projection document hash mismatch");
|
|
602
643
|
const provided = new Map();
|
|
603
|
-
if (!Array.isArray(material.resourceRefs)
|
|
644
|
+
if (!Array.isArray(material.resourceRefs)) throw Error("Invalid preview resources");
|
|
604
645
|
for (const kind of ["styles","assets","scripts"]) {
|
|
605
646
|
if (!Array.isArray(material[kind])) throw Error("Invalid preview resource kind");
|
|
606
647
|
for (const value of material[kind]) {
|
|
@@ -616,7 +657,6 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
616
657
|
if(!entry||entry.kind!==ref.kind)throw Error("Missing preview resource");
|
|
617
658
|
resolved.set(ref.hash,entry);
|
|
618
659
|
}
|
|
619
|
-
if([...resolved.values()].reduce((bytes,entry)=>bytes+entry.bytes,0)>8_388_608)throw Error("Preview resource byte budget");
|
|
620
660
|
for (const kind of ["styles","assets","scripts"]) material[kind]=[...resolved.values()].filter(entry=>entry.kind===kind).map(entry=>entry.value);
|
|
621
661
|
const script=document.scriptKey?material.scripts.find(script=>script.scriptKey===document.scriptKey):{js:document.js,sha256:hashes.js};
|
|
622
662
|
if(!script||!consoleContext.reviewedRuntimeHashes.includes(script.sha256)||await hash(script.js)!==script.sha256||document.scriptKey&&document.scriptKey!=="script_"+script.sha256)throw Error("Unreviewed preview runtime");
|
|
@@ -629,7 +669,7 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
629
669
|
if(consoleContext.catalog)m.routeTree={...material.routeTree,routes:consoleContext.catalog.routes,renders:consoleContext.catalog.renders};
|
|
630
670
|
const expanded=await expandNodeHashes(document,request);
|
|
631
671
|
if(pendingDocument!==request)return;
|
|
632
|
-
m.documents=[expanded];projectionReadOnly=projection.readOnly;pendingDocument=null;
|
|
672
|
+
expandedDocuments.add(expanded);m.documents=[expanded];projectionReadOnly=projection.readOnly;pendingDocument=null;
|
|
633
673
|
openPage(m.pages.find(page=>page.pageKey===request.pageKey),request.targetUrl,request.renderKey,request.historyMove);
|
|
634
674
|
}catch{if(pendingDocument===request){pendingDocument=null;projectionReadOnly=true;renderSaveCount();parent.postMessage({channel:"indiana-iam-console-v1",nonce:consoleContext.nonce,versionId:consoleContext.versionId,type:"error"},"*");}}
|
|
635
675
|
return;
|
|
@@ -804,9 +844,9 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
804
844
|
text("strong", p.route),
|
|
805
845
|
text(
|
|
806
846
|
"small",
|
|
807
|
-
p.componentKeys.length +
|
|
847
|
+
(p.componentCount??p.componentKeys.length) +
|
|
808
848
|
tr(" 组件 · ") +
|
|
809
|
-
p.stateKeys.length +
|
|
849
|
+
(p.stateCount??p.stateKeys.length) +
|
|
810
850
|
tr(" 状态候选"),
|
|
811
851
|
),
|
|
812
852
|
);
|
|
@@ -814,7 +854,7 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
814
854
|
const links = text("div", "", "map-targets"),
|
|
815
855
|
ids = [
|
|
816
856
|
...new Set(
|
|
817
|
-
|
|
857
|
+
directoryInteractions
|
|
818
858
|
.filter((i) => i.pageKey === p.pageKey && i.targetPageKey)
|
|
819
859
|
.map((i) => i.targetPageKey),
|
|
820
860
|
),
|
|
@@ -839,9 +879,9 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
839
879
|
format: "indiana-local-preview-changes",
|
|
840
880
|
schemaVersion: 1,
|
|
841
881
|
sourceGraphDigest: m.release.sourceGraphDigest,
|
|
842
|
-
manifestDocumentHashes: m.
|
|
843
|
-
documentKey:
|
|
844
|
-
hash:
|
|
882
|
+
manifestDocumentHashes: m.routeTree.renders.map((row) => ({
|
|
883
|
+
documentKey: row.documentKey,
|
|
884
|
+
hash: row.documentHash,
|
|
845
885
|
})),
|
|
846
886
|
changes: [...changes.values()],
|
|
847
887
|
scope: "local_preview_only",
|
|
@@ -859,19 +899,7 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
859
899
|
toast(tr("导出本地 JSON;未保存到服务器或应用到商户。"));
|
|
860
900
|
});
|
|
861
901
|
renderNav();
|
|
862
|
-
|
|
863
|
-
const initialRequest = {};
|
|
864
|
-
pendingDocument = initialRequest;
|
|
865
|
-
const expandedBytes = m.documents.reduce((total, document) => total + (document.domPayload?.bytes ?? new TextEncoder().encode(JSON.stringify({html:document.html,nodes:document.nodes})).byteLength), 0);
|
|
866
|
-
if (!Number.isSafeInteger(expandedBytes) || expandedBytes > 33_554_432) throw Error("Expanded DOM aggregate byte budget exceeded");
|
|
867
|
-
// Sequential decoding bounds peak memory for a multi-route artifact.
|
|
868
|
-
const expandedDocuments = [];
|
|
869
|
-
for (const document of m.documents) expandedDocuments.push(await expandNodeHashes(document, initialRequest));
|
|
870
|
-
m.documents = expandedDocuments;
|
|
871
|
-
pendingDocument = null;
|
|
872
|
-
renderNav();
|
|
873
|
-
}
|
|
874
|
-
openPage(page,consoleContext?.initialPage?.url??page.route,consoleContext?.initialPage?.renderKey??consoleContext?.catalog?.graph.variables.find(variable=>variable.id===consoleContext.initialPage?.variableKey)?.renderKey);
|
|
902
|
+
await openPage(page,consoleContext?.initialPage?.url??page.route,consoleContext?.initialPage?.renderKey??consoleContext?.catalog?.graph.variables.find(variable=>variable.id===consoleContext.initialPage?.variableKey)?.renderKey);
|
|
875
903
|
}
|
|
876
904
|
(async () => {
|
|
877
905
|
const data = JSON.parse(document.getElementById("iam-data").textContent);
|
|
@@ -908,7 +936,7 @@ function expandDomNodes(document, body, deriveTextId) {
|
|
|
908
936
|
const model={format:"indiana-preview-material",schemaVersion:1,app:{name:catalog.appName},release:{sourceGraphDigest:catalog.sourceGraphDigest},pages:catalog.routes.map(route=>({pageKey:route.routeKey,route:route.path,title:route.path,documentKeys:[route.documentKey],stateKeys:[],componentKeys:[]})),documents:[],elements:[],components:[],interactions:[],styles:[],scripts:[],assets:[],coverage:{gaps:[]},routeTree:{schemaVersion:catalog.renderIndexVersion,routes:catalog.routes,renders:catalog.renders,variables:catalog.graph.variables.map(variable=>({variableKey:variable.id,routeKey:variable.routeId,renderKey:variable.renderKey})),states:[],transitions:[],bindings:[],targets:[],navigation:[]}};
|
|
909
937
|
await mountIamViewer(model,context);loading.hidden=true;document.querySelector(".workspace").hidden=false;return;
|
|
910
938
|
}
|
|
911
|
-
if (!(message.bytes instanceof Uint8Array) || message.bytes.byteLength !== context.bytes
|
|
939
|
+
if (!(message.bytes instanceof Uint8Array) || message.bytes.byteLength !== context.bytes) throw new Error("Invalid IAM bytes");
|
|
912
940
|
const digest = [...new Uint8Array(await crypto.subtle.digest("SHA-256", message.bytes))].map(b => b.toString(16).padStart(2,"0")).join("");
|
|
913
941
|
if (digest !== context.manifestDigest) throw new Error("IAM digest mismatch");
|
|
914
942
|
const manifest = JSON.parse(new TextDecoder("utf-8",{fatal:true}).decode(message.bytes));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinkerlandai/indiana-iam",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Build-time Indiana App Manifest (IAM) generator for Next.js and Vite apps: scans source after the production build and writes the IAM and its release descriptor.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -49,10 +49,16 @@
|
|
|
49
49
|
"jsdom": "26.1.0",
|
|
50
50
|
"lucide-static": "1.34.0",
|
|
51
51
|
"magic-string": "0.30.21",
|
|
52
|
+
"mdast-util-from-markdown": "2.1.0",
|
|
53
|
+
"mdast-util-gfm": "3.1.0",
|
|
54
|
+
"mdast-util-mdx": "3.0.0",
|
|
55
|
+
"micromark-extension-gfm": "3.0.0",
|
|
56
|
+
"micromark-extension-mdxjs": "3.0.0",
|
|
52
57
|
"pako": "3.0.2",
|
|
53
58
|
"sharp": "0.35.5",
|
|
54
59
|
"tailwind-merge": "3.7.0",
|
|
55
60
|
"tailwindcss": "4.3.3",
|
|
56
|
-
"typescript": "5.9.3"
|
|
61
|
+
"typescript": "5.9.3",
|
|
62
|
+
"yaml": "2.9.1"
|
|
57
63
|
}
|
|
58
64
|
}
|