@wafertools/wafermap 0.21.1 → 0.22.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 (30) hide show
  1. package/AGENTS.md +170 -0
  2. package/CHANGELOG.md +160 -1
  3. package/README.md +9 -1
  4. package/dist/packages/canvas-adapter/icons.js +1 -1
  5. package/dist/packages/canvas-adapter/index.d.ts +2 -0
  6. package/dist/packages/canvas-adapter/index.js +1 -1
  7. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +14 -0
  8. package/dist/packages/canvas-adapter/renderWaferGallery.js +6 -6
  9. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +14 -0
  10. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  11. package/dist/packages/canvas-adapter/summaryPanel.d.ts +11 -1
  12. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  13. package/dist/packages/canvas-adapter/toolbar.d.ts +4 -1
  14. package/dist/packages/canvas-adapter/toolbar.js +1 -1
  15. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  16. package/dist/packages/canvas-adapter/userGuideHtml.js +59 -22
  17. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  18. package/dist/packages/canvas-adapter/version.js +1 -1
  19. package/dist/packages/canvas-adapter/warnings.d.ts +52 -0
  20. package/dist/packages/canvas-adapter/warnings.js +1 -0
  21. package/dist/packages/renderer/buildWaferMap.d.ts +15 -1
  22. package/dist/packages/renderer/buildWaferMap.js +1 -1
  23. package/dist/packages/renderer/index.d.ts +1 -1
  24. package/dist/packages/renderer/index.js +1 -1
  25. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  26. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  27. package/dist/packages/stats/renderFindingsReport.js +15 -15
  28. package/dist/packages/stats/types.d.ts +34 -4
  29. package/llms.txt +39 -0
  30. package/package.json +12 -8
@@ -1,11 +1,11 @@
1
- import{formatFindingDelta as g,formatFindingCoverage as h,formatFindingTooltip as b,escHtml as a,buildMetadataRows as d,renderDefinitionList as c,renderSection as p,renderSeverityBadge as w,reportStyles as v}from"./reportHtml.js";import{buildFindingsNarrative as y}from"./findingsNarrative.js";import{plainBinTerms as f}from"../renderer/fmt.js";function $(t,n){if(t.level==="lot"){const o=t,l=o.perWafer.map(r=>r.summary.wafer?.wafer??r.summary.wafer?.waferId).filter(r=>r!=null).map(String).join(", ");return c([...d(o.perWafer.map(r=>({metadata:r.summary.wafer}))),...l?[{label:"Wafers",value:l}]:[],{label:"Wafer count",value:String(o.stats.waferCount)},{label:"Generated",value:n}])}const e=t,i=e.stats.yieldPercent!==null?`${e.stats.yieldPercent.toFixed(1)}%`:"N/A";return c([...d([{metadata:e.wafer}]),{label:"Total dies",value:String(e.stats.totalDies)},{label:"Analysed dies",value:String(e.stats.analyzedDies)},{label:"Yield",value:i},{label:"Generated",value:n}])}function R(t,n){return t.length?t.map(e=>`<tr title="${a(b(e))}">
2
- <td class="tight">${w(e.severity)}</td>
3
- <td class="tight">${a(e.comparison.left)}</td>
4
- <td>${a(f(e.variable.label))}</td>
5
- <td class="numeric">${a(g(e))}</td>
6
- <td class="numeric">${a(h(e,n))}</td>
1
+ import{formatFindingDelta as b,formatFindingCoverage as w,formatFindingTooltip as v,escHtml as r,buildMetadataRows as c,renderDefinitionList as f,renderSection as p,renderSeverityBadge as $,reportStyles as y}from"./reportHtml.js";import{buildFindingsNarrative as R}from"./findingsNarrative.js";import{plainBinTerms as u}from"../renderer/fmt.js";function S(t,n){if(t.level==="lot"){const o=t,l=o.perWafer.map(a=>a.summary.wafer?.wafer??a.summary.wafer?.waferId).filter(a=>a!=null).map(String).join(", ");return f([...c(o.perWafer.map(a=>({metadata:a.summary.wafer}))),...l?[{label:"Wafers",value:l}]:[],{label:"Wafer count",value:String(o.stats.waferCount)},{label:"Generated",value:n}])}const e=t,i=e.stats.yieldPercent!==null?`${e.stats.yieldPercent.toFixed(1)}%`:"N/A";return f([...c([{metadata:e.wafer}]),{label:"Total dies",value:String(e.stats.totalDies)},{label:"Analysed dies",value:String(e.stats.analyzedDies)},{label:"Yield",value:i},{label:"Generated",value:n}])}function F(t,n){return t.length?t.map(e=>`<tr title="${r(v(e))}">
2
+ <td class="tight">${$(e.severity)}</td>
3
+ <td class="tight">${r(e.comparison.left)}</td>
4
+ <td>${r(u(e.variable.label))}</td>
5
+ <td class="numeric">${r(b(e))}</td>
6
+ <td class="numeric">${r(w(e,n))}</td>
7
7
  </tr>`).join(`
8
- `):'<tr><td colspan="5" class="no-data">No significant findings</td></tr>'}function F(t,n){return`<table class="report-table findings-table compact">
8
+ `):'<tr><td colspan="5" class="no-data">No significant findings</td></tr>'}function H(t,n){return`<table class="report-table findings-table compact">
9
9
  <thead>
10
10
  <tr>
11
11
  <th>Severity</th>
@@ -16,26 +16,26 @@ import{formatFindingDelta as g,formatFindingCoverage as h,formatFindingTooltip a
16
16
  </tr>
17
17
  </thead>
18
18
  <tbody>
19
- ${R(t,n)}
19
+ ${F(t,n)}
20
20
  </tbody>
21
- </table>`}export function renderFindingsReportHtml(t,n={}){const e=t.level==="lot",i=n.title??(e?"Lot Findings Report":"Wafer Findings Report"),o=new Date().toLocaleString(),l=t.findings,r=t.level==="lot"?t.stats.waferCount:void 0,s=f(y(l)??""),u=s?`<p class="findings-narrative">${a(s)}</p>
22
- `:"",m=[p("Summary",$(t,o)),p("Findings",u+F(l,r))].join(`
21
+ </table>`}export function renderFindingsReportHtml(t,n={}){const e=t.level==="lot",i=n.title??(e?"Lot Findings Report":"Wafer Findings Report"),o=new Date().toLocaleString(),l=new Set(t.findings.flatMap(s=>s.absorbedIds??[])),a=t.findings.filter(s=>!l.has(s.id)),m=t.level==="lot"?t.stats.waferCount:void 0,d=u(R(a)??""),g=d?`<p class="findings-narrative">${r(d)}</p>
22
+ `:"",h=[p("Summary",S(t,o)),p("Findings",g+H(a,m))].join(`
23
23
  `);return`<!DOCTYPE html>
24
24
  <html lang="en">
25
25
  <head>
26
26
  <meta charset="UTF-8">
27
- <title>${a(i)}</title>
27
+ <title>${r(i)}</title>
28
28
  <style>
29
- ${v()}
29
+ ${y()}
30
30
  </style>
31
31
  </head>
32
32
  <body>
33
33
  <main class="report">
34
34
  <header class="report-header">
35
- <h1>${a(i)}</h1>
36
- <p class="report-subtitle">Generated ${a(o)}</p>
35
+ <h1>${r(i)}</h1>
36
+ <p class="report-subtitle">Generated ${r(o)}</p>
37
37
  </header>
38
- ${m}
38
+ ${h}
39
39
  </main>
40
40
  </body>
41
41
  </html>`}export function openHtmlReport(t){if(typeof window.__openHtmlReport=="function"){window.__openHtmlReport(t);return}const n=window.open("","_blank");n&&(n.document.write(t),n.document.close())}export function setReportOpener(t){window.__openHtmlReport=t}
@@ -1,4 +1,5 @@
1
- import type { WaferMapInput, WaferMapResult } from '../renderer/buildWaferMap.js';
1
+ import type { WaferMapInput, WaferMapResult, WaferWarning } from '../renderer/buildWaferMap.js';
2
+ export type { WaferWarning };
2
3
  export type StatsSeverity = 'info' | 'notable' | 'unusual';
3
4
  export type StatsLevel = 'wafer' | 'lot' | 'inter-wafer';
4
5
  export type StatsVariableKind = 'yield' | 'hardBin' | 'softBin' | 'test' | 'functionalTest' | 'spatialPattern';
@@ -65,8 +66,25 @@ export interface StatsFinding {
65
66
  };
66
67
  summary: string;
67
68
  highlight: HighlightTarget;
68
- /** IDs of other findings that describe the same signal at a finer level of detail. */
69
+ /**
70
+ * IDs of other findings that describe the same signal at a finer level of
71
+ * detail. Note these do NOT all resolve to entries in `findings`: when a run
72
+ * of per-region findings is merged into one (e.g. `Rings 3–4`), this is the
73
+ * audit trail of the constituents it REPLACED, and those no longer exist.
74
+ */
69
75
  relatedIds?: string[];
76
+ /**
77
+ * IDs of findings that state exactly the same fact as this one and are
78
+ * therefore hidden from reading surfaces (the Summary panel, the findings
79
+ * report) in favour of it.
80
+ *
81
+ * Distinct from `relatedIds` deliberately: everything named here IS still
82
+ * present in `findings` and can be read programmatically — nothing is
83
+ * discarded, it is only de-duplicated for display. Two cases are collapsed:
84
+ * a soft-bin finding whose hard-bin twin covers provably the same dies, and
85
+ * the single pass bin's row against the yield row that restates it.
86
+ */
87
+ absorbedIds?: string[];
70
88
  }
71
89
  export interface StatsSummary {
72
90
  level: 'wafer';
@@ -96,8 +114,20 @@ export interface StatsSummary {
96
114
  */
97
115
  hardBinCounts?: Record<number, number>;
98
116
  softBinCounts?: Record<number, number>;
99
- /** Structured warnings emitted during analysis (e.g. test-count cap exceeded). */
100
- warnings?: string[];
117
+ /**
118
+ * Structured advisories raised during analysis — the same `WaferWarning`
119
+ * shape used by `WaferMapResult.warnings`, so a host has one warning
120
+ * vocabulary to handle rather than two. Branch on `warning.code`.
121
+ *
122
+ * The one raised today is `'test-count-capped'`: more tests were found than
123
+ * the analysis cap allows, so test-value analysis was skipped and no test
124
+ * findings exist. That is a silent absence — nothing throws — so check this
125
+ * rather than assuming an empty findings list means "nothing to report".
126
+ *
127
+ * `renderWaferMap`/`renderWaferGallery` surface these automatically in the
128
+ * toolbar's warning indicator; a host does not have to render them itself.
129
+ */
130
+ warnings?: WaferWarning[];
101
131
  /** True when this summary was produced from lot-aggregated data (lotStack). */
102
132
  isLotStack?: boolean;
103
133
  /** Aggregation method used to produce the lot-stack (e.g. 'mean', 'countBin'). Present only when isLotStack is true. */
package/llms.txt ADDED
@@ -0,0 +1,39 @@
1
+ # wafermap
2
+
3
+ Browser-first wafer map visualization toolkit for semiconductor test data.
4
+ Primary entry point: build wafer models with `buildWaferMap()`, then render with a canvas.
5
+
6
+ ## Core docs
7
+ Absolute URLs: this file ships inside the npm package and the examples archive,
8
+ where repo-relative paths do not resolve.
9
+ - [Agent rules](https://wafertools.github.io/wafermap/agents/) - the traps that produce silently wrong maps. Read this first.
10
+ - [Developer Guide](https://wafertools.github.io/wafermap/guide/) - end-to-end usage, CSV loading, display controls, stats, galleries, and worker usage.
11
+ - [API Reference](https://wafertools.github.io/wafermap/api/) - public API, input types, coordinate system rules, and configuration details.
12
+ - [Quick start](https://wafertools.github.io/wafermap/quickstart/) - install and first map.
13
+ - [Troubleshooting](https://wafertools.github.io/wafermap/troubleshooting/) - common failure modes.
14
+ - [Examples](https://wafertools.github.io/wafermap/examples/) - 20 runnable demo pages, also downloadable at https://wafertools.github.io/wafermap/wafermap-examples.zip to run offline.
15
+
16
+ ## Public package entry points
17
+ - `@wafertools/wafermap`
18
+ - `@wafertools/wafermap/render`
19
+ - `@wafertools/wafermap/stats`
20
+ - `@wafertools/wafermap/worker`
21
+ - `@wafertools/wafermap/worker-script`
22
+
23
+ ## Important notes
24
+ - `x` and `y` in `DieResult` are die grid positions (prober step integers), not millimetres.
25
+ - Use `testValues: Record<number, number>` (keyed by stable test number) rather than the deprecated `values: number[]` (positional array). Example: `testValues: { 1050: 0.95 }`.
26
+ - Hard bins go in `hbin`; soft bins go in `sbin`. Their number spaces are independent. Never merge them, and never fall back to `?? 0` — a missing bin is not bin 0.
27
+ - All coordinates shown to the user (axis ticks, tooltips) must be original die grid positions (`die.x` / `die.y`), not post-transform display coordinates.
28
+ - `buildWaferMap()` is pure and server-safe; renderers require the DOM.
29
+ - Recommended path for most users: `buildWaferMap()` + `renderWaferMap()`.
30
+ - `passBins` sets both the yield number and the wording of its label — do not assume `[1]`.
31
+ - `activeTest` takes a test number (e.g. `1050`), not a positional index.
32
+ - Functional tests (`testType: 'F'`) have no value; read verdicts only via `getTestPassStatus()`. A missing verdict is no-data, never a fail.
33
+ - `PlotMode` values are camelCase (`'hardBin'`, `'stackedValues'`), never snake_case.
34
+ - Full rules, including the removed-API table: see AGENTS.md, shipped alongside this file.
35
+
36
+ ## Development
37
+ - Build: `npm run build`
38
+ - Test: `npm test`
39
+ - Typecheck: `npm run check`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wafertools/wafermap",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "Interactive wafer map visualization and yield analysis for semiconductor test data. Hard bins, soft bins, test values, retests, edge exclusion, reticle overlays, spatial statistics, failure clustering, and lot-level trend analysis — pure ES modules, no runtime dependencies, no server required.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,7 +25,9 @@
25
25
  "!dist/**/*.map",
26
26
  "README.md",
27
27
  "LICENSE",
28
- "CHANGELOG.md"
28
+ "CHANGELOG.md",
29
+ "AGENTS.md",
30
+ "llms.txt"
29
31
  ],
30
32
  "main": "./dist/index.js",
31
33
  "module": "./dist/index.js",
@@ -72,10 +74,12 @@
72
74
  "build": "node scripts/build-user-guide.mjs && node scripts/sync-icons.mjs && node scripts/sync-version.mjs && rm -rf dist && tsc -p tsconfig.build.json",
73
75
  "build:guide": "node scripts/build-user-guide.mjs",
74
76
  "clean": "rm -rf dist",
75
- "check": "node scripts/sync-version.mjs && tsc --noEmit -p tsconfig.json",
77
+ "check": "node scripts/sync-version.mjs && tsc --noEmit -p tsconfig.json && node scripts/check-examples-manifest.mjs && node scripts/build-agents-page.mjs --check && node scripts/check-agents-guide.mjs && node scripts/check-toolbar-docs.mjs",
78
+ "build:examples-index": "node scripts/build-examples-index.mjs",
79
+ "check:drift": "node scripts/check-drift.mjs",
76
80
  "dev": "rm -rf docs/dist && cp -r dist docs/dist && ./.venv/bin/zensical serve --dev-addr localhost:8001",
77
81
  "test": "npm run build && node --test tests/*.test.mjs",
78
- "build:site": "rm -rf docs/dist && cp -r dist docs/ && ${ZENSICAL:-.venv/bin/zensical} build --clean && cp -r docs/data site/data && rm -rf docs/dist && node scripts/bundle-docs.mjs",
82
+ "build:site": "rm -rf docs/dist && cp -r dist docs/ && ${ZENSICAL:-.venv/bin/zensical} build --clean && cp -r docs/data site/data && rm -rf docs/dist && node scripts/bundle-docs.mjs && node scripts/build-examples-archive.mjs",
79
83
  "preview:site": "npm run build:site && echo \"\\nServing bundled site at http://localhost:8002/examples/ — Ctrl-C to stop\\n\" && cd site && python3 -m http.server 8002",
80
84
  "prepack": "npm run build && node scripts/minify-dist.mjs",
81
85
  "pack:check": "npm pack --dry-run",
@@ -83,7 +87,8 @@
83
87
  "postpublish": "echo \"\\ndist/ was minified for publish — run 'npm run build' to restore the readable dev build.\\n\"",
84
88
  "screenshots": "node scripts/capture-screenshots.mjs",
85
89
  "screenshots:list": "node scripts/capture-screenshots.mjs --list",
86
- "build:images": "npm run build && node scripts/capture-screenshots.mjs"
90
+ "build:images": "npm run build && node scripts/capture-screenshots.mjs",
91
+ "build:agents-page": "node scripts/build-agents-page.mjs"
87
92
  },
88
93
  "keywords": [
89
94
  "wafer-map",
@@ -110,11 +115,10 @@
110
115
  "devDependencies": {
111
116
  "esbuild": "^0.28.1",
112
117
  "jsdom": "^29.1.1",
113
- "marked": "^18.0.3",
118
+ "marked": "^18.0.7",
114
119
  "playwright": "^1.60.0",
115
- "semble": "^0.1.1",
116
120
  "serve": "^14.2.0",
117
- "typescript": "^5.6.3"
121
+ "typescript": "^5.9.3"
118
122
  },
119
123
  "allowScripts": {
120
124
  "esbuild@0.28.1": true