foundry-design 0.2.0-beta.21 → 0.2.0-beta.23
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 +6 -4
- package/dist/delivery-capture.d.ts +8 -0
- package/dist/delivery-capture.d.ts.map +1 -0
- package/dist/delivery-capture.js +234 -0
- package/dist/delivery-capture.js.map +1 -0
- package/dist/delivery-export.d.ts +3 -1
- package/dist/delivery-export.d.ts.map +1 -1
- package/dist/delivery-export.js +172 -8
- package/dist/delivery-export.js.map +1 -1
- package/dist/doctor.d.ts +8 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +110 -13
- package/dist/doctor.js.map +1 -1
- package/dist/index.js +51 -27
- package/dist/index.js.map +1 -1
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +9 -0
- package/dist/indexer.js.map +1 -1
- package/dist/installer.d.ts +2 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +2 -1
- package/dist/installer.js.map +1 -1
- package/dist/release.d.ts +3 -3
- package/dist/release.js +1 -1
- package/dist/session-resume.d.ts +16 -0
- package/dist/session-resume.d.ts.map +1 -0
- package/dist/session-resume.js +30 -0
- package/dist/session-resume.js.map +1 -0
- package/dist/source-anchor.d.ts +8 -0
- package/dist/source-anchor.d.ts.map +1 -0
- package/dist/source-anchor.js +40 -0
- package/dist/source-anchor.js.map +1 -0
- package/dist/visual-check-capture.d.ts +8 -0
- package/dist/visual-check-capture.d.ts.map +1 -0
- package/dist/visual-check-capture.js +331 -0
- package/dist/visual-check-capture.js.map +1 -0
- package/dist/visual-check.d.ts +10 -0
- package/dist/visual-check.d.ts.map +1 -0
- package/dist/visual-check.js +411 -0
- package/dist/visual-check.js.map +1 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -10,7 +10,9 @@ Design Branches keeps parallel visual directions isolated inside one local sessi
|
|
|
10
10
|
|
|
11
11
|
Foundry is distributed through npm, so testers do not need GitHub access.
|
|
12
12
|
|
|
13
|
-
> **Current beta release:** `0.2.0-beta.
|
|
13
|
+
> **Current beta release:** `0.2.0-beta.23`. For an exact-version install, use `foundry-design@0.2.0-beta.23`.
|
|
14
|
+
|
|
15
|
+
The redesigned workspace now opens by default. Existing `ui=next` links continue to work; append `ui=legacy` to the workspace URL only when you need the previous interface for compatibility.
|
|
14
16
|
|
|
15
17
|
Full documentation is available at [withfoundry.ai](https://withfoundry.ai).
|
|
16
18
|
|
|
@@ -141,10 +143,10 @@ If an npm mirror or existing `npx` cache reports an old tag, bypass it with a te
|
|
|
141
143
|
|
|
142
144
|
```bash
|
|
143
145
|
FOUNDRY_NPX_CACHE="$(mktemp -d)"
|
|
144
|
-
npx --yes --prefer-online --registry=https://registry.npmjs.org --cache "$FOUNDRY_NPX_CACHE" --package=foundry-design@0.2.0-beta.
|
|
146
|
+
npx --yes --prefer-online --registry=https://registry.npmjs.org --cache "$FOUNDRY_NPX_CACHE" --package=foundry-design@0.2.0-beta.23 foundry-design
|
|
145
147
|
```
|
|
146
148
|
|
|
147
|
-
The
|
|
149
|
+
The CLI supports Node.js 20.9 or newer. Repository development and the framework compatibility fixtures require Node.js 20.19 or newer. Read the [local-first safety model](https://withfoundry.ai/#safety) before using it with sensitive work.
|
|
148
150
|
|
|
149
151
|
## Agent plugin
|
|
150
152
|
|
|
@@ -158,7 +160,7 @@ Foundry remains local-first. Installing the plugin does not create an account, e
|
|
|
158
160
|
|
|
159
161
|
### Cursor one-click connection
|
|
160
162
|
|
|
161
|
-
[Add the Foundry MCP bridge to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=foundry-design-control&config=
|
|
163
|
+
[Add the Foundry MCP bridge to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=foundry-design-control&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcHJlZmVyLW9ubGluZSIsImZvdW5kcnktZGVzaWduLW1jcC1zZXJ2ZXJAMC4yLjAtYmV0YS4yMyJdLCJlbnYiOnsiRk9VTkRSWV9ERVNJR05fUlVOVElNRV9VUkwiOiJodHRwOi8vMTI3LjAuMC4xOjQzODcifX0%3D), then run `npx foundry-design` inside the project. The repository's Cursor plugin additionally bundles the Foundry skill and session-start hook for marketplace distribution.
|
|
162
164
|
|
|
163
165
|
## Current capabilities
|
|
164
166
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ChangeContext, type DesignChange, type VisualScreen } from 'foundry-design-protocol';
|
|
2
|
+
import type { DeliveryCaptureRequest, DeliveryCaptureResult, DeliveryEvidenceRequest } from 'foundry-design-runtime';
|
|
3
|
+
/** Reads one runtime-indexed capture, never a client-supplied file or arbitrary screenshot. */
|
|
4
|
+
export declare function readDeliveryEvidence({ projectRoot, evidence, }: DeliveryEvidenceRequest): Promise<Uint8Array>;
|
|
5
|
+
export declare function deliveryCaptureScreen(input: DeliveryCaptureRequest, change: DesignChange, context: ChangeContext): VisualScreen;
|
|
6
|
+
/** A reviewed Apply is the only caller. Fresh contexts never install the overlay or replay drafts. */
|
|
7
|
+
export declare function captureDeliveryEvidence(input: DeliveryCaptureRequest): Promise<DeliveryCaptureResult>;
|
|
8
|
+
//# sourceMappingURL=delivery-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-capture.d.ts","sourceRoot":"","sources":["../src/delivery-capture.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EAEjB,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAOhC,+FAA+F;AAC/F,wBAAsB,oBAAoB,CAAC,EACzC,WAAW,EACX,QAAQ,GACT,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC,CA0C/C;AAsBD,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,EAC7B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa,GACrB,YAAY,CAmFd;AA4BD,sGAAsG;AACtG,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CA6EhC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import { lstat, mkdir, open, realpath } from 'node:fs/promises';
|
|
5
|
+
import { join, relative, sep } from 'node:path';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import { chromium } from 'playwright';
|
|
8
|
+
import sharp from 'sharp';
|
|
9
|
+
import { visualScreenSchema, } from 'foundry-design-protocol';
|
|
10
|
+
import { captureVisualScreen, UnsupportedVisualContextError, validateVisualUrl, } from './visual-check-capture.js';
|
|
11
|
+
/** Reads one runtime-indexed capture, never a client-supplied file or arbitrary screenshot. */
|
|
12
|
+
export async function readDeliveryEvidence({ projectRoot, evidence, }) {
|
|
13
|
+
const capture = evidence.capture;
|
|
14
|
+
if (!capture?.sha256 ||
|
|
15
|
+
!capture.conditions ||
|
|
16
|
+
!['before', 'rebuilt'].includes(capture.phase) ||
|
|
17
|
+
!/^\.foundry\/sessions\/delivery-evidence\/(before|rebuilt)-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.png$/.test(evidence.path) ||
|
|
18
|
+
!evidence.path.split('/').at(-1)?.startsWith(`${capture.phase}-`))
|
|
19
|
+
throw new Error('Evidence is not a trusted source capture.');
|
|
20
|
+
const root = await realpath(projectRoot);
|
|
21
|
+
let file = root;
|
|
22
|
+
const parts = evidence.path.split('/');
|
|
23
|
+
for (const [index, part] of parts.entries()) {
|
|
24
|
+
file = join(file, part);
|
|
25
|
+
const stat = await lstat(file);
|
|
26
|
+
if (stat.isSymbolicLink() || (index < parts.length - 1 ? !stat.isDirectory() : !stat.isFile()))
|
|
27
|
+
throw new Error('Evidence paths must remain inside the project capture directory.');
|
|
28
|
+
}
|
|
29
|
+
if ((await realpath(file)) !== file)
|
|
30
|
+
throw new Error('Evidence path changed during validation.');
|
|
31
|
+
const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
32
|
+
try {
|
|
33
|
+
const stat = await handle.stat();
|
|
34
|
+
if (!stat.isFile() || stat.size > 20 * 1024 * 1024)
|
|
35
|
+
throw new Error('Evidence image exceeds the safe size limit.');
|
|
36
|
+
const png = await handle.readFile();
|
|
37
|
+
if (createHash('sha256').update(png).digest('hex') !== capture.sha256)
|
|
38
|
+
throw new Error('Evidence image does not match its recorded digest.');
|
|
39
|
+
const metadata = await sharp(png, { limitInputPixels: 40_000_000 }).metadata();
|
|
40
|
+
const scale = capture.conditions.deviceScaleFactor;
|
|
41
|
+
if (metadata.format !== 'png' ||
|
|
42
|
+
metadata.width !== capture.viewport.width * scale ||
|
|
43
|
+
metadata.height !== capture.viewport.height * scale)
|
|
44
|
+
throw new Error('Evidence image does not match its recorded viewport.');
|
|
45
|
+
return png;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await handle.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function themeHook(id, graph) {
|
|
52
|
+
if (id === 'current' || id === 'system')
|
|
53
|
+
return undefined;
|
|
54
|
+
const theme = graph?.themes?.find((candidate) => candidate.id === id);
|
|
55
|
+
if (!theme?.selector)
|
|
56
|
+
throw new UnsupportedVisualContextError(`Theme ${id} has no indexed root selector.`);
|
|
57
|
+
const attribute = /^(html|body|:root)?\[([a-z-]+)=["']?([^"'\]]+)["']?\]$/.exec(theme.selector);
|
|
58
|
+
if (attribute && (!theme.attribute || theme.attribute === attribute[2]))
|
|
59
|
+
return {
|
|
60
|
+
selector: attribute[1] || 'html',
|
|
61
|
+
attribute: attribute[2],
|
|
62
|
+
value: theme.value ?? attribute[3],
|
|
63
|
+
};
|
|
64
|
+
const className = /^(html|body|:root)?\.([a-zA-Z_-][\w-]*)$/.exec(theme.selector);
|
|
65
|
+
if (className)
|
|
66
|
+
return { selector: className[1] || 'html', attribute: 'class', value: className[2] };
|
|
67
|
+
throw new UnsupportedVisualContextError(`Theme ${id} does not expose a supported root attribute or class hook.`);
|
|
68
|
+
}
|
|
69
|
+
export function deliveryCaptureScreen(input, change, context) {
|
|
70
|
+
if (!input.changeSet.context.targetUrl)
|
|
71
|
+
throw new UnsupportedVisualContextError('No product preview URL is configured.');
|
|
72
|
+
const url = validateVisualUrl(input.changeSet.context.targetUrl);
|
|
73
|
+
const selector = typeof change.target.locator.selector === 'string'
|
|
74
|
+
? change.target.locator.selector
|
|
75
|
+
: typeof change.target.locator.foundryId === 'string'
|
|
76
|
+
? `[data-foundry-id=${JSON.stringify(change.target.locator.foundryId)}]`
|
|
77
|
+
: undefined;
|
|
78
|
+
if (!selector)
|
|
79
|
+
throw new UnsupportedVisualContextError('Reviewed target has no browser selector.');
|
|
80
|
+
const breakpoint = input.designGraph?.breakpoints?.find((candidate) => candidate.id === context.breakpoint);
|
|
81
|
+
const viewport = context.breakpoint === 'current'
|
|
82
|
+
? input.changeSet.context.viewport
|
|
83
|
+
: breakpoint && {
|
|
84
|
+
width: breakpoint.width,
|
|
85
|
+
height: breakpoint.height ?? input.changeSet.context.viewport?.height,
|
|
86
|
+
};
|
|
87
|
+
if (!viewport?.width || !viewport.height)
|
|
88
|
+
throw new UnsupportedVisualContextError(`Breakpoint ${context.breakpoint} has no recorded dimensions.`);
|
|
89
|
+
let selectedTheme = context.theme;
|
|
90
|
+
let state = 'current';
|
|
91
|
+
let stateHook;
|
|
92
|
+
if (context.state !== 'current') {
|
|
93
|
+
const authored = input.designGraph?.states?.find((candidate) => candidate.id === context.state);
|
|
94
|
+
if (!authored)
|
|
95
|
+
throw new UnsupportedVisualContextError(`State ${context.state} is not indexed.`);
|
|
96
|
+
if (authored.pseudoStates?.length)
|
|
97
|
+
throw new UnsupportedVisualContextError(`State ${context.state} needs pseudo-state replay; no visual capture was fabricated.`);
|
|
98
|
+
if (authored.reducedMotion === false)
|
|
99
|
+
throw new UnsupportedVisualContextError(`State ${context.state} requires motion; the still-image capture uses reduced motion.`);
|
|
100
|
+
if (authored.viewport &&
|
|
101
|
+
(authored.viewport.width !== viewport.width || authored.viewport.height !== viewport.height))
|
|
102
|
+
throw new UnsupportedVisualContextError(`State ${context.state} has different authored dimensions.`);
|
|
103
|
+
if (authored.theme) {
|
|
104
|
+
if (!['current', 'system', authored.theme].includes(selectedTheme))
|
|
105
|
+
throw new UnsupportedVisualContextError('Authored state and requested theme conflict.');
|
|
106
|
+
selectedTheme = authored.theme;
|
|
107
|
+
}
|
|
108
|
+
for (const [key, value] of Object.entries(authored.query ?? {}))
|
|
109
|
+
url.searchParams.set(key, value);
|
|
110
|
+
const variants = Object.entries(authored.variant ?? {});
|
|
111
|
+
if (variants.length > 1)
|
|
112
|
+
throw new UnsupportedVisualContextError(`State ${context.state} has multiple variant axes; no partial capture was retained.`);
|
|
113
|
+
if (variants[0]) {
|
|
114
|
+
const [property, value] = variants[0];
|
|
115
|
+
state = context.state;
|
|
116
|
+
stateHook = {
|
|
117
|
+
selector,
|
|
118
|
+
attribute: `data-${property.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`,
|
|
119
|
+
value: String(value),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
validateVisualUrl(url.toString());
|
|
124
|
+
return visualScreenSchema.parse({
|
|
125
|
+
version: 1,
|
|
126
|
+
name: 'delivery-capture',
|
|
127
|
+
url: url.toString(),
|
|
128
|
+
viewport,
|
|
129
|
+
theme: ['current', 'system'].includes(selectedTheme) ? 'current' : selectedTheme,
|
|
130
|
+
state,
|
|
131
|
+
themeHook: themeHook(selectedTheme, input.designGraph),
|
|
132
|
+
stateHook,
|
|
133
|
+
targets: [selector],
|
|
134
|
+
masks: [],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async function ignoredCaptureDirectory(projectRoot) {
|
|
138
|
+
const root = await realpath(projectRoot);
|
|
139
|
+
const local = '.foundry/sessions/delivery-evidence';
|
|
140
|
+
const { stdout } = await promisify(execFile)('git', ['check-ignore', '--', `${local}/capture.png`], { cwd: root, timeout: 2500 });
|
|
141
|
+
if (!stdout.trim())
|
|
142
|
+
throw new Error('Delivery evidence needs the ignored .foundry/sessions folder created by setup.');
|
|
143
|
+
let directory = root;
|
|
144
|
+
for (const part of local.split('/')) {
|
|
145
|
+
directory = join(directory, part);
|
|
146
|
+
const stat = await lstat(directory).catch((error) => {
|
|
147
|
+
if (error.code === 'ENOENT')
|
|
148
|
+
return undefined;
|
|
149
|
+
throw error;
|
|
150
|
+
});
|
|
151
|
+
if (stat?.isSymbolicLink() || (stat && !stat.isDirectory()))
|
|
152
|
+
throw new Error('Delivery evidence directory must be a local project directory.');
|
|
153
|
+
if (!stat)
|
|
154
|
+
await mkdir(directory);
|
|
155
|
+
}
|
|
156
|
+
return directory;
|
|
157
|
+
}
|
|
158
|
+
/** A reviewed Apply is the only caller. Fresh contexts never install the overlay or replay drafts. */
|
|
159
|
+
export async function captureDeliveryEvidence(input) {
|
|
160
|
+
const result = { screenshots: [], unavailable: [] };
|
|
161
|
+
if (input.signal.aborted)
|
|
162
|
+
return { screenshots: [], unavailable: ['Visual capture was cancelled.'] };
|
|
163
|
+
const root = await realpath(input.changeSet.context.projectRoot);
|
|
164
|
+
const directory = await ignoredCaptureDirectory(root);
|
|
165
|
+
const browser = await chromium.launch({ headless: true, timeout: 8000 });
|
|
166
|
+
const abort = () => {
|
|
167
|
+
void browser.close().catch(() => { });
|
|
168
|
+
};
|
|
169
|
+
input.signal.addEventListener('abort', abort, { once: true });
|
|
170
|
+
try {
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
for (const change of input.changeSet.changes) {
|
|
173
|
+
for (const breakpoint of change.contextSet.breakpoints)
|
|
174
|
+
for (const theme of change.contextSet.themes)
|
|
175
|
+
for (const state of change.contextSet.states) {
|
|
176
|
+
const context = { breakpoint, theme, state };
|
|
177
|
+
const key = JSON.stringify([change.target.id, context]);
|
|
178
|
+
if (seen.has(key))
|
|
179
|
+
continue;
|
|
180
|
+
seen.add(key);
|
|
181
|
+
const label = `${change.target.label}: ${breakpoint} / ${theme} / ${state}`;
|
|
182
|
+
if (input.signal.aborted) {
|
|
183
|
+
result.unavailable.push(`${label}: capture time limit reached.`);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const screen = deliveryCaptureScreen(input, change, context);
|
|
188
|
+
const path = join(directory, `${input.phase}-${randomUUID()}.png`);
|
|
189
|
+
const captured = await captureVisualScreen(browser, screen, root, path);
|
|
190
|
+
const geometry = captured.geometry[0];
|
|
191
|
+
if (captured.geometry.length !== 1 ||
|
|
192
|
+
!geometry ||
|
|
193
|
+
geometry.width <= 0 ||
|
|
194
|
+
geometry.height <= 0 ||
|
|
195
|
+
geometry.x >= screen.viewport.width ||
|
|
196
|
+
geometry.y >= screen.viewport.height ||
|
|
197
|
+
geometry.x + geometry.width <= 0 ||
|
|
198
|
+
geometry.y + geometry.height <= 0)
|
|
199
|
+
throw new UnsupportedVisualContextError('Reviewed target does not resolve to one visible rendered element.');
|
|
200
|
+
result.screenshots.push({
|
|
201
|
+
label: `${input.phase === 'before' ? 'Source before Apply' : 'Verified rebuilt source'} · ${label}`,
|
|
202
|
+
path: relative(root, path).split(sep).join('/'),
|
|
203
|
+
createdAt: captured.createdAt,
|
|
204
|
+
capture: {
|
|
205
|
+
version: 1,
|
|
206
|
+
phase: input.phase,
|
|
207
|
+
context,
|
|
208
|
+
viewport: screen.viewport,
|
|
209
|
+
sourceRevision: input.revision,
|
|
210
|
+
applyRunId: input.applyRunId,
|
|
211
|
+
targetId: change.target.id,
|
|
212
|
+
sha256: captured.screenshotHash,
|
|
213
|
+
conditions: {
|
|
214
|
+
motion: captured.motion,
|
|
215
|
+
browser: captured.browser,
|
|
216
|
+
platform: captured.platform,
|
|
217
|
+
deviceScaleFactor: captured.deviceScaleFactor,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
result.unavailable.push(`${input.phase} ${label}: ${error instanceof Error ? error.message : 'capture unavailable'}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
input.signal.removeEventListener('abort', abort);
|
|
231
|
+
await browser.close().catch(() => { });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=delivery-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-capture.js","sourceRoot":"","sources":["../src/delivery-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DeliveryRecord } from 'foundry-design-protocol';
|
|
2
|
+
export { matchingDeliveryCaptures } from 'foundry-design-protocol';
|
|
2
3
|
import { type StoredSession } from 'foundry-design-runtime';
|
|
3
4
|
export interface DeliveryExportManifest {
|
|
4
5
|
format: 'foundry.delivery-export';
|
|
@@ -20,7 +21,8 @@ export declare class DeliveryExportConflictError extends Error {
|
|
|
20
21
|
readonly documentationPageIds: string[];
|
|
21
22
|
constructor(conflictingPaths: string[], documentationPageIds: string[]);
|
|
22
23
|
}
|
|
23
|
-
export declare function renderPortableDeliveryJson(record: DeliveryRecord, projectRoot: string): string;
|
|
24
|
+
export declare function renderPortableDeliveryJson(record: DeliveryRecord, projectRoot: string, session?: Pick<StoredSession, 'applyRuns'>): string;
|
|
25
|
+
export declare function renderEngineeringBrief(session: StoredSession, record: DeliveryRecord): string;
|
|
24
26
|
export declare function deliveryExportFiles(session: StoredSession, record: DeliveryRecord): Map<string, string>;
|
|
25
27
|
export declare function resolveManagedDeliveryPath(outputRoot: string, relativePath: string): string;
|
|
26
28
|
export declare function writeRepositoryDeliveryExport(outputRoot: string, session: StoredSession, record: DeliveryRecord, now?: string): Promise<DeliveryExportManifest>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery-export.d.ts","sourceRoot":"","sources":["../src/delivery-export.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"delivery-export.d.ts","sourceRoot":"","sources":["../src/delivery-export.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,yBAAyB,CAAC;AAEjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,kBAAkB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClF;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,EAAE,CAAC;gBAE5B,gBAAgB,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE;CAQvE;AAqBD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,GACzC,MAAM,CAeR;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CA+G7F;AA+ED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,cAAc,GACrB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAyErB;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAc3F;AAuFD,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,cAAc,EACtB,GAAG,SAA2B,GAC7B,OAAO,CAAC,sBAAsB,CAAC,CAoEjC"}
|
package/dist/delivery-export.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { access, lstat, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import sharp from 'sharp';
|
|
5
|
+
import { engineeringVerification, matchingDeliveryCaptures } from 'foundry-design-protocol';
|
|
6
|
+
export { matchingDeliveryCaptures } from 'foundry-design-protocol';
|
|
4
7
|
import { renderDeliveryMarkdown, sanitizeDeliveryText, } from 'foundry-design-runtime';
|
|
5
8
|
export class DeliveryExportConflictError extends Error {
|
|
6
9
|
conflictingPaths;
|
|
@@ -28,12 +31,98 @@ function sanitizeJsonValue(value, projectRoot) {
|
|
|
28
31
|
function safeJson(value, projectRoot) {
|
|
29
32
|
return `${JSON.stringify(sanitizeJsonValue(value, projectRoot), null, 2)}\n`;
|
|
30
33
|
}
|
|
31
|
-
export function renderPortableDeliveryJson(record, projectRoot) {
|
|
32
|
-
|
|
34
|
+
export function renderPortableDeliveryJson(record, projectRoot, session) {
|
|
35
|
+
const run = session?.applyRuns.find((item) => item.id === record.applyRunId);
|
|
36
|
+
return JSON.stringify(sanitizeJsonValue({
|
|
37
|
+
format: 'foundry.delivery-record',
|
|
38
|
+
version: 1,
|
|
39
|
+
record,
|
|
40
|
+
reviewedChangeSet: run?.reviewedChangeSet,
|
|
41
|
+
}, projectRoot), null, 2);
|
|
42
|
+
}
|
|
43
|
+
export function renderEngineeringBrief(session, record) {
|
|
44
|
+
const root = session.changeSet.context.projectRoot;
|
|
45
|
+
const safe = (value) => sanitizeDeliveryText(value, root)
|
|
46
|
+
.replace(/\|/g, '\\|')
|
|
47
|
+
.replace(/[\r\n]+/g, ' ');
|
|
48
|
+
const value = (input) => safe(typeof input === 'string' ? input : (JSON.stringify(input) ?? 'unknown'));
|
|
49
|
+
const run = session.applyRuns.find((item) => item.id === record.applyRunId);
|
|
50
|
+
const reviewed = run?.reviewedChangeSet;
|
|
51
|
+
const changes = reviewed?.changes.filter((change) => record.changeIds.includes(change.id)) ?? [];
|
|
52
|
+
const lines = [
|
|
53
|
+
renderDeliveryMarkdown({ ...record, verificationResults: [] }, root),
|
|
54
|
+
'The coverage below is checked against the frozen request and current Apply attempt.',
|
|
55
|
+
'',
|
|
56
|
+
'## Reviewed implementation contract',
|
|
57
|
+
'',
|
|
58
|
+
];
|
|
59
|
+
if (!reviewed)
|
|
60
|
+
lines.push('The frozen reviewed contract is unavailable in this older record. Exact edits and complete context verification cannot be reconstructed.', '');
|
|
61
|
+
else {
|
|
62
|
+
lines.push('| Target / property | Before | Requested | Source | Context | Verification |', '| --- | --- | --- | --- | --- | --- |');
|
|
63
|
+
for (const change of changes) {
|
|
64
|
+
const mapping = change.mappingCandidates.find((item) => item.id === change.selectedMappingId) ??
|
|
65
|
+
(change.mappingCandidates.length === 1 ? change.mappingCandidates[0] : undefined);
|
|
66
|
+
const source = mapping?.source ?? change.target.source;
|
|
67
|
+
for (const breakpoint of change.contextSet.breakpoints)
|
|
68
|
+
for (const theme of change.contextSet.themes)
|
|
69
|
+
for (const state of change.contextSet.states) {
|
|
70
|
+
const verification = engineeringVerification(record, run, change, {
|
|
71
|
+
breakpoint,
|
|
72
|
+
theme,
|
|
73
|
+
state,
|
|
74
|
+
});
|
|
75
|
+
const result = verification.result;
|
|
76
|
+
const outcome = verification.status === 'passed'
|
|
77
|
+
? 'Passed'
|
|
78
|
+
: verification.status === 'failed'
|
|
79
|
+
? 'Failed'
|
|
80
|
+
: 'Untested';
|
|
81
|
+
lines.push(`| ${safe(change.target.label)} / ${safe(change.property)} | ${value(change.before)} | ${value(change.after)} | ${source ? safe(`${source.file}${source.line ? `:${source.line}` : ''}`) : 'Mapping unavailable'} | ${safe(`${breakpoint} / ${theme} / ${state}`)} | ${outcome}${result ? `: rendered ${value(result.rendered)}` : ''}${verification.reason ? ` (${safe(verification.reason)})` : ''} |`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!changes.length)
|
|
85
|
+
lines.push('| No matching changes in the frozen contract | | | | | Untested |');
|
|
86
|
+
lines.push('');
|
|
87
|
+
}
|
|
88
|
+
lines.push('## Execution provenance', '', `- Reviewed source revision: ${safe(record.baselineRevision ?? 'Unavailable')}`, `- Applied source revision: ${safe(record.appliedRevision ?? 'Unavailable')}`, `- Apply run: ${safe(record.applyRunId)}`, `- Design graph revision: ${safe(record.designGraphRevision ?? 'Unavailable')}`, `- Narrative origin: ${record.narrativeSource}`, '');
|
|
89
|
+
if (record.blockers.length)
|
|
90
|
+
lines.push('## Unresolved blockers', '', ...record.blockers.map((item) => `- ${safe(item)}`), '');
|
|
91
|
+
const pairs = matchingDeliveryCaptures(record);
|
|
92
|
+
lines.push('## Visual evidence', '');
|
|
93
|
+
if (record.captureIssues?.length)
|
|
94
|
+
lines.push(...record.captureIssues.map((issue) => `- Capture unavailable: ${safe(issue)}`), '');
|
|
95
|
+
if (!pairs.length)
|
|
96
|
+
lines.push('No matching before/rebuilt image pair is recorded. Existing references are not proof of a visual comparison.', '');
|
|
97
|
+
for (const pair of pairs) {
|
|
98
|
+
const context = pair.after.capture;
|
|
99
|
+
lines.push(`### ${safe(`${context.context.breakpoint} / ${context.context.theme} / ${context.context.state}`)} · ${context.viewport.width} × ${context.viewport.height}`, '', `Before: ${safe(pair.before.path)}`, '', `Rebuilt: ${safe(pair.after.path)}`, '');
|
|
100
|
+
}
|
|
101
|
+
for (const evidence of record.evidence) {
|
|
102
|
+
const phase = evidence.capture?.phase === 'preview'
|
|
103
|
+
? 'Temporary preview'
|
|
104
|
+
: evidence.capture?.phase === 'rebuilt'
|
|
105
|
+
? 'Rebuilt capture (see provenance)'
|
|
106
|
+
: evidence.capture?.phase === 'before'
|
|
107
|
+
? 'Before source change'
|
|
108
|
+
: 'Unclassified reference';
|
|
109
|
+
lines.push(`- ${phase}: ${safe(evidence.label)} — ${safe(evidence.path)} (${safe(evidence.createdAt)})`);
|
|
110
|
+
}
|
|
111
|
+
if (!record.validationResults.length)
|
|
112
|
+
lines.push('', 'No validation results are recorded.');
|
|
113
|
+
if (!record.verificationResults.length)
|
|
114
|
+
lines.push('', 'No rendered verification results are recorded.');
|
|
115
|
+
return `${lines.join('\n')}\n`;
|
|
33
116
|
}
|
|
34
117
|
function historyMarkdown(session, projectRoot) {
|
|
35
118
|
const safe = (value) => sanitizeDeliveryText(value, projectRoot);
|
|
36
119
|
const lines = ['# Foundry design history', ''];
|
|
120
|
+
for (const milestone of session.deliveryMilestones) {
|
|
121
|
+
const entries = session.designHistory.filter((entry) => milestone.entryIds.includes(entry.id));
|
|
122
|
+
lines.push(`## Milestone: ${safe(milestone.name)} (${milestone.status})`, '', safe(milestone.summary), '', ...entries.map((entry) => `- ${safe(entry.title)}: ${safe(entry.summary)} (${safe(entry.deliveryRecordId)})`), '');
|
|
123
|
+
if (milestone.entryIds.length > entries.length)
|
|
124
|
+
lines.push('Some milestone entries are unavailable in this session.', '');
|
|
125
|
+
}
|
|
37
126
|
if (!session.designHistory.length)
|
|
38
127
|
lines.push('No verified delivery records yet.', '');
|
|
39
128
|
for (const entry of session.designHistory) {
|
|
@@ -84,20 +173,35 @@ export function deliveryExportFiles(session, record) {
|
|
|
84
173
|
const files = new Map();
|
|
85
174
|
const recordId = deliveryIdentifier(record.id);
|
|
86
175
|
const safe = (value) => sanitizeDeliveryText(value, root);
|
|
87
|
-
addManagedFile(files, `docs/foundry/handoffs/${recordId}.md`,
|
|
88
|
-
addManagedFile(files, `docs/foundry/handoffs/${recordId}.json`,
|
|
176
|
+
addManagedFile(files, `docs/foundry/handoffs/${recordId}.md`, renderEngineeringBrief(session, record));
|
|
177
|
+
addManagedFile(files, `docs/foundry/handoffs/${recordId}.json`, `${renderPortableDeliveryJson(record, root, session)}\n`);
|
|
89
178
|
addManagedFile(files, `docs/foundry/handoffs/${recordId}.pr.md`, [
|
|
90
179
|
`# ${safe(record.title)}`,
|
|
91
180
|
'',
|
|
92
181
|
safe(record.summary),
|
|
93
182
|
'',
|
|
183
|
+
'## Intent',
|
|
184
|
+
'',
|
|
185
|
+
safe(record.intent),
|
|
186
|
+
'',
|
|
187
|
+
'## Affected source and contexts',
|
|
188
|
+
'',
|
|
189
|
+
...record.affectedFiles.map((file) => `- ${safe(file)}`),
|
|
190
|
+
...record.contexts.map((context) => `- ${safe(context.breakpoint)} / ${safe(context.theme)} / ${safe(context.state)}`),
|
|
191
|
+
'',
|
|
192
|
+
'## Risks and unresolved questions',
|
|
193
|
+
'',
|
|
194
|
+
...[...record.risks, ...record.questions, ...record.blockers].map((item) => `- ${safe(item)}`),
|
|
195
|
+
'',
|
|
94
196
|
'## Validation',
|
|
95
197
|
'',
|
|
96
198
|
...record.validationResults.map((item) => `- [${item.passed ? 'x' : ' '}] ${safe(item.name)}${item.summary ? `: ${safe(item.summary)}` : ''}`),
|
|
97
199
|
'',
|
|
98
200
|
'## Rendered verification',
|
|
99
201
|
'',
|
|
100
|
-
|
|
202
|
+
'See the engineering brief for exact request, claim and context coverage. Raw pass flags alone are not treated as proof.',
|
|
203
|
+
'',
|
|
204
|
+
`Complete engineering brief: ${recordId}.md`,
|
|
101
205
|
'',
|
|
102
206
|
].join('\n'));
|
|
103
207
|
for (const page of session.documentationPages)
|
|
@@ -146,6 +250,60 @@ async function assertNoSymlinkTraversal(root, target) {
|
|
|
146
250
|
}
|
|
147
251
|
}
|
|
148
252
|
}
|
|
253
|
+
async function bundledDeliveryEvidence(projectRoot, record) {
|
|
254
|
+
const files = new Map();
|
|
255
|
+
const attachments = [];
|
|
256
|
+
const candidates = [
|
|
257
|
+
...new Set([
|
|
258
|
+
...record.evidence.map((item) => item.path),
|
|
259
|
+
...record.verificationResults
|
|
260
|
+
.map((item) => item.screenshotPath)
|
|
261
|
+
.filter((item) => Boolean(item)),
|
|
262
|
+
]),
|
|
263
|
+
];
|
|
264
|
+
for (const [index, source] of candidates.entries()) {
|
|
265
|
+
try {
|
|
266
|
+
const target = resolve(projectRoot, source);
|
|
267
|
+
const inside = relative(projectRoot, target);
|
|
268
|
+
if (!inside || inside === '..' || inside.startsWith(`..${sep}`) || isAbsolute(inside))
|
|
269
|
+
throw new Error('Evidence is outside the project and was not copied.');
|
|
270
|
+
await assertNoSymlinkTraversal(projectRoot, target);
|
|
271
|
+
const extension = extname(target).toLowerCase();
|
|
272
|
+
if (!['.png', '.jpg', '.jpeg', '.webp'].includes(extension))
|
|
273
|
+
throw new Error('Only PNG, JPEG and WebP evidence can be bundled.');
|
|
274
|
+
const stat = await lstat(target);
|
|
275
|
+
if (!stat.isFile() || stat.size > 20 * 1024 * 1024)
|
|
276
|
+
throw new Error('Evidence is not a supported image file below 20 MB.');
|
|
277
|
+
const content = await readFile(target);
|
|
278
|
+
const metadata = await sharp(content).metadata();
|
|
279
|
+
if (!['png', 'jpeg', 'webp'].includes(metadata.format ?? ''))
|
|
280
|
+
throw new Error('Evidence file content is not a supported image.');
|
|
281
|
+
const provenance = record.evidence.find((item) => item.path === source)?.capture;
|
|
282
|
+
const scale = provenance?.conditions?.deviceScaleFactor ?? 1;
|
|
283
|
+
if (provenance &&
|
|
284
|
+
(metadata.width !== Math.round(provenance.viewport.width * scale) ||
|
|
285
|
+
metadata.height !== Math.round(provenance.viewport.height * scale)))
|
|
286
|
+
throw new Error('Image dimensions do not match the recorded capture viewport.');
|
|
287
|
+
const expected = record.evidence.find((item) => item.path === source)?.capture?.sha256;
|
|
288
|
+
const contentHash = sha256(content);
|
|
289
|
+
if (expected && expected !== contentHash)
|
|
290
|
+
throw new Error('Evidence hash no longer matches the capture provenance.');
|
|
291
|
+
const path = `docs/foundry/assets/${deliveryIdentifier(record.id)}-${index}${extension}`;
|
|
292
|
+
files.set(path, content);
|
|
293
|
+
attachments.push({ source, status: 'bundled', path, sha256: contentHash });
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
attachments.push({
|
|
297
|
+
source,
|
|
298
|
+
status: 'unavailable',
|
|
299
|
+
reason: error.code === 'ENOENT'
|
|
300
|
+
? 'Evidence file is not present in the project.'
|
|
301
|
+
: String(error.message),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { files, attachments };
|
|
306
|
+
}
|
|
149
307
|
export async function writeRepositoryDeliveryExport(outputRoot, session, record, now = new Date().toISOString()) {
|
|
150
308
|
const root = resolve(outputRoot);
|
|
151
309
|
const manifestPath = resolveManagedDeliveryPath(root, 'docs/foundry/manifest.json');
|
|
@@ -154,7 +312,13 @@ export async function writeRepositoryDeliveryExport(outputRoot, session, record,
|
|
|
154
312
|
? JSON.parse(await readFile(manifestPath, 'utf8'))
|
|
155
313
|
: undefined;
|
|
156
314
|
const priorHashes = new Map(previous?.files.map((file) => [file.path, file.sha256]) ?? []);
|
|
157
|
-
const files = deliveryExportFiles(session, record);
|
|
315
|
+
const files = new Map(deliveryExportFiles(session, record));
|
|
316
|
+
const bundled = await bundledDeliveryEvidence(session.changeSet.context.projectRoot, record);
|
|
317
|
+
for (const [path, content] of bundled.files)
|
|
318
|
+
files.set(path, content);
|
|
319
|
+
files.set(`docs/foundry/assets/${deliveryIdentifier(record.id)}.json`, safeJson({ screenshots: record.evidence, attachments: bundled.attachments }, session.changeSet.context.projectRoot));
|
|
320
|
+
const handoffPath = `docs/foundry/handoffs/${deliveryIdentifier(record.id)}.md`;
|
|
321
|
+
files.set(handoffPath, `${files.get(handoffPath)}\n## Bundled image files\n\n${bundled.attachments.length ? bundled.attachments.map((item) => (item.status === 'bundled' ? `- [${sanitizeDeliveryText(item.source, session.changeSet.context.projectRoot)}](../assets/${item.path.split('/').pop()}) · SHA-256 ${item.sha256}` : `- Unavailable: ${sanitizeDeliveryText(item.source, session.changeSet.context.projectRoot)}. ${sanitizeDeliveryText(item.reason ?? '', session.changeSet.context.projectRoot)}`)).join('\n') : 'No image files are referenced.'}\n`);
|
|
158
322
|
const documentationPages = documentationExports(session);
|
|
159
323
|
const targets = new Map();
|
|
160
324
|
for (const relativePath of files.keys()) {
|
|
@@ -167,7 +331,7 @@ export async function writeRepositoryDeliveryExport(outputRoot, session, record,
|
|
|
167
331
|
const target = targets.get(relativePath);
|
|
168
332
|
if (!(await exists(target)))
|
|
169
333
|
continue;
|
|
170
|
-
const actualHash = sha256(await readFile(target
|
|
334
|
+
const actualHash = sha256(await readFile(target));
|
|
171
335
|
const managedHash = priorHashes.get(relativePath);
|
|
172
336
|
if ((managedHash && actualHash !== managedHash) ||
|
|
173
337
|
(!managedHash && actualHash !== sha256(content))) {
|