@pygmalionjs/pygmalion 0.7.4 → 0.8.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.ko.md +17 -2
- package/README.md +18 -2
- package/dist-lib/CameraLayer-nkNrazn8.js +159 -0
- package/dist-lib/pygmalion.js +14628 -16995
- package/dist-lib/runtime-DdYnT6k4.js +19705 -0
- package/dist-lib/style.css +1 -1
- package/dist-lib/testing.js +20 -21
- package/dist-lib/types/EditorShell.d.ts +1 -1
- package/dist-lib/types/canvas/CameraLayer.d.ts +3 -3
- package/dist-lib/types/canvas/Canvas.d.ts +2 -2
- package/dist-lib/types/canvas/FrameLabelBanner.d.ts +1 -1
- package/dist-lib/types/canvas/FrameView.d.ts +8 -2
- package/dist-lib/types/canvas/FrozenRoutePreview.d.ts +1 -1
- package/dist-lib/types/canvas/InspectOverlay.d.ts +4 -2
- package/dist-lib/types/canvas/LightweightCanvas.d.ts +1 -1
- package/dist-lib/types/canvas/SectionBoxes.d.ts +1 -1
- package/dist-lib/types/canvas/ShadowRoutePreview.d.ts +2 -2
- package/dist-lib/types/canvas/useFlowSession.d.ts +1 -1
- package/dist-lib/types/canvas/useFrameLod.d.ts +1 -1
- package/dist-lib/types/contract/transport.d.ts +5 -5
- package/dist-lib/types/contract/types.d.ts +1 -1
- package/dist-lib/types/contract/validate.d.ts +3 -3
- package/dist-lib/types/core/context.d.ts +2 -2
- package/dist-lib/types/core/debug.d.ts +14 -14
- package/dist-lib/types/core/hostBindings.d.ts +2 -2
- package/dist-lib/types/core/runtime.d.ts +34 -3
- package/dist-lib/types/core/runtimeContext.d.ts +3 -3
- package/dist-lib/types/core/schedulers.d.ts +67 -0
- package/dist-lib/types/editor/assetEditing.d.ts +2 -2
- package/dist-lib/types/editor/automaticMotion.d.ts +0 -11
- package/dist-lib/types/editor/automaticPseudoStates.d.ts +8 -2
- package/dist-lib/types/editor/captureSupply.d.ts +16 -7
- package/dist-lib/types/editor/catalog.d.ts +2 -2
- package/dist-lib/types/editor/codegen.d.ts +6 -3
- package/dist-lib/types/editor/componentConnections.d.ts +13 -8
- package/dist-lib/types/editor/componentInstances.d.ts +2 -2
- package/dist-lib/types/editor/declarations.d.ts +107 -0
- package/dist-lib/types/editor/designCompiler.d.ts +9 -4
- package/dist-lib/types/editor/designImport.d.ts +48 -12
- package/dist-lib/types/editor/documentSync.d.ts +28 -25
- package/dist-lib/types/editor/domImport.d.ts +2 -2
- package/dist-lib/types/editor/fiberMap.d.ts +1 -1
- package/dist-lib/types/editor/flowSessionScheduler.d.ts +106 -159
- package/dist-lib/types/editor/flowSessions.d.ts +147 -84
- package/dist-lib/types/editor/frameInteraction.d.ts +22 -32
- package/dist-lib/types/editor/frameLabels.d.ts +11 -5
- package/dist-lib/types/editor/frameLanes.d.ts +14 -17
- package/dist-lib/types/editor/frameLod.d.ts +17 -0
- package/dist-lib/types/editor/frameLodRuntime.d.ts +38 -38
- package/dist-lib/types/editor/framePreviewKeys.d.ts +14 -5
- package/dist-lib/types/editor/frozenImport.d.ts +13 -14
- package/dist-lib/types/editor/heldPseudoStates.d.ts +1 -1
- package/dist-lib/types/editor/host.d.ts +10 -55
- package/dist-lib/types/editor/implementationSourceMap.d.ts +2 -2
- package/dist-lib/types/editor/importFidelity.d.ts +1 -1
- package/dist-lib/types/editor/inspect.d.ts +7 -2
- package/dist-lib/types/editor/interactiveSessionSurface.d.ts +26 -34
- package/dist-lib/types/editor/interactiveStates.d.ts +12 -10
- package/dist-lib/types/editor/livePreviewInstances.d.ts +13 -3
- package/dist-lib/types/editor/liveScreens.d.ts +25 -16
- package/dist-lib/types/editor/mountScheduler.d.ts +8 -11
- package/dist-lib/types/editor/nodeSelector.d.ts +1 -1
- package/dist-lib/types/editor/pendingSourceChanges.d.ts +19 -0
- package/dist-lib/types/editor/previewBootstrap.d.ts +12 -22
- package/dist-lib/types/editor/previewCaptureProgress.d.ts +10 -6
- package/dist-lib/types/editor/previewEnvironmentControls.d.ts +14 -17
- package/dist-lib/types/editor/previewIntent.d.ts +11 -16
- package/dist-lib/types/editor/previewSweep.d.ts +7 -3
- package/dist-lib/types/editor/previewWarmup.d.ts +19 -7
- package/dist-lib/types/editor/projectRuntime.d.ts +2 -2
- package/dist-lib/types/editor/registry.d.ts +6 -2
- package/dist-lib/types/editor/revisionCatalog.d.ts +19 -19
- package/dist-lib/types/editor/revisionCatalogInstall.d.ts +18 -17
- package/dist-lib/types/editor/routePreview.d.ts +85 -139
- package/dist-lib/types/editor/scenarioCoverage.d.ts +52 -20
- package/dist-lib/types/editor/screenCards.d.ts +10 -6
- package/dist-lib/types/editor/screenDimensions.d.ts +9 -5
- package/dist-lib/types/editor/screenFlows.d.ts +8 -5
- package/dist-lib/types/editor/screenInteractions.d.ts +9 -1
- package/dist-lib/types/editor/screenLists.d.ts +8 -4
- package/dist-lib/types/editor/screenStateGroups.d.ts +1 -1
- package/dist-lib/types/editor/sectionHeaders.d.ts +10 -6
- package/dist-lib/types/editor/sessionPresets.d.ts +12 -32
- package/dist-lib/types/editor/shadowPreview.d.ts +7 -10
- package/dist-lib/types/editor/sharedSource.d.ts +4 -2
- package/dist-lib/types/editor/sourceChangeSummary.d.ts +1 -1
- package/dist-lib/types/editor/sourceJournal.d.ts +2 -2
- package/dist-lib/types/editor/stateSwitchMetrics.d.ts +68 -82
- package/dist-lib/types/editor/store.d.ts +88 -22
- package/dist-lib/types/editor/storyboardAuthored.d.ts +2 -2
- package/dist-lib/types/editor/storyboardDiscovery.d.ts +4 -3
- package/dist-lib/types/editor/storyboardEnvironment.d.ts +9 -5
- package/dist-lib/types/editor/storyboardGraph.d.ts +1 -1
- package/dist-lib/types/editor/storyboardGraphRuntime.d.ts +8 -10
- package/dist-lib/types/editor/storyboardGraphView.d.ts +1 -1
- package/dist-lib/types/editor/storyboardJourney.d.ts +1 -1
- package/dist-lib/types/editor/surfaceDemands.d.ts +196 -31
- package/dist-lib/types/editor/tokens.d.ts +24 -25
- package/dist-lib/types/editor/variantAxes.d.ts +70 -0
- package/dist-lib/types/editor/variantPrefetch.d.ts +26 -43
- package/dist-lib/types/editor/viewportPresets.d.ts +6 -6
- package/dist-lib/types/editor/visualQa.d.ts +2 -2
- package/dist-lib/types/lib.d.ts +104 -108
- package/dist-lib/types/shell/CatalogAssetPanel.d.ts +1 -1
- package/dist-lib/types/shell/CodePanel.d.ts +1 -1
- package/dist-lib/types/shell/ComponentStateControls.d.ts +7 -53
- package/dist-lib/types/shell/FrameInspector.d.ts +17 -0
- package/dist-lib/types/shell/FrameReviewDetails.d.ts +28 -0
- package/dist-lib/types/shell/LayerTree.d.ts +1 -1
- package/dist-lib/types/shell/StoryboardGraphPanel.d.ts +2 -2
- package/dist-lib/types/shell/VariantPanel.d.ts +23 -0
- package/dist-lib/types/shell/inspectorControls.d.ts +34 -0
- package/dist-lib/types/shell/qaPresentation.d.ts +1 -1
- package/dist-lib/types/testing.d.ts +5 -6
- package/dist-lib/types/ui/SourceRefControl.d.ts +1 -1
- package/docs/coverage-contracts.md +431 -0
- package/docs/screen-state-contract.md +14 -16
- package/node/component-branches.mjs +7 -13
- package/node/design-session.mjs +3 -5
- package/node/dev-mirror.mjs +9 -6
- package/node/envelope.mjs +43 -0
- package/node/impact-frames.mjs +94 -0
- package/node/inspect-plugin.mjs +12 -6
- package/node/preview-artifact-plugin.mjs +3 -9
- package/node/qa-capture-plugin.mjs +3 -6
- package/node/source-graph.mjs +1 -0
- package/node/storyboard-environment.mjs +33 -6
- package/node/vite.mjs +1 -0
- package/package.json +7 -2
- package/dist-lib/CameraLayer-D2wrEPwG.js +0 -415
- package/dist-lib/frameLodRuntime-CU0IYaSu.js +0 -272
- package/dist-lib/runtime-DRkTU9eq.js +0 -16195
- package/dist-lib/types/core/defaultRuntime.d.ts +0 -3
package/node/dev-mirror.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
import { promisify } from 'node:util';
|
|
10
10
|
import { execFile } from 'node:child_process';
|
|
11
11
|
import { resolvePygmalionPreviewViteCacheDir } from './preview-vite-cache.mjs';
|
|
12
|
+
import { sendJson as sendJsonEnvelope } from './envelope.mjs';
|
|
12
13
|
|
|
13
14
|
const execFileAsync = promisify(execFile);
|
|
14
15
|
|
|
@@ -253,11 +254,8 @@ export function resolveDevMirrorWorktreePaths({
|
|
|
253
254
|
};
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
res.setHeader('content-type', 'application/json; charset=utf-8');
|
|
259
|
-
res.end(JSON.stringify(body));
|
|
260
|
-
}
|
|
257
|
+
// One envelope for every endpoint; see envelope.mjs.
|
|
258
|
+
const json = sendJsonEnvelope;
|
|
261
259
|
|
|
262
260
|
async function run(command, args, options = {}) {
|
|
263
261
|
const result = await execFileAsync(command, args, {
|
|
@@ -613,7 +611,12 @@ async function reclaimStaleDevMirrorSyncLock({
|
|
|
613
611
|
}
|
|
614
612
|
}
|
|
615
613
|
|
|
616
|
-
/**
|
|
614
|
+
/**
|
|
615
|
+
* Serializes mirror mutation and exact read-lease acquisition across processes.
|
|
616
|
+
* @param {string} repoRoot
|
|
617
|
+
* @param {() => Promise<unknown>} task
|
|
618
|
+
* @param {{ onLockCandidateReady?: (info: unknown) => unknown; onStaleLockConfirmed?: (info: unknown) => unknown; lockTimeoutMs?: number }} [options]
|
|
619
|
+
*/
|
|
617
620
|
export async function withDevMirrorSyncLock(
|
|
618
621
|
repoRoot,
|
|
619
622
|
task,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// One JSON envelope for every Pygmalion endpoint.
|
|
2
|
+
//
|
|
3
|
+
// Success bodies are whatever the operation returns. Failures always carry
|
|
4
|
+
// `ok: false`, a machine `error` code the transport maps to TransportError,
|
|
5
|
+
// and a prose `message`; `retryable`, `preparing`, and `details` ride along
|
|
6
|
+
// when the operation sets them. Plugins that used to write prose into `error`
|
|
7
|
+
// still can — it moves to `message` here and the code becomes request_failed.
|
|
8
|
+
const MACHINE_CODE = /^[a-z][a-z0-9_]*$/;
|
|
9
|
+
|
|
10
|
+
/** Shapes a failure body: machine code in `error`, prose in `message`. */
|
|
11
|
+
export function errorBody(body) {
|
|
12
|
+
const source = body && typeof body === 'object' && !Array.isArray(body) ? body : { error: String(body) };
|
|
13
|
+
const { error, message, ...rest } = source;
|
|
14
|
+
const code = typeof error === 'string' && MACHINE_CODE.test(error) ? error : 'request_failed';
|
|
15
|
+
const prose =
|
|
16
|
+
typeof message === 'string' && message
|
|
17
|
+
? message
|
|
18
|
+
: typeof error === 'string' && !MACHINE_CODE.test(error)
|
|
19
|
+
? error
|
|
20
|
+
: undefined;
|
|
21
|
+
return {
|
|
22
|
+
...rest,
|
|
23
|
+
ok: false,
|
|
24
|
+
error: code,
|
|
25
|
+
...(prose === undefined ? {} : { message: prose }),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Writes a JSON response. A status at or above 400, or a body that says
|
|
31
|
+
* `ok: false`, is written through `errorBody`; anything else is written as is.
|
|
32
|
+
*/
|
|
33
|
+
export function sendJson(response, statusCode, body) {
|
|
34
|
+
const failed =
|
|
35
|
+
statusCode >= 400 || (body && typeof body === 'object' && !Array.isArray(body) && body.ok === false);
|
|
36
|
+
const payload = `${JSON.stringify(failed ? errorBody(body) : body)}\n`;
|
|
37
|
+
response.statusCode = statusCode;
|
|
38
|
+
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
39
|
+
response.setHeader('content-length', String(Buffer.byteLength(payload)));
|
|
40
|
+
response.setHeader('cache-control', 'no-store');
|
|
41
|
+
response.setHeader('x-content-type-options', 'nosniff');
|
|
42
|
+
response.end(payload);
|
|
43
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Which frames a source change reaches, decided where the catalog lives.
|
|
2
|
+
//
|
|
3
|
+
// The editor used to take the dependency closure from `inspect.impact` and
|
|
4
|
+
// match it against each page's sourcePaths in the browser; the QA capture
|
|
5
|
+
// then took the frames the browser had decided on. Deciding here, from the
|
|
6
|
+
// revision catalog the capture worker also reads, gives both the same list.
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
export function normalizeSourcePath(value) {
|
|
11
|
+
return String(value)
|
|
12
|
+
.replaceAll('\\', '/')
|
|
13
|
+
.replace(/[?#].*$/, '')
|
|
14
|
+
.replace(/^\.?\//, '')
|
|
15
|
+
.replace(/\/+/g, '/');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function sameSourcePath(a, b) {
|
|
19
|
+
const left = normalizeSourcePath(a);
|
|
20
|
+
const right = normalizeSourcePath(b);
|
|
21
|
+
return left === right || left.endsWith(`/${right}`) || right.endsWith(`/${left}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The catalog screens whose declared source paths intersect `files`, in
|
|
26
|
+
* catalog order. Each entry is `{ id, name }`: the canonical frame id the
|
|
27
|
+
* editor and the capture worker both use, and the screen's name.
|
|
28
|
+
*/
|
|
29
|
+
export function impactFramesFromCatalog(catalog, files) {
|
|
30
|
+
const pages = catalog?.screens?.pages;
|
|
31
|
+
if (!Array.isArray(pages) || !Array.isArray(files) || files.length === 0) return [];
|
|
32
|
+
const frames = [];
|
|
33
|
+
for (const page of pages) {
|
|
34
|
+
if (!page || typeof page !== 'object') continue;
|
|
35
|
+
const id = typeof page.importPageId === 'string' ? page.importPageId : null;
|
|
36
|
+
if (!id) continue;
|
|
37
|
+
const sourcePaths = Array.isArray(page.sourcePaths) ? page.sourcePaths : [];
|
|
38
|
+
const hit = sourcePaths.some(
|
|
39
|
+
(sourcePath) =>
|
|
40
|
+
typeof sourcePath === 'string' &&
|
|
41
|
+
files.some((file) => typeof file === 'string' && sameSourcePath(sourcePath, file)),
|
|
42
|
+
);
|
|
43
|
+
if (!hit) continue;
|
|
44
|
+
frames.push({ id, name: typeof page.name === 'string' ? page.name : id });
|
|
45
|
+
}
|
|
46
|
+
return frames;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function containedFile(root, file) {
|
|
50
|
+
const resolvedRoot = path.resolve(root);
|
|
51
|
+
const resolved = path.resolve(resolvedRoot, file);
|
|
52
|
+
if (resolved !== resolvedRoot && !resolved.startsWith(`${resolvedRoot}${path.sep}`)) {
|
|
53
|
+
throw new Error('Revision catalog file must stay inside the source root.');
|
|
54
|
+
}
|
|
55
|
+
return resolved;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Reads the revision catalog on demand and answers impact frames from it. The
|
|
60
|
+
* file is re-read only when its modification time changes; a missing or
|
|
61
|
+
* unreadable catalog answers no frames rather than failing the impact call,
|
|
62
|
+
* because the dependency closure is still useful on its own.
|
|
63
|
+
*/
|
|
64
|
+
export function createImpactFrameResolver({ root, catalogFile }) {
|
|
65
|
+
let cached = null;
|
|
66
|
+
async function readCatalog() {
|
|
67
|
+
if (!catalogFile) return null;
|
|
68
|
+
const file = await containedFile(root, catalogFile);
|
|
69
|
+
let stat;
|
|
70
|
+
try {
|
|
71
|
+
stat = await fs.stat(file);
|
|
72
|
+
} catch {
|
|
73
|
+
cached = null;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
|
77
|
+
return cached.catalog;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const catalog = JSON.parse(await fs.readFile(file, 'utf8'));
|
|
81
|
+
cached = { mtimeMs: stat.mtimeMs, size: stat.size, catalog };
|
|
82
|
+
return catalog;
|
|
83
|
+
} catch {
|
|
84
|
+
cached = null;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
async resolve(files) {
|
|
90
|
+
const catalog = await readCatalog();
|
|
91
|
+
return catalog ? impactFramesFromCatalog(catalog, files) : [];
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
package/node/inspect-plugin.mjs
CHANGED
|
@@ -4,7 +4,9 @@ import path from 'node:path';
|
|
|
4
4
|
import MagicString from 'magic-string';
|
|
5
5
|
import ts from 'typescript';
|
|
6
6
|
import { findAffectedSourceFiles } from './source-graph.mjs';
|
|
7
|
+
import { createImpactFrameResolver } from './impact-frames.mjs';
|
|
7
8
|
import { writeSourceOperations } from './source-operations.mjs';
|
|
9
|
+
import { sendJson as sendJsonEnvelope } from './envelope.mjs';
|
|
8
10
|
|
|
9
11
|
export { findAffectedSourceFiles } from './source-graph.mjs';
|
|
10
12
|
export { writeSourceOperations } from './source-operations.mjs';
|
|
@@ -338,8 +340,13 @@ export function pygmalionInspectPlugin({
|
|
|
338
340
|
legacyEndpoints = [],
|
|
339
341
|
normalizeValue,
|
|
340
342
|
sourceDirectory = 'src',
|
|
343
|
+
catalogFile = null,
|
|
341
344
|
}) {
|
|
342
345
|
const controls = [endpoint, ...legacyEndpoints];
|
|
346
|
+
// Impact answers frames as well as files when the revision catalog is
|
|
347
|
+
// known: the same catalog the capture worker reads names each screen's
|
|
348
|
+
// source paths, so the editor and the QA capture agree on the frames.
|
|
349
|
+
const impactFrames = createImpactFrameResolver({ root, catalogFile });
|
|
343
350
|
return {
|
|
344
351
|
name: 'pygmalion-inspect',
|
|
345
352
|
apply: 'serve',
|
|
@@ -449,7 +456,9 @@ export function pygmalionInspectPlugin({
|
|
|
449
456
|
const files = await findAffectedSourceFiles(root, input?.componentFiles, {
|
|
450
457
|
sourceDirectory,
|
|
451
458
|
});
|
|
452
|
-
|
|
459
|
+
const componentFiles = Array.isArray(input?.componentFiles) ? input.componentFiles : [];
|
|
460
|
+
const frames = await impactFrames.resolve([...componentFiles, ...files]);
|
|
461
|
+
sendJson(res, 200, { files, frames });
|
|
453
462
|
return;
|
|
454
463
|
}
|
|
455
464
|
|
|
@@ -517,11 +526,8 @@ async function readJson(req) {
|
|
|
517
526
|
return JSON.parse(raw);
|
|
518
527
|
}
|
|
519
528
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
res.setHeader('content-type', 'application/json; charset=utf-8');
|
|
523
|
-
res.end(JSON.stringify(body));
|
|
524
|
-
}
|
|
529
|
+
// One envelope for every endpoint; see envelope.mjs.
|
|
530
|
+
const sendJson = sendJsonEnvelope;
|
|
525
531
|
|
|
526
532
|
export async function writeStyleEdit(
|
|
527
533
|
root,
|
|
@@ -19,6 +19,7 @@ export {
|
|
|
19
19
|
PYGMALION_REVISION_CATALOG_SUFFIX,
|
|
20
20
|
PREVIEW_CAPTURE_PRIORITY,
|
|
21
21
|
} from './contract.mjs';
|
|
22
|
+
import { sendJson as sendJsonEnvelope } from './envelope.mjs';
|
|
22
23
|
export const DEFAULT_PYGMALION_PREVIEW_ARTIFACT_FILE =
|
|
23
24
|
'artifacts/pygmalion-route-previews.json';
|
|
24
25
|
|
|
@@ -57,15 +58,8 @@ function isLocalRequestHost(value) {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
response.statusCode = statusCode;
|
|
63
|
-
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
64
|
-
response.setHeader('content-length', String(Buffer.byteLength(payload)));
|
|
65
|
-
response.setHeader('cache-control', 'no-store');
|
|
66
|
-
response.setHeader('x-content-type-options', 'nosniff');
|
|
67
|
-
response.end(payload);
|
|
68
|
-
}
|
|
61
|
+
// One envelope for every endpoint; see envelope.mjs.
|
|
62
|
+
const sendJson = sendJsonEnvelope;
|
|
69
63
|
|
|
70
64
|
function requestedIdentity(params, key) {
|
|
71
65
|
const value = params.get(key);
|
|
@@ -6,6 +6,7 @@ import os from 'node:os';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import pixelmatch from 'pixelmatch';
|
|
8
8
|
import pngjs from 'pngjs';
|
|
9
|
+
import { sendJson as sendJsonEnvelope } from './envelope.mjs';
|
|
9
10
|
|
|
10
11
|
export const PYGMALION_QA_CAPTURE_ENDPOINT = PYGMALION_ENDPOINTS.qaCapture;
|
|
11
12
|
export const PYGMALION_QA_ARTIFACT_PREFIX = PYGMALION_ENDPOINTS.qaArtifactPrefix;
|
|
@@ -1177,12 +1178,8 @@ async function readJson(req) {
|
|
|
1177
1178
|
}
|
|
1178
1179
|
}
|
|
1179
1180
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
res.setHeader('content-type', 'application/json; charset=utf-8');
|
|
1183
|
-
res.setHeader('cache-control', 'no-store');
|
|
1184
|
-
res.end(JSON.stringify(body));
|
|
1185
|
-
}
|
|
1181
|
+
// One envelope for every endpoint; see envelope.mjs.
|
|
1182
|
+
const sendJson = sendJsonEnvelope;
|
|
1186
1183
|
|
|
1187
1184
|
function sendPng(res, artifact) {
|
|
1188
1185
|
res.statusCode = 200;
|
package/node/source-graph.mjs
CHANGED
|
@@ -379,6 +379,7 @@ export function directSourceUsers(usage, targetFile, { maxDepth = MAX_BARREL_DEP
|
|
|
379
379
|
const seen = new Set();
|
|
380
380
|
// 'star' means every symbol of the target is in play, which is what the
|
|
381
381
|
// target itself exposes to a direct importer.
|
|
382
|
+
/** @type {Array<{ module: string; names: 'star' | Set<string>; depth: number }>} */
|
|
382
383
|
const queue = [{ module: targetFile, names: 'star', depth: 0 }];
|
|
383
384
|
|
|
384
385
|
while (queue.length > 0) {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
canonicalizeStoryboardExecutions,
|
|
17
17
|
PYGMALION_STORYBOARD_CANONICAL_CONTROL,
|
|
18
18
|
} from './storyboard-canonical.mjs';
|
|
19
|
+
import { sendJson as sendJsonEnvelope } from './envelope.mjs';
|
|
19
20
|
|
|
20
21
|
export const PYGMALION_STORYBOARD_ENVIRONMENT_CONTROL =
|
|
21
22
|
PYGMALION_ENDPOINTS.storyboardEnvironment;
|
|
@@ -1306,6 +1307,36 @@ export function storyboardEnvironmentBootstrapSource() {
|
|
|
1306
1307
|
value: screenPreset,
|
|
1307
1308
|
});
|
|
1308
1309
|
}
|
|
1310
|
+
// A preset that fixes the clock (timers.mode 'fixed') is what the Node
|
|
1311
|
+
// capture runtime asks Playwright for. A live frame installs the same clock
|
|
1312
|
+
// here, before application modules run, so the running screen shows the
|
|
1313
|
+
// date and time its capture shows instead of the designer's wall clock.
|
|
1314
|
+
// Timers keep running, as they do under Playwright's fixed time.
|
|
1315
|
+
const timers = screenPreset ? plainRecord(screenPreset.timers) : null;
|
|
1316
|
+
const fixedNow = timers && timers.mode === 'fixed' ? timers.now : null;
|
|
1317
|
+
const fixedTime =
|
|
1318
|
+
typeof fixedNow === 'string' || typeof fixedNow === 'number'
|
|
1319
|
+
? new Date(fixedNow).getTime()
|
|
1320
|
+
: NaN;
|
|
1321
|
+
if (Number.isFinite(fixedTime)) {
|
|
1322
|
+
const RealDate = globalThis.Date ?? Date;
|
|
1323
|
+
const FixedDate = function Date(...args) {
|
|
1324
|
+
if (!new.target) return new RealDate(fixedTime).toString();
|
|
1325
|
+
return Reflect.construct(
|
|
1326
|
+
RealDate,
|
|
1327
|
+
args.length === 0 ? [fixedTime] : args,
|
|
1328
|
+
new.target === FixedDate ? RealDate : new.target,
|
|
1329
|
+
);
|
|
1330
|
+
};
|
|
1331
|
+
Object.setPrototypeOf(FixedDate, RealDate);
|
|
1332
|
+
FixedDate.prototype = RealDate.prototype;
|
|
1333
|
+
FixedDate.now = () => fixedTime;
|
|
1334
|
+
Object.defineProperty(globalThis, 'Date', {
|
|
1335
|
+
configurable: true,
|
|
1336
|
+
writable: true,
|
|
1337
|
+
value: FixedDate,
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1309
1340
|
// 'sandbox' virtualizes the whole store per document: reads of undeclared
|
|
1310
1341
|
// keys see a pristine profile and writes never reach the shared origin
|
|
1311
1342
|
// storage. Hidden preview instances run with it so a walk that persists UI
|
|
@@ -1699,12 +1730,8 @@ export function sourceUsageMiddleware(root, { sourceDirectory = 'src' } = {}) {
|
|
|
1699
1730
|
};
|
|
1700
1731
|
}
|
|
1701
1732
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
1705
|
-
response.setHeader('cache-control', 'no-store');
|
|
1706
|
-
response.end(JSON.stringify(body));
|
|
1707
|
-
}
|
|
1733
|
+
// One envelope for every endpoint; see envelope.mjs.
|
|
1734
|
+
const sendJson = sendJsonEnvelope;
|
|
1708
1735
|
|
|
1709
1736
|
export function pygmalionStoryboardPlugin(options) {
|
|
1710
1737
|
const root = path.resolve(options.root);
|
package/node/vite.mjs
CHANGED
|
@@ -344,6 +344,7 @@ export function createPygmalionVitePlugins(config) {
|
|
|
344
344
|
),
|
|
345
345
|
legacyEndpoints: project.inspect.legacyEndpoints ?? [],
|
|
346
346
|
normalizeValue: project.inspect.normalizeValue,
|
|
347
|
+
catalogFile: revisionCatalogFile,
|
|
347
348
|
sourceDirectory: project.sourceDirectory,
|
|
348
349
|
}),
|
|
349
350
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pygmalionjs/pygmalion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Code-backed DOM design sandbox and visual QA editor",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"files": [
|
|
54
54
|
"dist-lib",
|
|
55
|
+
"docs/coverage-contracts.md",
|
|
55
56
|
"docs/screen-state-contract.md",
|
|
56
57
|
"node/contract.mjs",
|
|
57
58
|
"node/contract.d.mts",
|
|
@@ -59,8 +60,10 @@
|
|
|
59
60
|
"node/automatic-motion-runtime.mjs",
|
|
60
61
|
"node/automatic-pseudo-runtime.mjs",
|
|
61
62
|
"node/design-session.mjs",
|
|
63
|
+
"node/envelope.mjs",
|
|
62
64
|
"node/dev-mirror.mjs",
|
|
63
65
|
"node/dev-view.vite.mjs",
|
|
66
|
+
"node/impact-frames.mjs",
|
|
64
67
|
"node/inspect-plugin.mjs",
|
|
65
68
|
"node/inspect.mjs",
|
|
66
69
|
"node/inspect-writeback.mjs",
|
|
@@ -99,9 +102,11 @@
|
|
|
99
102
|
"lint": "eslint .",
|
|
100
103
|
"lint:language": "node scripts/lint-language.mjs",
|
|
101
104
|
"typecheck": "tsc --noEmit",
|
|
102
|
-
"
|
|
105
|
+
"typecheck:node": "tsc -p tsconfig.node.json",
|
|
106
|
+
"prepublishOnly": "npm run lint && npm run lint:language && npm test && npm run typecheck && npm run typecheck:node && npm run build:lib",
|
|
103
107
|
"preview": "vite preview",
|
|
104
108
|
"build:types": "node scripts/emit-types.mjs",
|
|
109
|
+
"smoke:consumer": "node scripts/smoke-consumer.mjs",
|
|
105
110
|
"release": "node scripts/release.mjs"
|
|
106
111
|
},
|
|
107
112
|
"peerDependencies": {
|