browser-commander 0.2.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 (82) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/release.yml +296 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.jscpd.json +20 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +10 -0
  8. package/CHANGELOG.md +32 -0
  9. package/LICENSE +24 -0
  10. package/README.md +320 -0
  11. package/bunfig.toml +3 -0
  12. package/deno.json +7 -0
  13. package/eslint.config.js +125 -0
  14. package/examples/react-test-app/index.html +25 -0
  15. package/examples/react-test-app/package.json +19 -0
  16. package/examples/react-test-app/src/App.jsx +473 -0
  17. package/examples/react-test-app/src/main.jsx +10 -0
  18. package/examples/react-test-app/src/styles.css +323 -0
  19. package/examples/react-test-app/vite.config.js +9 -0
  20. package/package.json +89 -0
  21. package/scripts/changeset-version.mjs +38 -0
  22. package/scripts/create-github-release.mjs +93 -0
  23. package/scripts/create-manual-changeset.mjs +86 -0
  24. package/scripts/format-github-release.mjs +83 -0
  25. package/scripts/format-release-notes.mjs +216 -0
  26. package/scripts/instant-version-bump.mjs +121 -0
  27. package/scripts/merge-changesets.mjs +260 -0
  28. package/scripts/publish-to-npm.mjs +126 -0
  29. package/scripts/setup-npm.mjs +37 -0
  30. package/scripts/validate-changeset.mjs +262 -0
  31. package/scripts/version-and-commit.mjs +237 -0
  32. package/src/ARCHITECTURE.md +270 -0
  33. package/src/README.md +517 -0
  34. package/src/bindings.js +298 -0
  35. package/src/browser/launcher.js +93 -0
  36. package/src/browser/navigation.js +513 -0
  37. package/src/core/constants.js +24 -0
  38. package/src/core/engine-adapter.js +466 -0
  39. package/src/core/engine-detection.js +49 -0
  40. package/src/core/logger.js +21 -0
  41. package/src/core/navigation-manager.js +503 -0
  42. package/src/core/navigation-safety.js +160 -0
  43. package/src/core/network-tracker.js +373 -0
  44. package/src/core/page-session.js +299 -0
  45. package/src/core/page-trigger-manager.js +564 -0
  46. package/src/core/preferences.js +46 -0
  47. package/src/elements/content.js +197 -0
  48. package/src/elements/locators.js +243 -0
  49. package/src/elements/selectors.js +360 -0
  50. package/src/elements/visibility.js +166 -0
  51. package/src/exports.js +121 -0
  52. package/src/factory.js +192 -0
  53. package/src/high-level/universal-logic.js +206 -0
  54. package/src/index.js +17 -0
  55. package/src/interactions/click.js +684 -0
  56. package/src/interactions/fill.js +383 -0
  57. package/src/interactions/scroll.js +341 -0
  58. package/src/utilities/url.js +33 -0
  59. package/src/utilities/wait.js +135 -0
  60. package/tests/e2e/playwright.e2e.test.js +442 -0
  61. package/tests/e2e/puppeteer.e2e.test.js +408 -0
  62. package/tests/helpers/mocks.js +542 -0
  63. package/tests/unit/bindings.test.js +218 -0
  64. package/tests/unit/browser/navigation.test.js +345 -0
  65. package/tests/unit/core/constants.test.js +72 -0
  66. package/tests/unit/core/engine-adapter.test.js +170 -0
  67. package/tests/unit/core/engine-detection.test.js +81 -0
  68. package/tests/unit/core/logger.test.js +80 -0
  69. package/tests/unit/core/navigation-safety.test.js +202 -0
  70. package/tests/unit/core/network-tracker.test.js +198 -0
  71. package/tests/unit/core/page-trigger-manager.test.js +358 -0
  72. package/tests/unit/elements/content.test.js +318 -0
  73. package/tests/unit/elements/locators.test.js +236 -0
  74. package/tests/unit/elements/selectors.test.js +302 -0
  75. package/tests/unit/elements/visibility.test.js +234 -0
  76. package/tests/unit/factory.test.js +174 -0
  77. package/tests/unit/high-level/universal-logic.test.js +299 -0
  78. package/tests/unit/interactions/click.test.js +340 -0
  79. package/tests/unit/interactions/fill.test.js +378 -0
  80. package/tests/unit/interactions/scroll.test.js +330 -0
  81. package/tests/unit/utilities/url.test.js +63 -0
  82. package/tests/unit/utilities/wait.test.js +207 -0
@@ -0,0 +1,270 @@
1
+ # Browser Commander - Architecture
2
+
3
+ A universal browser automation library that supports both Playwright and Puppeteer with a unified API.
4
+
5
+ ## Core Concept
6
+
7
+ Browser Commander models browser automation as a **state machine** with page triggers that automatically start/stop based on URL conditions.
8
+
9
+ ```
10
+ ┌─────────────────┐ ┌─────────────────┐
11
+ │ │ navigation start │ │
12
+ │ WORKING STATE │ ─────────────────► │ LOADING STATE │
13
+ │ (action runs) │ │ (wait only) │
14
+ │ │ ◄───────────────── │ │
15
+ └─────────────────┘ page ready └─────────────────┘
16
+ ```
17
+
18
+ ## Directory Structure
19
+
20
+ ```
21
+ browser-commander/
22
+ ├── index.js # Main entry, exports, makeBrowserCommander()
23
+ ├── ARCHITECTURE.md # This file
24
+ ├── README.md # API documentation and usage guide
25
+
26
+ ├── core/ # Core infrastructure
27
+ │ ├── page-trigger-manager.js # Trigger lifecycle management
28
+ │ ├── navigation-manager.js # URL changes, abort signals
29
+ │ ├── network-tracker.js # HTTP request tracking
30
+ │ ├── page-session.js # Per-page context (legacy)
31
+ │ ├── navigation-safety.js # Handle navigation errors
32
+ │ ├── constants.js # CHROME_ARGS, TIMING
33
+ │ ├── logger.js # Logging utilities
34
+ │ ├── engine-detection.js # Detect Playwright/Puppeteer
35
+ │ └── preferences.js # Chrome preferences
36
+
37
+ ├── browser/ # Browser lifecycle
38
+ │ ├── launcher.js # Browser launch (launchBrowser)
39
+ │ └── navigation.js # goto, waitForNavigation
40
+
41
+ ├── elements/ # Element operations
42
+ │ ├── locators.js # Element location strategies
43
+ │ ├── selectors.js # querySelector, findByText
44
+ │ ├── visibility.js # isVisible, isEnabled
45
+ │ └── content.js # textContent, getAttribute
46
+
47
+ ├── interactions/ # User interactions
48
+ │ ├── click.js # clickButton, clickElement
49
+ │ ├── fill.js # fillTextArea
50
+ │ └── scroll.js # scrollIntoView
51
+
52
+ ├── utilities/ # Utility functions
53
+ │ ├── wait.js # wait(), evaluate()
54
+ │ └── url.js # getUrl
55
+
56
+ └── high-level/ # High-level automation patterns
57
+ └── universal-logic.js # Reusable patterns
58
+ ```
59
+
60
+ ## Component Relationships
61
+
62
+ ```
63
+ ┌─────────────────────────────────────────────────────────────────────────┐
64
+ │ makeBrowserCommander() │
65
+ │ (index.js) │
66
+ │ │
67
+ │ Binds all modules together and provides unified API │
68
+ └───────────────────────────────┬─────────────────────────────────────────┘
69
+
70
+ ┌───────────────────────┼───────────────────────┐
71
+ │ │ │
72
+ ▼ ▼ ▼
73
+ ┌───────────────┐ ┌───────────────┐ ┌───────────────────┐
74
+ │NetworkTracker │ │NavigationMgr │ │PageTriggerManager │
75
+ │ │ │ │ │ │
76
+ │ • Track HTTP │◄─────│ • URL changes │◄─────│ • Register │
77
+ │ • Wait idle │ │ • Abort sigs │ │ triggers │
78
+ │ • 30s timeout │ │ • Events │ │ • Start/stop │
79
+ │ │ │ │ │ lifecycle │
80
+ └───────────────┘ └───────────────┘ └───────────────────┘
81
+ ▲ ▲ ▲
82
+ │ │ │
83
+ │ ┌────────┴────────┐ │
84
+ │ │ │ │
85
+ │ ▼ ▼ │
86
+ │ ┌──────────────┐ ┌──────────────┐ │
87
+ │ │ browser/ │ │ interactions/│ │
88
+ │ │ navigation.js│ │ click.js │ │
89
+ │ │ launcher.js │ │ fill.js │ │
90
+ │ └──────────────┘ └──────────────┘ │
91
+ │ │
92
+ └──────────────────────────────────────────────┘
93
+ All use NetworkTracker for idle detection
94
+ ```
95
+
96
+ ## Key Design Patterns
97
+
98
+ ### 1. Options Object Pattern
99
+
100
+ All functions accept options as an object for maximum flexibility:
101
+
102
+ ```javascript
103
+ await commander.clickButton({
104
+ selector: 'button.submit',
105
+ scrollIntoView: true,
106
+ waitForNavigation: true,
107
+ timeout: 30000,
108
+ });
109
+ ```
110
+
111
+ ### 2. Page Trigger Pattern
112
+
113
+ Declarative page handlers that automatically manage lifecycle:
114
+
115
+ ```javascript
116
+ commander.pageTrigger({
117
+ name: 'checkout-handler',
118
+ condition: makeUrlCondition('*/checkout*'),
119
+ action: async (ctx) => {
120
+ // ctx.checkStopped() - check if should stop
121
+ // ctx.commander - wrapped commander (throws on navigation)
122
+ // ctx.onCleanup(fn) - register cleanup
123
+ await ctx.commander.fillTextArea({ selector: 'input', text: 'value' });
124
+ },
125
+ });
126
+ ```
127
+
128
+ ### 3. Safe Iteration Pattern
129
+
130
+ `ctx.forEach()` automatically checks for navigation between items:
131
+
132
+ ```javascript
133
+ await ctx.forEach(items, async (item) => {
134
+ await ctx.commander.click({ selector: item.selector });
135
+ });
136
+ ```
137
+
138
+ ### 4. Engine Abstraction
139
+
140
+ Unified API works with both Playwright and Puppeteer:
141
+
142
+ ```javascript
143
+ const { browser, page } = await launchBrowser({ engine: 'playwright' });
144
+ // or
145
+ const { browser, page } = await launchBrowser({ engine: 'puppeteer' });
146
+ ```
147
+
148
+ ## State Machine Details
149
+
150
+ ### LOADING STATE
151
+
152
+ When navigation is detected:
153
+
154
+ 1. Signal current action to stop (AbortController.abort())
155
+ 2. Wait for action cleanup (max 10 seconds)
156
+ 3. Run cleanup callbacks (ctx.onCleanup)
157
+ 4. Wait for URL stabilization (no redirects for 1s)
158
+ 5. Wait for network idle (30s no HTTP requests)
159
+
160
+ ### WORKING STATE
161
+
162
+ When page is ready:
163
+
164
+ 1. Find matching trigger (condition check)
165
+ 2. Create action context with wrapped commander
166
+ 3. Execute action
167
+ 4. Handle completion or errors
168
+
169
+ ## Network Idle Detection
170
+
171
+ The library waits for **30 seconds** of zero pending HTTP requests:
172
+
173
+ ```javascript
174
+ // Ensures:
175
+ // - All lazy-loaded content fetched
176
+ // - All analytics complete
177
+ // - All async JavaScript executed
178
+ // - SPAs fully hydrated
179
+ ```
180
+
181
+ ## Error Handling
182
+
183
+ ### ActionStoppedError
184
+
185
+ Thrown when navigation interrupts an action:
186
+
187
+ ```javascript
188
+ try {
189
+ await ctx.commander.clickButton({ selector: 'button' });
190
+ } catch (error) {
191
+ if (commander.isActionStoppedError(error)) {
192
+ // Navigation happened - action was stopped
193
+ return;
194
+ }
195
+ throw error;
196
+ }
197
+ ```
198
+
199
+ ### Navigation Errors
200
+
201
+ Handled gracefully with automatic recovery:
202
+
203
+ ```javascript
204
+ if (isNavigationError(error)) {
205
+ // Page navigated during operation - normal behavior
206
+ }
207
+ ```
208
+
209
+ ## URL Condition Helpers
210
+
211
+ Multiple ways to define URL conditions:
212
+
213
+ ```javascript
214
+ // Exact match
215
+ makeUrlCondition('https://example.com/page');
216
+
217
+ // Wildcards
218
+ makeUrlCondition('*checkout*');
219
+
220
+ // Route patterns
221
+ makeUrlCondition('/vacancy/:id');
222
+
223
+ // Regex
224
+ makeUrlCondition(/\/product\/\d+/);
225
+
226
+ // Custom function
227
+ makeUrlCondition((url, ctx) => url.includes('/admin'));
228
+
229
+ // Combine
230
+ allConditions(condition1, condition2);
231
+ anyCondition(condition1, condition2);
232
+ notCondition(condition);
233
+ ```
234
+
235
+ ## Design Principles Applied
236
+
237
+ ### Modularity
238
+
239
+ - Each module has single responsibility
240
+ - Clear boundaries between components
241
+ - Easy to test in isolation
242
+
243
+ ### Abstraction
244
+
245
+ - Unified API hides engine differences
246
+ - High-level patterns for common tasks
247
+
248
+ ### Stable Contracts
249
+
250
+ - Options object pattern prevents breaking changes
251
+ - Adding new options is backward compatible
252
+
253
+ ### Separation of Concerns
254
+
255
+ - Navigation tracking separate from page operations
256
+ - Network tracking separate from element interactions
257
+ - Trigger management separate from action execution
258
+
259
+ ### Composition Over Complexity
260
+
261
+ - Complex operations built from simple primitives
262
+ - `clickButton` uses `scrollIntoView`, `click`, verification
263
+
264
+ ## Future Improvements
265
+
266
+ See GitHub issues:
267
+
268
+ - [#94](https://github.com/konard/hh-job-application-automation/issues/94) - Engine adapter interface
269
+ - [#95](https://github.com/konard/hh-job-application-automation/issues/95) - Split large functions
270
+ - [#96](https://github.com/konard/hh-job-application-automation/issues/96) - withNavigationSafety wrapper