decisium 0.0.0-stage → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrei Letenkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,34 @@
1
- # Temporary Holding Version
1
+ # Decisium
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Semantic UI testing with decision models. Plain-English Gherkin Steps run in a real browser (Playwright): Jev, a decision
4
+ model, picks among the actions the page actually offers, and code decides pass/fail. No selectors, no LLM, no prompts.
5
+ Record/replay cassettes make CI deterministic with zero model calls.
6
+
7
+ **Requires Jev:** live runs call TypeSafe's Jev model through its API (`TYPESAFE_API_KEY`). Replays (`--record none`)
8
+ make no calls and need no key. Decisium's code is MIT; Jev is TypeSafe's hosted service under its own terms.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ npm install --save-dev decisium
14
+ npx playwright install chromium
15
+ export TYPESAFE_API_KEY=... # Jev; not needed to replay with --record none
16
+ npx decisium doctor my.feature
17
+ npx decisium run my.feature
18
+ ```
19
+
20
+ `npx decisium` prints the full usage.
21
+
22
+ ## Raw cucumber-js
23
+
24
+ ```js
25
+ // cucumber.mjs
26
+ export default {
27
+ import: ["node_modules/decisium/dist/steps.js"],
28
+ format: ["decisium/formatter"],
29
+ worldParameters: { decisium: { record: "new" } },
30
+ };
31
+ ```
32
+
33
+ then `npx cucumber-js my.feature`. Options: [docs/configuration.md](docs/configuration.md).
34
+ Writing steps: [docs/cookbook.md](docs/cookbook.md). Agent skill: `skills/decisium/SKILL.md`.
@@ -0,0 +1,98 @@
1
+ import { type BrowserContext, type BrowserContextOptions, type Page } from "playwright";
2
+ import { type NetworkReplay } from "./network.ts";
3
+ import { type Observation, type Verb } from "./observation.ts";
4
+ export interface Action {
5
+ verb: Verb | "SCROLL" | "SCROLL_UP" | "WAIT" | "ENTER" | "PRESS" | "ACCEPT" | "DISMISS" | "GOTO" | "BACK" | "FORWARD" | "RELOAD" | "CLOSE_TAB" | "SWITCH_TAB" | "HOVER" | "UPLOAD" | "DBLCLICK" | "RIGHTCLICK" | "DRAG" | "PASTE";
6
+ ref?: string;
7
+ to?: string;
8
+ value?: string;
9
+ }
10
+ export declare class Browser {
11
+ private browser;
12
+ /** Public for client step definitions (`this.context` on the World, task 0048). */
13
+ readonly context: BrowserContext;
14
+ /** The tab Steps act on. A tab the page opens (a popup, a target=_blank link) becomes it at once (task 0041). Public as `this.page`. */
15
+ page: Page;
16
+ /** `@replay-network` requests missing from the recording, until the Step that made them fails on them (task 0048). */
17
+ readonly networkMisses: string[];
18
+ private start;
19
+ private inflight;
20
+ private last?;
21
+ /**
22
+ * Browser dialogs (task 0040). While one is open the page is blocked (snapshots, evaluate, screenshots and clicks
23
+ * hang), so the Observation is the page as it was under it; `opened` resolves when one opens.
24
+ */
25
+ private open?;
26
+ private dialog?;
27
+ private under?;
28
+ private opened;
29
+ private signal;
30
+ readonly consoleErrors: string[];
31
+ /** Secret placeholders (`$NAME` -> value): typed for the token, masked back to it in every Observation (task 0030). */
32
+ private secrets;
33
+ /** A secret was typed: the Playwright trace stores typed text unmasked, so close() keeps none. */
34
+ typedSecret: boolean;
35
+ /** Task 0044: UPLOAD paths resolve against `files` (the feature's folder); downloads are saved to `downloads`. */
36
+ private files;
37
+ private downloads?;
38
+ private download?;
39
+ /** Task 0062: a headed browser may share the OS clipboard; only text copied since the open is shown. */
40
+ private clipboardAtOpen?;
41
+ private readClipboard;
42
+ private mask;
43
+ private constructor();
44
+ /** Saves each download; counted in flight until saved, so the action that started it settles after it. */
45
+ private watchDownloads;
46
+ private rearm;
47
+ /** `p`, or undefined once a dialog is open: the page is blocked until it is answered. */
48
+ private unblocked;
49
+ /** Owns per-Action-step cleanup, including thrown errors. `execute` keeps cleanup actions in the runtime's trace. */
50
+ actionStep<T>(run: () => Promise<T>, log?: (...data: any[]) => void, execute?: (a: Action) => Promise<void>): Promise<T>;
51
+ /**
52
+ * `trace`: record a Playwright trace, saved by close(tracePath). `secrets`: placeholder token -> value. `files`: the
53
+ * folder UPLOAD paths are relative to; `downloads`: where downloads are saved (none: not saved, not readable).
54
+ * `context`: options that must exist before the page opens (tags, task 0048); `network`: `@replay-network`.
55
+ */
56
+ static open(url: string, headed?: boolean, trace?: boolean, secrets?: Record<string, string>, files?: string, downloads?: string, context?: BrowserContextOptions, network?: NetworkReplay): Promise<Browser>;
57
+ /**
58
+ * Waits until no request is in flight, no work is due soon (see __soon: lazily loaded code runs idle callbacks and
59
+ * short timers after its scripts arrive, before it renders) and the DOM has not changed for QUIET_MS (capped).
60
+ */
61
+ private settle;
62
+ observe(): Promise<Observation>;
63
+ private stamp?;
64
+ /** The DOM changed since the latest Observation (task 0066): the page moved on its own before an action. */
65
+ stale(): Promise<boolean>;
66
+ private read;
67
+ /** Collapsed controls (`aria-expanded=false` + `aria-controls`, closed `<details>`) by name -> hidden entries they hold. */
68
+ private collapsed;
69
+ /** `title` attributes of visible elements by name, where the title says more than the name. */
70
+ private titles;
71
+ /**
72
+ * Facts by ref, for elements `factsIn` picks in any frame (open shadow roots included): one DOM read per frame, then
73
+ * one element snapshot per match. Run before the page snapshot: an element snapshot invalidates older refs, and the
74
+ * page snapshot after it reuses the refs it gave. Reading per ref instead took ~2 s per Observation on Wikipedia.
75
+ */
76
+ private facts;
77
+ /**
78
+ * Refs of links marked aria-current (the current page or tab in a navigation). The hrefs of aria-current links pick
79
+ * the candidates; each candidate is then read by ref, since links that share an href ("#") can differ.
80
+ */
81
+ private currentLinks;
82
+ /**
83
+ * Executes one action and checks its effect where the effect is deterministic (TYPE/SELECT). An action that opens a
84
+ * dialog returns once it is open (its effect check is skipped); ACCEPT/DISMISS answer it.
85
+ */
86
+ execute(action: Action): Promise<void>;
87
+ private perform;
88
+ /**
89
+ * Waits until the Observation differs from `since` (true), the timeout passes, or the page cannot change on its
90
+ * own any more (false): no request, timer, animation frame or socket pending on two polls in a row.
91
+ */
92
+ waitForChange(since: string, timeoutMs?: number): Promise<boolean>;
93
+ /** True when nothing pending in the page can change it without user input. */
94
+ private idle;
95
+ screenshot(path: string): Promise<unknown>;
96
+ /** `tracePath`: where to save the Playwright trace started by open(..., trace); omitted, or a secret typed = discard it. */
97
+ close(tracePath?: string): Promise<void>;
98
+ }