@pygmalionjs/pygmalion 0.5.26 → 0.5.28

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.
@@ -1,4 +1,4 @@
1
- import { F as s, N as a, e as r, s as i, a as t } from "./FrozenRoutePreview-Df_DH4UK.js";
1
+ import { F as s, N as a, e as r, s as i, a as t } from "./FrozenRoutePreview-fEKTwThA.js";
2
2
  export {
3
3
  s as FrozenRoutePreviewView,
4
4
  a as NodeModel,
@@ -183,6 +183,11 @@ export interface DesignImportPage {
183
183
  stateKind?: string;
184
184
  /** Host-declared situation group the canvas arranges the frame into. */
185
185
  lane?: string;
186
+ /**
187
+ * Runtimes this screen exists in, by session preset id. Absent means every
188
+ * runtime — only the screens a runtime cannot reach at all need to say so.
189
+ */
190
+ runtimes?: readonly string[];
186
191
  scenarioId?: string;
187
192
  /**
188
193
  * Every scenario this screen demonstrates. Coverage is the whole set; the
@@ -301,6 +306,11 @@ export interface DesignImportInitialPage {
301
306
  state?: string;
302
307
  stateKind?: string;
303
308
  lane?: string;
309
+ /**
310
+ * Runtimes this screen exists in, by session preset id. Absent means every
311
+ * runtime — only the screens a runtime cannot reach at all need to say so.
312
+ */
313
+ runtimes?: readonly string[];
304
314
  scenarioId?: string;
305
315
  interactions?: DesignScreenInteraction[];
306
316
  environment?: StoryboardEnvironment;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * What a screen declaration has to carry to be worth trusting.
3
+ *
4
+ * A declared screen is a claim: replay these steps and you arrive at this
5
+ * screen. Nothing checked the claim. A step whose selector had moved failed
6
+ * quietly, the capture went ahead from wherever it stood, and the frame shipped
7
+ * under the declared name — a screen nobody had ever seen, filed as the one they
8
+ * meant. Ten of seventy screens in the first host were in that state, including
9
+ * two the designer had been looking at for weeks.
10
+ *
11
+ * The floor here is that a declaration must be able to fail. Reviewing the
12
+ * declarations by eye does not scale and does not survive a refactor; a screen
13
+ * that cannot fail will eventually be wrong without saying so.
14
+ *
15
+ * The second rule is subtler and matters more over time. A screen may declare
16
+ * an assertion that proves only that the application booted — every screen in
17
+ * the catalog satisfies it — which reads as evidence in review and is none. That
18
+ * shape is recognizable without knowing what a host named its shared constant:
19
+ * an assertion that nearly every other screen also declares distinguishes
20
+ * nothing. So the check compares the declarations against each other rather
21
+ * than against a list of names, and stays useful in a codebase it has never
22
+ * seen.
23
+ */
24
+ /** Reasons must say something; the surface ledger uses the same floor. */
25
+ export declare const MIN_UNVERIFIED_REASON_LENGTH = 10;
26
+ /**
27
+ * How many screens may share an assertion before it stops counting as evidence
28
+ * that you reached one of them.
29
+ *
30
+ * Expressed as a share of the catalog rather than a count, so it means the same
31
+ * thing for a catalog of twelve screens and one of three hundred.
32
+ */
33
+ export declare const GENERIC_ASSERTION_SHARE = 0.5;
34
+ export type ScreenConformanceKind =
35
+ /** Steps replay with nothing that could fail if they went elsewhere. */
36
+ 'no-arrival-evidence'
37
+ /** Everything it asserts is asserted by most of the catalog. */
38
+ | 'generic-arrival-only'
39
+ /** The host admitted the gap and said why. Informational. */
40
+ | 'unverified-declared';
41
+ export interface ScreenConformanceFinding {
42
+ severity: 'error' | 'warning' | 'info';
43
+ kind: ScreenConformanceKind;
44
+ screenId: string;
45
+ message: string;
46
+ /** Where to declare the fix, named so the reader does not have to guess. */
47
+ fix: string;
48
+ }
49
+ interface ConformanceAssertion {
50
+ selector?: string;
51
+ text?: string;
52
+ attribute?: {
53
+ name?: string;
54
+ value?: string;
55
+ };
56
+ }
57
+ export interface ConformanceScreen {
58
+ id: string;
59
+ name?: string;
60
+ /** Steps replayed before the capture. */
61
+ interactions?: readonly unknown[];
62
+ assertions?: readonly ConformanceAssertion[];
63
+ /**
64
+ * An admission that this screen's arrival is not checked, and why. Held to
65
+ * the same floor as a surface verdict's reason: "later" is not a reason.
66
+ */
67
+ arrivalUnverified?: string;
68
+ }
69
+ /**
70
+ * Reads the catalog against itself.
71
+ *
72
+ * The host owns which screens exist and what proves each one; this only refuses
73
+ * to let a screen claim something nothing could contradict.
74
+ */
75
+ export declare function validateScreenConformance(screens: readonly ConformanceScreen[], options?: {
76
+ genericShare?: number;
77
+ }): ScreenConformanceFinding[];
78
+ /**
79
+ * A test hook exists so something can aim at it. One that nothing aims at is
80
+ * not a hook, it is a comment that looks like contract — and the failure is
81
+ * silent in both directions. A hook nobody targets can be deleted by a refactor
82
+ * with no test going red; a target naming a hook that no longer exists fails
83
+ * only when that screen is next captured, which may be never.
84
+ *
85
+ * The host owns the source and hands over the hooks it found; this side owns the
86
+ * declarations and knows what they aim at. Neither could answer alone.
87
+ */
88
+ export interface HookTargetingInput {
89
+ /** Hooks the product declares, as the host's own scan found them. */
90
+ hooks: readonly string[];
91
+ /** Selectors the declarations aim at — assertions, steps, axis requirements. */
92
+ targets: readonly string[];
93
+ /**
94
+ * Hooks answered some other way: named in a scenario test, or recorded as
95
+ * out of scope. Absent means nothing is excused.
96
+ */
97
+ answered?: readonly string[];
98
+ }
99
+ export type HookTargetingKind = 'hook-untargeted' | 'target-missing';
100
+ export interface HookTargetingFinding {
101
+ severity: 'error';
102
+ kind: HookTargetingKind;
103
+ hook: string;
104
+ message: string;
105
+ fix: string;
106
+ }
107
+ /** Pulls hook names out of a selector, so `[data-testid="x"][y]` reads as `x`. */
108
+ export declare function hooksInSelector(selector: string): string[];
109
+ export declare function validateHookTargeting(input: HookTargetingInput): HookTargetingFinding[];
110
+ /**
111
+ * Frames that should have been an axis.
112
+ *
113
+ * A frame is for a different screen in a flow; an axis is for the same screen
114
+ * held a different way. Hosts reach for a frame either way, because a frame is
115
+ * the thing they already know how to declare — and then the storyboard grows
116
+ * nodes that are not places, and the reader cannot tell a step from a variation.
117
+ *
118
+ * The signal is a group of screens that agree on route, flow, and scenario and
119
+ * differ only in the state they name, none of which says it is a branch of the
120
+ * others. That is one screen wearing several states, which is what the axis and
121
+ * the screen-states control exist for.
122
+ */
123
+ export interface StateGroupScreen {
124
+ id: string;
125
+ name?: string;
126
+ route?: string;
127
+ flow?: string;
128
+ scenario?: string;
129
+ state?: string;
130
+ /** Set when the host already declares this as a branch of its group. */
131
+ stateKind?: string;
132
+ }
133
+ export interface StateGroupFinding {
134
+ severity: 'warning';
135
+ kind: 'axis-candidate';
136
+ screenIds: readonly string[];
137
+ message: string;
138
+ fix: string;
139
+ }
140
+ export declare function detectStateGroupCandidates(screens: readonly StateGroupScreen[]): StateGroupFinding[];
141
+ /**
142
+ * Routes the catalog only ever shows working.
143
+ *
144
+ * Empty, slow, and failed are states a product spends real time in and a
145
+ * catalog almost never holds, because reaching one is not something a designer
146
+ * can click — so they go undrawn until a customer finds them. A route whose
147
+ * every screen boots on the default path is not proof that nothing else exists
148
+ * there; it is the absence of the question.
149
+ *
150
+ * This counts rather than judges: the host decides which routes deserve the
151
+ * other states, and only the host knows which requests a route even makes.
152
+ */
153
+ export interface ConditionCoverageScreen {
154
+ id: string;
155
+ route?: string;
156
+ /** Boot conditions this screen declares (network outcomes, seeded storage). */
157
+ conditions?: number;
158
+ }
159
+ export interface ConditionCoverageRoute {
160
+ route: string;
161
+ screens: number;
162
+ /** Screens declaring at least one boot condition. */
163
+ conditioned: number;
164
+ }
165
+ export declare function summarizeConditionCoverage(screens: readonly ConditionCoverageScreen[]): ConditionCoverageRoute[];
166
+ export {};
@@ -16,6 +16,11 @@ import type { StoryboardEnvironment } from './designImport';
16
16
  * frames captured under the old one.
17
17
  */
18
18
  export interface SessionPresetDef {
19
+ /**
20
+ * Stable key a screen names to say it exists in this runtime. Labels are for
21
+ * reading and change; this does not.
22
+ */
23
+ id: string;
19
24
  /** Button text. The panel column is narrow, so keep it short. */
20
25
  label: string;
21
26
  /**
@@ -44,3 +49,16 @@ export declare function getSelectedSessionPreset(): number | null;
44
49
  */
45
50
  export declare function selectSessionPreset(index: number | null): void;
46
51
  export declare function subscribeSessionPresets(listener: () => void): () => void;
52
+ /**
53
+ * Whether a screen belongs in the runtime currently selected.
54
+ *
55
+ * A screen that names no runtime exists in all of them — most do, and a product
56
+ * that ships one build should not have to say so 75 times. Naming runtimes is
57
+ * for the screens that only exist in some: an EMR-linked build has surfaces the
58
+ * standalone one cannot reach at all, and showing them side by side reads as a
59
+ * catalog of screens that do not exist.
60
+ *
61
+ * With no preset selected everything shows, because "the product's own default"
62
+ * is not a claim about which runtime is running.
63
+ */
64
+ export declare function isInSelectedSessionPreset(runtimes: readonly string[] | undefined): boolean;
@@ -187,6 +187,12 @@ export interface PageModel {
187
187
  stateKind?: string;
188
188
  /** Host-declared situation group the frame is arranged into (see frameLanes). */
189
189
  lane?: string;
190
+ /**
191
+ * Runtimes this screen exists in, by session preset id. Absent means every
192
+ * runtime — most screens are the same product either way, and only the ones
193
+ * a runtime cannot reach at all need to say so.
194
+ */
195
+ runtimes?: readonly string[];
190
196
  scenarioId?: string;
191
197
  /** Every scenario the screen demonstrates; coverage counts this set. */
192
198
  scenarioIds?: readonly string[];
@@ -341,6 +347,14 @@ export declare class EditorStore {
341
347
  /** The group member holding the group's canvas slot, or null for non-groups. */
342
348
  shownScreenStateId(scenario: string, canvas: string): string | null;
343
349
  /** False only for state variants folded into the Screen states control. */
350
+ /**
351
+ * Whether the frame belongs in the runtime the panel is set to.
352
+ *
353
+ * An EMR-linked build has surfaces the standalone one cannot reach at all, so
354
+ * showing every frame under both runtimes reads as a catalog of screens that
355
+ * do not exist. A frame that names no runtime exists in all of them.
356
+ */
357
+ isInSelectedRuntime(page: PageModel): boolean;
344
358
  isScreenStateShown(page: PageModel): boolean;
345
359
  /** Swaps a folded variant into its group's canvas slot; the camera stays. */
346
360
  private revealScreenState;
@@ -476,6 +490,7 @@ export declare class EditorStore {
476
490
  state?: string;
477
491
  stateKind?: string;
478
492
  lane?: string;
493
+ runtimes?: readonly string[];
479
494
  scenarioId?: string;
480
495
  scenarioIds?: readonly string[];
481
496
  interactions?: DesignScreenInteraction[];
@@ -82,6 +82,8 @@ import { type SessionPresetDef } from './editor/sessionPresets';
82
82
  export type { ViewportPresetDef } from './editor/viewportPresets';
83
83
  export { getSelectedSessionPreset, getSessionPresets, selectSessionPreset, setSessionPresets, subscribeSessionPresets, } from './editor/sessionPresets';
84
84
  export type { SessionPresetDef } from './editor/sessionPresets';
85
+ export { GENERIC_ASSERTION_SHARE, MIN_UNVERIFIED_REASON_LENGTH, detectStateGroupCandidates, hooksInSelector, summarizeConditionCoverage, validateHookTargeting, validateScreenConformance, } from './editor/screenConformance';
86
+ export type { ConditionCoverageRoute, ConditionCoverageScreen, ConformanceScreen, HookTargetingFinding, HookTargetingInput, HookTargetingKind, ScreenConformanceFinding, ScreenConformanceKind, StateGroupFinding, StateGroupScreen, } from './editor/screenConformance';
85
87
  export { clearPreviewEnvironmentControlValues, getPreviewEnvironmentControlValue, getPreviewEnvironmentControls, getPreviewEnvironmentOverride, setPreviewEnvironmentControlValue, setPreviewEnvironmentControls, subscribePreviewEnvironmentControls, } from './editor/previewEnvironmentControls';
86
88
  export type { PreviewEnvironmentControlDef, PreviewEnvironmentControlOptionDef, } from './editor/previewEnvironmentControls';
87
89
  export { createDesignImportController, createDesignScreenCollection, DESIGN_IMPORT_KINDS, expandDesignScreenCasesViewports, expandDesignScreenCollectionViewports, mergeScreenPresets, } from './editor/designImport';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.5.26",
3
+ "version": "0.5.28",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {