@tinkerlandai/indiana-iam 0.2.0 → 0.4.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.
Files changed (55) hide show
  1. package/README.md +127 -5
  2. package/assets/document-controller.js +18 -1
  3. package/assets/document-runtime.js +189 -1
  4. package/assets/viewer.js +74 -7
  5. package/package.json +6 -13
  6. package/src/analyze.mjs +897 -123
  7. package/src/anchors.mjs +5 -0
  8. package/src/build-options.d.ts +6 -1
  9. package/src/build-runtime.mjs +4 -1
  10. package/src/build-styles.mjs +49 -6
  11. package/src/build.mjs +13 -7
  12. package/src/cli.mjs +10 -4
  13. package/src/component-registry.mjs +229 -0
  14. package/src/dependencies.mjs +2 -2
  15. package/src/dom-payload-browser.mjs +28 -0
  16. package/src/dom-payload-json.mjs +12 -0
  17. package/src/dom-payload.mjs +50 -0
  18. package/src/framework-routes.mjs +44 -89
  19. package/src/generate.mjs +20 -7
  20. package/src/graph-sources.mjs +54 -0
  21. package/src/index.d.ts +13 -1
  22. package/src/index.mjs +1 -1
  23. package/src/integrity.mjs +4 -2
  24. package/src/library-entry.mjs +5 -0
  25. package/src/lucide-source.mjs +18 -0
  26. package/src/next-loader.cjs +7 -1
  27. package/src/next.cjs +5 -0
  28. package/src/next.mjs +4 -3
  29. package/src/node-hashes.mjs +13 -9
  30. package/src/render-index.mjs +6 -4
  31. package/src/route-lazy.mjs +80 -0
  32. package/src/route-tree.mjs +15 -3
  33. package/src/routing.mjs +1 -1
  34. package/src/schema-definition.mjs +8 -1
  35. package/src/source-attributes.mjs +24 -0
  36. package/src/source-charts.mjs +38 -0
  37. package/src/source-collections.mjs +241 -0
  38. package/src/source-entry.mjs +50 -0
  39. package/src/source-environment.mjs +26 -0
  40. package/src/source-media.mjs +21 -0
  41. package/src/source-query-evidence.mjs +64 -0
  42. package/src/source-redirects.mjs +388 -0
  43. package/src/source-resolution.mjs +268 -0
  44. package/src/static-primitives.mjs +48 -0
  45. package/src/static-query.mjs +39 -11
  46. package/src/tab-scenes.mjs +214 -0
  47. package/src/tanstack-routes.mjs +143 -0
  48. package/src/transform.mjs +2 -2
  49. package/src/ui-preview.mjs +225 -0
  50. package/src/ui-state-machine.mjs +76 -0
  51. package/src/ui-structure.mjs +645 -0
  52. package/src/upload.mjs +28 -6
  53. package/src/validate.mjs +19 -4
  54. package/src/vite.d.ts +4 -1
  55. package/src/vite.mjs +25 -10
package/src/validate.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { documentDom, assertDomBudget } from "./dom-payload.mjs";
1
2
  import { loadTrustedRuntimes } from "./runtime-asset.mjs";
2
3
  export { parseManifest } from "./json.mjs";
3
4
  import { dependency } from "./dependencies.mjs";
@@ -31,6 +32,14 @@ export async function validate(manifest) {
31
32
  const verifiedSelectors = new Map();
32
33
  canonical(manifest);
33
34
  assertManifestShape(manifest);
35
+ assertDomBudget(manifest.documents);
36
+ if (manifest.documents.some(doc => doc.domPayload)) assertManifestShape({
37
+ ...manifest,
38
+ documents: manifest.documents.map(doc => {
39
+ const { domPayload, ...body } = doc;
40
+ return { ...body, ...documentDom(doc) };
41
+ }),
42
+ });
34
43
  const map = (values, key) => {
35
44
  const result = new Map(values.map((v) => [v[key], v]));
36
45
  if (result.size !== values.length) throw new Error("Duplicate " + key);
@@ -57,7 +66,7 @@ export async function validate(manifest) {
57
66
  if (!components.has(element.componentKey))
58
67
  throw new Error("Missing element component");
59
68
  }
60
- if (["react-router", "react-spa"].includes(manifest.routing.framework)) {
69
+ if (["react-router", "react-spa", "tanstack-router"].includes(manifest.routing.framework)) {
61
70
  if (manifest.routing.basePath || manifest.routing.trailingSlash || manifest.routing.routeSources?.length !== pages.size)
62
71
  throw Error("React route provenance is incomplete");
63
72
  for (const row of manifest.routing.routeSources) {
@@ -121,10 +130,11 @@ export async function validate(manifest) {
121
130
  throw new Error("Document hash mismatch");
122
131
  const fullNodes=documentNodes(doc);
123
132
  if (doc.sourceMappingsHash !== sourceMappingsHash(fullNodes, elements)) throw new Error('Source mapping hash mismatch');
124
- expandedDocuments.push({...doc,nodes:fullNodes});
133
+ const decoded = documentDom(doc);
134
+ expandedDocuments.push({...doc,html:decoded.html,nodes:fullNodes});
125
135
  const nodes = map(fullNodes, "nodeId");
126
136
  const dom = new JSDOM(
127
- "<!doctype html><html><body>" + doc.html + "</body></html>",
137
+ "<!doctype html><html><body>" + decoded.html + "</body></html>",
128
138
  );
129
139
  try {
130
140
  const document = dom.window.document;
@@ -161,6 +171,11 @@ export async function validate(manifest) {
161
171
  )
162
172
  throw new Error("DOM tag/locator mismatch");
163
173
  for (const attr of node.attributes) {
174
+ if(node.tagName.toLowerCase()==='use'&&['href','xlink:href'].includes(attr.name)){
175
+ if(attr.name!=='href'||!/^#iam-glyph-[a-f0-9]{24}$/.test(attr.value))throw Error('Unsafe SVG glyph reference');
176
+ const glyphs=[...document.querySelectorAll('[id]')].filter(el=>el.id===attr.value.slice(1));
177
+ if(glyphs.length!==1||glyphs[0].tagName.toLowerCase()!=='g'||glyphs[0].parentElement?.tagName.toLowerCase()!=='defs'||[...glyphs[0].querySelectorAll('*')].some(el=>!['path','circle','rect','line','polyline','polygon','ellipse','g'].includes(el.tagName.toLowerCase())))throw Error('Invalid SVG glyph definition');
178
+ }
164
179
  if (
165
180
  /^on/i.test(attr.name) ||
166
181
  ["srcdoc", "formaction", "action"].includes(attr.name)
@@ -277,7 +292,7 @@ export async function validate(manifest) {
277
292
  components: components.size,
278
293
  elements: elements.size,
279
294
  documents: docs.size,
280
- concreteNodes: manifest.documents.reduce((n, d) => n + d.nodes.length, 0),
295
+ concreteNodes: expandedDocuments.reduce((n, d) => n + d.nodes.length, 0),
281
296
  runtimeObserved: manifest.coverage.runtimeObserved,
282
297
  routeTree,
283
298
  };
package/src/vite.d.ts CHANGED
@@ -6,7 +6,10 @@ export declare function indianaIam(options?: IndianaIamBuildOptions): {
6
6
  name: "indiana-iam";
7
7
  apply: "build";
8
8
  enforce: "pre";
9
- transform(code: string, id: string): IamSourceTransform | null;
9
+ transform: {
10
+ order: "pre";
11
+ handler(code: string, id: string): IamSourceTransform | null;
12
+ };
10
13
  config(config: {
11
14
  root?: string;
12
15
  build?: { ssr?: unknown };
package/src/vite.mjs CHANGED
@@ -13,12 +13,13 @@ import { releasePath } from "./release-metadata.mjs";
13
13
  * IAM as the current version. Server builds are skipped.
14
14
  */
15
15
  export function indianaIam(options = {}) {
16
- const { enabled = true, name, automaticAnchors = true, strict = false, rootFont, omitSvgResources, logger = console, apiKey, baseUrl, upload, fetch } = options;
16
+ const { enabled = true, name, entry, automaticAnchors = true, strict = false, rootFont, omitSvgResources, logger = console, apiKey, baseUrl, upload, client, fetch } = options;
17
17
  let releaseId = null,
18
18
  root,
19
19
  outDir,
20
20
  base,
21
- server = false;
21
+ server = false,
22
+ buildFailed = false;
22
23
  return {
23
24
  name: "indiana-iam",
24
25
  apply: "build",
@@ -26,7 +27,7 @@ export function indianaIam(options = {}) {
26
27
  async config(config) {
27
28
  if (!enabled || config.build?.ssr) return;
28
29
  root = await fs.realpath(path.resolve(config.root ?? process.cwd()));
29
- const prepared = await prepareBuildRelease({ root, name, automaticAnchors, strict, logger });
30
+ const prepared = await prepareBuildRelease({ root, name, entry, automaticAnchors, strict, logger });
30
31
  if (!prepared) return;
31
32
  releaseId = prepared.releaseId;
32
33
  return { define: { "import.meta.env.INDIANA_IAM_RELEASE_URL": JSON.stringify(releasePath(releaseId)) } };
@@ -37,26 +38,40 @@ export function indianaIam(options = {}) {
37
38
  base = /^\/(?:[^/]+\/)*$/.test(config.base) ? config.base : "/";
38
39
  server = Boolean(config.build.ssr);
39
40
  },
40
- transform(code, id) {
41
- if (!enabled || server || !automaticAnchors || !releaseId || !root || id.startsWith("\0") || id.includes("?")) return null;
42
- const file = path.relative(root, id).split(path.sep).join("/");
43
- if (file.startsWith("../") || path.isAbsolute(file)) return null;
44
- return transformSourceForIam(code, file);
41
+ transform: {
42
+ // TanStack also runs a pre-transform. Anchor the original JSX before its
43
+ // reference/virtual module split, using the same original source path.
44
+ order: "pre",
45
+ handler(code, id) {
46
+ if (!enabled || server || !automaticAnchors || !releaseId || !root || id.startsWith("\0")) return null;
47
+ const [sourceId, query] = id.split("?");
48
+ if (query !== undefined) {
49
+ const parameters = [...new URLSearchParams(query)];
50
+ if (id.split("?").length !== 2 || parameters.length !== 1 || !["tsr-split", "tsr-shared"].includes(parameters[0][0]) || !parameters[0][1] || parameters[0][1].length > 256) return null;
51
+ }
52
+ const file = path.relative(root, sourceId).split(path.sep).join("/");
53
+ if (file.startsWith("../") || path.isAbsolute(file)) return null;
54
+ return transformSourceForIam(code, file);
55
+ },
56
+ },
57
+ buildEnd(error) {
58
+ if (error) buildFailed = true;
45
59
  },
46
60
  async closeBundle() {
47
61
  const environment = this?.environment?.name;
48
- if (!releaseId || server || (environment && environment !== "client")) return;
62
+ if (!releaseId || server || buildFailed || (environment && environment !== "client")) return;
49
63
  await finishBuildRelease({
50
64
  root,
51
65
  releaseId,
52
66
  name,
67
+ entry,
53
68
  automaticAnchors,
54
69
  cssDirectory: outDir,
55
70
  mounts: [{ directory: outDir, urlPath: base }],
56
71
  publicDirectory: outDir,
57
72
  rootFont,
58
73
  omitSvgResources,
59
- upload: { apiKey, baseUrl, upload, fetch },
74
+ upload: { apiKey, baseUrl, upload, client, fetch },
60
75
  strict,
61
76
  logger,
62
77
  });