obsidian-integration-testing 1.1.1 → 1.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 (81) hide show
  1. package/README.md +254 -13
  2. package/dist/lib/cjs/context-id.cjs +1 -1
  3. package/dist/lib/cjs/context-id.d.cts +1 -1
  4. package/dist/lib/cjs/enable-plugin.cjs +54 -0
  5. package/dist/lib/cjs/enable-plugin.d.cts +44 -0
  6. package/dist/lib/cjs/exec.cjs +4 -4
  7. package/dist/lib/cjs/exec.d.cts +8 -8
  8. package/dist/lib/cjs/function-expression.cjs +1 -1
  9. package/dist/lib/cjs/function-expression.d.cts +1 -1
  10. package/dist/lib/cjs/index.cjs +13 -1
  11. package/dist/lib/cjs/index.d.cts +9 -1
  12. package/dist/lib/cjs/integration-global-setup.cjs +19 -6
  13. package/dist/lib/cjs/integration-global-setup.d.cts +1 -6
  14. package/dist/lib/cjs/json-with-functions.cjs +1 -1
  15. package/dist/lib/cjs/json-with-functions.d.cts +1 -1
  16. package/dist/lib/cjs/noop.cjs +1 -1
  17. package/dist/lib/cjs/noop.d.cts +1 -1
  18. package/dist/lib/cjs/obsidian-cli.cjs +10 -76
  19. package/dist/lib/cjs/obsidian-cli.d.cts +9 -3
  20. package/dist/lib/cjs/obsidian-config.cjs +1 -1
  21. package/dist/lib/cjs/obsidian-config.d.cts +1 -1
  22. package/dist/lib/cjs/temp-vault.cjs +47 -5
  23. package/dist/lib/cjs/temp-vault.d.cts +19 -1
  24. package/dist/lib/cjs/transport-appium.cjs +232 -0
  25. package/dist/lib/cjs/transport-appium.d.cts +164 -0
  26. package/dist/lib/cjs/transport-desktop-cdp.cjs +390 -0
  27. package/dist/lib/cjs/transport-desktop-cdp.d.cts +158 -0
  28. package/dist/lib/cjs/transport-desktop-cli.cjs +221 -0
  29. package/dist/lib/cjs/transport-desktop-cli.d.cts +65 -0
  30. package/dist/lib/cjs/transport-factory.cjs +94 -0
  31. package/dist/lib/cjs/transport-factory.d.cts +25 -0
  32. package/dist/lib/cjs/transport-options.cjs +17 -0
  33. package/dist/lib/cjs/transport-options.d.cts +100 -0
  34. package/dist/lib/cjs/transport.cjs +17 -0
  35. package/dist/lib/cjs/transport.d.cts +86 -0
  36. package/dist/lib/cjs/type-guards.cjs +1 -1
  37. package/dist/lib/cjs/type-guards.d.cts +1 -1
  38. package/dist/lib/cjs/vault-registry.cjs +7 -56
  39. package/dist/lib/cjs/vault-registry.d.cts +3 -5
  40. package/dist/lib/esm/context-id.d.mts +1 -1
  41. package/dist/lib/esm/context-id.mjs +1 -1
  42. package/dist/lib/esm/enable-plugin.d.mts +44 -0
  43. package/dist/lib/esm/enable-plugin.mjs +30 -0
  44. package/dist/lib/esm/exec.d.mts +8 -8
  45. package/dist/lib/esm/exec.mjs +4 -4
  46. package/dist/lib/esm/function-expression.d.mts +1 -1
  47. package/dist/lib/esm/function-expression.mjs +1 -1
  48. package/dist/lib/esm/index.d.mts +9 -1
  49. package/dist/lib/esm/index.mjs +9 -1
  50. package/dist/lib/esm/integration-global-setup.d.mts +1 -6
  51. package/dist/lib/esm/integration-global-setup.mjs +19 -6
  52. package/dist/lib/esm/json-with-functions.d.mts +1 -1
  53. package/dist/lib/esm/json-with-functions.mjs +1 -1
  54. package/dist/lib/esm/noop.d.mts +1 -1
  55. package/dist/lib/esm/noop.mjs +1 -1
  56. package/dist/lib/esm/obsidian-cli.d.mts +9 -3
  57. package/dist/lib/esm/obsidian-cli.mjs +10 -80
  58. package/dist/lib/esm/obsidian-config.d.mts +1 -1
  59. package/dist/lib/esm/obsidian-config.mjs +1 -1
  60. package/dist/lib/esm/temp-vault.d.mts +19 -1
  61. package/dist/lib/esm/temp-vault.mjs +54 -7
  62. package/dist/lib/esm/transport-appium.d.mts +164 -0
  63. package/dist/lib/esm/transport-appium.mjs +208 -0
  64. package/dist/lib/esm/transport-desktop-cdp.d.mts +158 -0
  65. package/dist/lib/esm/transport-desktop-cdp.mjs +356 -0
  66. package/dist/lib/esm/transport-desktop-cli.d.mts +65 -0
  67. package/dist/lib/esm/transport-desktop-cli.mjs +190 -0
  68. package/dist/lib/esm/transport-factory.d.mts +25 -0
  69. package/dist/lib/esm/transport-factory.mjs +69 -0
  70. package/dist/lib/esm/transport-options.d.mts +100 -0
  71. package/dist/lib/esm/transport-options.mjs +1 -0
  72. package/dist/lib/esm/transport.d.mts +86 -0
  73. package/dist/lib/esm/transport.mjs +1 -0
  74. package/dist/lib/esm/type-guards.d.mts +1 -1
  75. package/dist/lib/esm/type-guards.mjs +1 -1
  76. package/dist/lib/esm/vault-registry.d.mts +3 -5
  77. package/dist/lib/esm/vault-registry.mjs +7 -56
  78. package/dist/obsidian-integration-testing-1.2.0.tgz +0 -0
  79. package/package.json +16 -2
  80. package/scripts/postinstall.mjs +29 -0
  81. package/dist/obsidian-integration-testing-1.1.1.tgz +0 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Appium transport — evaluates expressions inside Obsidian Mobile via WebView
5
+ * JavaScript injection. Manages vault lifecycle via localStorage and file push.
6
+ *
7
+ * Configured via `environmentOptions.obsidianTransport` in vitest config:
8
+ *
9
+ * ```typescript
10
+ * // vitest.config.ts
11
+ * environmentOptions: {
12
+ * obsidianTransport: {
13
+ * type: 'obsidian-android-appium',
14
+ * appiumUrl: 'http://localhost:4723',
15
+ * deviceId: 'emulator-5554',
16
+ * },
17
+ * }
18
+ * ```
19
+ *
20
+ * For BrowserStack, set `appiumUrl` to the BrowserStack hub URL
21
+ * — the transport itself is hub-agnostic.
22
+ *
23
+ * ## How vault registration works on mobile
24
+ *
25
+ * Obsidian Mobile stores its vault registry in the WebView's `localStorage`:
26
+ *
27
+ * - `mobile-external-vaults` — JSON array of registered vault paths
28
+ * - `mobile-selected-vault` — the currently active vault path
29
+ * - `enable-plugin-<vaultPath>` — `"true"` to enable the plugin system for the vault
30
+ *
31
+ * To register a vault programmatically (without UI interaction):
32
+ *
33
+ * 1. Push vault files to the device (e.g. `/sdcard/Documents/<Name>/.obsidian/app.json`)
34
+ * 2. Switch to `WEBVIEW_md.obsidian` context
35
+ * 3. Set the localStorage entries
36
+ * 4. Call `location.reload()` — Obsidian re-reads localStorage and opens the vault
37
+ *
38
+ * This avoids the onboarding flow entirely.
39
+ */
40
+ import type { Browser } from 'webdriverio';
41
+ import type { ObsidianTransport, TransportEvalOptions } from './transport.mjs';
42
+ /**
43
+ * Configuration for the Appium transport.
44
+ */
45
+ export interface AppiumTransportConfig {
46
+ /**
47
+ * App package (Android) or bundle ID (iOS).
48
+ * Defaults to `'md.obsidian'`.
49
+ */
50
+ appId?: string;
51
+ /**
52
+ * The Appium browser/driver instance.
53
+ * Created by the consumer via e.g. WebDriverIO's `remote()`.
54
+ */
55
+ browser: Browser;
56
+ /**
57
+ * Target platform. Determines WebView context naming and device file paths.
58
+ */
59
+ platform: 'android' | 'ios';
60
+ /**
61
+ * Base path on the device where Obsidian stores vaults.
62
+ *
63
+ * Defaults:
64
+ * - Android: `/sdcard/Documents/`
65
+ * - iOS: `@md.obsidian:documents/`
66
+ */
67
+ vaultBasePath?: string;
68
+ }
69
+ /**
70
+ * Transport that communicates with Obsidian Mobile via Appium WebView JS injection.
71
+ *
72
+ * Evaluates expressions by switching to the `WEBVIEW_md.obsidian` context and
73
+ * calling `execute()`. Manages vaults by writing to the WebView's `localStorage`
74
+ * (which Obsidian uses as its vault registry on mobile) and pushing files to the device.
75
+ */
76
+ export declare class AppiumTransport implements ObsidianTransport {
77
+ /** */
78
+ readonly isMobile = true;
79
+ private readonly appId;
80
+ private readonly browser;
81
+ private readonly platform;
82
+ private readonly vaultBasePath;
83
+ /**
84
+ * Creates a new Appium transport.
85
+ *
86
+ * @param config - Appium transport configuration.
87
+ */
88
+ constructor(config: AppiumTransportConfig);
89
+ /**
90
+ * Ends the Appium session.
91
+ */
92
+ dispose(): Promise<void>;
93
+ /**
94
+ * Evaluates a JavaScript expression inside Obsidian Mobile's WebView.
95
+ *
96
+ * Switches to the `WEBVIEW_md.obsidian` context, executes the expression,
97
+ * and returns the result string.
98
+ *
99
+ * @param expression - The JavaScript expression to evaluate.
100
+ * @param _options - Evaluation options (cwd is not used on mobile — vault targeting is via localStorage).
101
+ * @returns The normalized result string.
102
+ */
103
+ evaluate(expression: string, _options: TransportEvalOptions): Promise<string>;
104
+ /**
105
+ * Verifies that the Obsidian app is running and the WebView is available.
106
+ *
107
+ * @param _vaultPath - Not used on mobile.
108
+ */
109
+ preflightCheck(_vaultPath: string): Promise<void>;
110
+ /**
111
+ * Pushes vault files to the device.
112
+ *
113
+ * @param vaultPath - The vault path (used as the vault directory name on device).
114
+ * @param files - Map of relative file paths to content strings.
115
+ */
116
+ pushFiles(vaultPath: string, files: Record<string, string>): Promise<void>;
117
+ /**
118
+ * Registers a vault on mobile by pushing files and configuring localStorage.
119
+ *
120
+ * The registration flow:
121
+ * 1. Push a minimal `.obsidian/app.json` to the device so Obsidian recognizes the vault
122
+ * 2. Switch to the WebView context
123
+ * 3. Add the vault to localStorage (`mobile-external-vaults`, `mobile-selected-vault`,
124
+ * `enable-plugin-<path>`)
125
+ * 4. Trigger `location.reload()` so Obsidian re-reads localStorage and opens the vault
126
+ * 5. Wait for `app.workspace.layoutReady`
127
+ *
128
+ * Existing vault registrations in localStorage are preserved (append, not overwrite).
129
+ *
130
+ * @param vaultPath - The absolute path to the vault on the host machine.
131
+ */
132
+ registerVault(vaultPath: string): Promise<void>;
133
+ /**
134
+ * Unregisters a vault on mobile by removing it from localStorage.
135
+ *
136
+ * Preserves other vault registrations. If the unregistered vault was selected,
137
+ * switches to the first remaining vault (or clears the selection).
138
+ *
139
+ * Note: Vault files on the device are not deleted. The test harness or CI
140
+ * environment should handle device cleanup.
141
+ *
142
+ * @param vaultPath - The absolute path to the vault on the host machine.
143
+ */
144
+ unregisterVault(vaultPath: string): Promise<void>;
145
+ /**
146
+ * Switches the driver to the `WEBVIEW_md.obsidian` context.
147
+ *
148
+ * Polls until the context becomes available (the app may still be loading).
149
+ * Uses the `WEBVIEW_md.obsidian` context specifically to avoid connecting
150
+ * to Chrome or other WebViews on the device.
151
+ */
152
+ private ensureWebViewContext;
153
+ /**
154
+ * Converts a host-side vault path to the device-side path.
155
+ *
156
+ * @param vaultPath - Absolute path on the host machine.
157
+ * @returns The device-side vault path.
158
+ */
159
+ private getDeviceVaultPath;
160
+ /**
161
+ * Polls until `app.workspace.layoutReady` is `true` in the WebView.
162
+ */
163
+ private waitForLayoutReady;
164
+ }
@@ -0,0 +1,208 @@
1
+ const NO_OUTPUT = "(no output)";
2
+ const APP_STATE_FOREGROUND = 4;
3
+ const WEBVIEW_CONTEXT_PREFIX = "WEBVIEW_md.obsidian";
4
+ const WEBVIEW_POLL_INTERVAL_IN_MILLISECONDS = 500;
5
+ const WEBVIEW_POLL_TIMEOUT_IN_MILLISECONDS = 3e4;
6
+ const LAYOUT_READY_POLL_INTERVAL_IN_MILLISECONDS = 500;
7
+ const LAYOUT_READY_POLL_TIMEOUT_IN_MILLISECONDS = 3e4;
8
+ const APP_RESTART_DELAY_IN_MILLISECONDS = 2e3;
9
+ const DEFAULT_APP_ID = "md.obsidian";
10
+ const DEFAULT_VAULT_BASE_PATH = {
11
+ android: "/sdcard/Documents/",
12
+ ios: "@md.obsidian:documents/"
13
+ };
14
+ class AppiumTransport {
15
+ /** */
16
+ isMobile = true;
17
+ appId;
18
+ browser;
19
+ platform;
20
+ vaultBasePath;
21
+ /**
22
+ * Creates a new Appium transport.
23
+ *
24
+ * @param config - Appium transport configuration.
25
+ */
26
+ constructor(config) {
27
+ this.browser = config.browser;
28
+ this.platform = config.platform;
29
+ this.appId = config.appId ?? DEFAULT_APP_ID;
30
+ this.vaultBasePath = config.vaultBasePath ?? DEFAULT_VAULT_BASE_PATH[this.platform] ?? "/sdcard/Documents/";
31
+ }
32
+ /**
33
+ * Ends the Appium session.
34
+ */
35
+ async dispose() {
36
+ await this.browser.deleteSession();
37
+ }
38
+ /**
39
+ * Evaluates a JavaScript expression inside Obsidian Mobile's WebView.
40
+ *
41
+ * Switches to the `WEBVIEW_md.obsidian` context, executes the expression,
42
+ * and returns the result string.
43
+ *
44
+ * @param expression - The JavaScript expression to evaluate.
45
+ * @param _options - Evaluation options (cwd is not used on mobile — vault targeting is via localStorage).
46
+ * @returns The normalized result string.
47
+ */
48
+ async evaluate(expression, _options) {
49
+ await this.ensureWebViewContext();
50
+ const result = await this.browser.execute(
51
+ `return (${expression})`
52
+ );
53
+ if (result === void 0 || result === null) {
54
+ return NO_OUTPUT;
55
+ }
56
+ return result;
57
+ }
58
+ /**
59
+ * Verifies that the Obsidian app is running and the WebView is available.
60
+ *
61
+ * @param _vaultPath - Not used on mobile.
62
+ */
63
+ async preflightCheck(_vaultPath) {
64
+ const state = await this.browser.queryAppState(this.appId);
65
+ if (state !== APP_STATE_FOREGROUND) {
66
+ await this.browser.activateApp(this.appId);
67
+ await delay(APP_RESTART_DELAY_IN_MILLISECONDS);
68
+ }
69
+ await this.ensureWebViewContext();
70
+ }
71
+ /**
72
+ * Pushes vault files to the device.
73
+ *
74
+ * @param vaultPath - The vault path (used as the vault directory name on device).
75
+ * @param files - Map of relative file paths to content strings.
76
+ */
77
+ async pushFiles(vaultPath, files) {
78
+ const deviceVaultPath = this.getDeviceVaultPath(vaultPath);
79
+ for (const [filePath, content] of Object.entries(files)) {
80
+ const deviceFilePath = `${deviceVaultPath}/${filePath}`;
81
+ const base64Content = Buffer.from(content, "utf-8").toString("base64");
82
+ await this.browser.pushFile(deviceFilePath, base64Content);
83
+ }
84
+ }
85
+ /**
86
+ * Registers a vault on mobile by pushing files and configuring localStorage.
87
+ *
88
+ * The registration flow:
89
+ * 1. Push a minimal `.obsidian/app.json` to the device so Obsidian recognizes the vault
90
+ * 2. Switch to the WebView context
91
+ * 3. Add the vault to localStorage (`mobile-external-vaults`, `mobile-selected-vault`,
92
+ * `enable-plugin-<path>`)
93
+ * 4. Trigger `location.reload()` so Obsidian re-reads localStorage and opens the vault
94
+ * 5. Wait for `app.workspace.layoutReady`
95
+ *
96
+ * Existing vault registrations in localStorage are preserved (append, not overwrite).
97
+ *
98
+ * @param vaultPath - The absolute path to the vault on the host machine.
99
+ */
100
+ async registerVault(vaultPath) {
101
+ const deviceVaultPath = this.getDeviceVaultPath(vaultPath);
102
+ const obsidianMarker = `${deviceVaultPath}/.obsidian/app.json`;
103
+ const base64Content = Buffer.from("{}", "utf-8").toString("base64");
104
+ await this.browser.pushFile(obsidianMarker, base64Content);
105
+ await this.ensureWebViewContext();
106
+ await this.browser.execute((path) => {
107
+ const existing = JSON.parse(localStorage.getItem("mobile-external-vaults") ?? "[]");
108
+ if (!existing.includes(path)) {
109
+ existing.push(path);
110
+ localStorage.setItem("mobile-external-vaults", JSON.stringify(existing));
111
+ }
112
+ localStorage.setItem("mobile-selected-vault", path);
113
+ localStorage.setItem(`enable-plugin-${path}`, "true");
114
+ location.reload();
115
+ }, deviceVaultPath);
116
+ await this.waitForLayoutReady();
117
+ }
118
+ /**
119
+ * Unregisters a vault on mobile by removing it from localStorage.
120
+ *
121
+ * Preserves other vault registrations. If the unregistered vault was selected,
122
+ * switches to the first remaining vault (or clears the selection).
123
+ *
124
+ * Note: Vault files on the device are not deleted. The test harness or CI
125
+ * environment should handle device cleanup.
126
+ *
127
+ * @param vaultPath - The absolute path to the vault on the host machine.
128
+ */
129
+ async unregisterVault(vaultPath) {
130
+ const deviceVaultPath = this.getDeviceVaultPath(vaultPath);
131
+ await this.ensureWebViewContext();
132
+ await this.browser.execute((path) => {
133
+ const existing = JSON.parse(localStorage.getItem("mobile-external-vaults") ?? "[]");
134
+ const filtered = existing.filter((v) => v !== path);
135
+ localStorage.setItem("mobile-external-vaults", JSON.stringify(filtered));
136
+ localStorage.removeItem(`enable-plugin-${path}`);
137
+ if (localStorage.getItem("mobile-selected-vault") === path) {
138
+ if (filtered.length > 0) {
139
+ localStorage.setItem("mobile-selected-vault", filtered[0] ?? "");
140
+ } else {
141
+ localStorage.removeItem("mobile-selected-vault");
142
+ }
143
+ }
144
+ }, deviceVaultPath);
145
+ }
146
+ /**
147
+ * Switches the driver to the `WEBVIEW_md.obsidian` context.
148
+ *
149
+ * Polls until the context becomes available (the app may still be loading).
150
+ * Uses the `WEBVIEW_md.obsidian` context specifically to avoid connecting
151
+ * to Chrome or other WebViews on the device.
152
+ */
153
+ async ensureWebViewContext() {
154
+ const deadline = Date.now() + WEBVIEW_POLL_TIMEOUT_IN_MILLISECONDS;
155
+ while (Date.now() < deadline) {
156
+ const contexts = await this.browser.getContexts();
157
+ const obsidianContext = contexts.find((ctx) => typeof ctx === "string" && ctx.startsWith(WEBVIEW_CONTEXT_PREFIX));
158
+ if (obsidianContext) {
159
+ await this.browser.switchContext(obsidianContext);
160
+ return;
161
+ }
162
+ await delay(WEBVIEW_POLL_INTERVAL_IN_MILLISECONDS);
163
+ }
164
+ throw new Error(`No ${WEBVIEW_CONTEXT_PREFIX} context found within ${String(WEBVIEW_POLL_TIMEOUT_IN_MILLISECONDS)}ms. Is the Obsidian app fully loaded?`);
165
+ }
166
+ /**
167
+ * Converts a host-side vault path to the device-side path.
168
+ *
169
+ * @param vaultPath - Absolute path on the host machine.
170
+ * @returns The device-side vault path.
171
+ */
172
+ getDeviceVaultPath(vaultPath) {
173
+ return `${this.vaultBasePath}${extractVaultName(vaultPath)}`;
174
+ }
175
+ /**
176
+ * Polls until `app.workspace.layoutReady` is `true` in the WebView.
177
+ */
178
+ async waitForLayoutReady() {
179
+ const deadline = Date.now() + LAYOUT_READY_POLL_TIMEOUT_IN_MILLISECONDS;
180
+ while (Date.now() < deadline) {
181
+ try {
182
+ const isReady = await this.browser.execute(
183
+ 'return typeof app !== "undefined" && app.workspace && app.workspace.layoutReady === true'
184
+ );
185
+ if (isReady) {
186
+ return;
187
+ }
188
+ } catch {
189
+ }
190
+ await delay(LAYOUT_READY_POLL_INTERVAL_IN_MILLISECONDS);
191
+ }
192
+ throw new Error(`Obsidian layout did not become ready within ${String(LAYOUT_READY_POLL_TIMEOUT_IN_MILLISECONDS)}ms`);
193
+ }
194
+ }
195
+ function delay(ms) {
196
+ return new Promise((resolve) => {
197
+ setTimeout(resolve, ms);
198
+ });
199
+ }
200
+ function extractVaultName(vaultPath) {
201
+ const normalized = vaultPath.replace(/[\\/]+$/, "");
202
+ const lastSep = Math.max(normalized.lastIndexOf("/"), normalized.lastIndexOf("\\"));
203
+ return normalized.slice(lastSep + 1);
204
+ }
205
+ export {
206
+ AppiumTransport
207
+ };
208
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL3RyYW5zcG9ydC1hcHBpdW0udHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQGZpbGVcbiAqXG4gKiBBcHBpdW0gdHJhbnNwb3J0IFx1MjAxNCBldmFsdWF0ZXMgZXhwcmVzc2lvbnMgaW5zaWRlIE9ic2lkaWFuIE1vYmlsZSB2aWEgV2ViVmlld1xuICogSmF2YVNjcmlwdCBpbmplY3Rpb24uIE1hbmFnZXMgdmF1bHQgbGlmZWN5Y2xlIHZpYSBsb2NhbFN0b3JhZ2UgYW5kIGZpbGUgcHVzaC5cbiAqXG4gKiBDb25maWd1cmVkIHZpYSBgZW52aXJvbm1lbnRPcHRpb25zLm9ic2lkaWFuVHJhbnNwb3J0YCBpbiB2aXRlc3QgY29uZmlnOlxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIHZpdGVzdC5jb25maWcudHNcbiAqIGVudmlyb25tZW50T3B0aW9uczoge1xuICogICBvYnNpZGlhblRyYW5zcG9ydDoge1xuICogICAgIHR5cGU6ICdvYnNpZGlhbi1hbmRyb2lkLWFwcGl1bScsXG4gKiAgICAgYXBwaXVtVXJsOiAnaHR0cDovL2xvY2FsaG9zdDo0NzIzJyxcbiAqICAgICBkZXZpY2VJZDogJ2VtdWxhdG9yLTU1NTQnLFxuICogICB9LFxuICogfVxuICogYGBgXG4gKlxuICogRm9yIEJyb3dzZXJTdGFjaywgc2V0IGBhcHBpdW1VcmxgIHRvIHRoZSBCcm93c2VyU3RhY2sgaHViIFVSTFxuICogXHUyMDE0IHRoZSB0cmFuc3BvcnQgaXRzZWxmIGlzIGh1Yi1hZ25vc3RpYy5cbiAqXG4gKiAjIyBIb3cgdmF1bHQgcmVnaXN0cmF0aW9uIHdvcmtzIG9uIG1vYmlsZVxuICpcbiAqIE9ic2lkaWFuIE1vYmlsZSBzdG9yZXMgaXRzIHZhdWx0IHJlZ2lzdHJ5IGluIHRoZSBXZWJWaWV3J3MgYGxvY2FsU3RvcmFnZWA6XG4gKlxuICogLSBgbW9iaWxlLWV4dGVybmFsLXZhdWx0c2AgXHUyMDE0IEpTT04gYXJyYXkgb2YgcmVnaXN0ZXJlZCB2YXVsdCBwYXRoc1xuICogLSBgbW9iaWxlLXNlbGVjdGVkLXZhdWx0YCBcdTIwMTQgdGhlIGN1cnJlbnRseSBhY3RpdmUgdmF1bHQgcGF0aFxuICogLSBgZW5hYmxlLXBsdWdpbi08dmF1bHRQYXRoPmAgXHUyMDE0IGBcInRydWVcImAgdG8gZW5hYmxlIHRoZSBwbHVnaW4gc3lzdGVtIGZvciB0aGUgdmF1bHRcbiAqXG4gKiBUbyByZWdpc3RlciBhIHZhdWx0IHByb2dyYW1tYXRpY2FsbHkgKHdpdGhvdXQgVUkgaW50ZXJhY3Rpb24pOlxuICpcbiAqIDEuIFB1c2ggdmF1bHQgZmlsZXMgdG8gdGhlIGRldmljZSAoZS5nLiBgL3NkY2FyZC9Eb2N1bWVudHMvPE5hbWU+Ly5vYnNpZGlhbi9hcHAuanNvbmApXG4gKiAyLiBTd2l0Y2ggdG8gYFdFQlZJRVdfbWQub2JzaWRpYW5gIGNvbnRleHRcbiAqIDMuIFNldCB0aGUgbG9jYWxTdG9yYWdlIGVudHJpZXNcbiAqIDQuIENhbGwgYGxvY2F0aW9uLnJlbG9hZCgpYCBcdTIwMTQgT2JzaWRpYW4gcmUtcmVhZHMgbG9jYWxTdG9yYWdlIGFuZCBvcGVucyB0aGUgdmF1bHRcbiAqXG4gKiBUaGlzIGF2b2lkcyB0aGUgb25ib2FyZGluZyBmbG93IGVudGlyZWx5LlxuICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBJbnRlZ3JhdGlvbi10aW1lIGNvZGUgY292ZXJlZCBieSBpbnRlZ3JhdGlvbiB0ZXN0cywgbm90IHVuaXQgdGVzdHMuICovXG5cbmltcG9ydCB0eXBlIHsgQnJvd3NlciB9IGZyb20gJ3dlYmRyaXZlcmlvJztcblxuaW1wb3J0IHR5cGUge1xuICBPYnNpZGlhblRyYW5zcG9ydCxcbiAgVHJhbnNwb3J0RXZhbE9wdGlvbnNcbn0gZnJvbSAnLi90cmFuc3BvcnQubWpzJztcblxuLyoqXG4gKiBDb25maWd1cmF0aW9uIGZvciB0aGUgQXBwaXVtIHRyYW5zcG9ydC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBBcHBpdW1UcmFuc3BvcnRDb25maWcge1xuICAvKipcbiAgICogQXBwIHBhY2thZ2UgKEFuZHJvaWQpIG9yIGJ1bmRsZSBJRCAoaU9TKS5cbiAgICogRGVmYXVsdHMgdG8gYCdtZC5vYnNpZGlhbidgLlxuICAgKi9cbiAgYXBwSWQ/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBBcHBpdW0gYnJvd3Nlci9kcml2ZXIgaW5zdGFuY2UuXG4gICAqIENyZWF0ZWQgYnkgdGhlIGNvbnN1bWVyIHZpYSBlLmcuIFdlYkRyaXZlcklPJ3MgYHJlbW90ZSgpYC5cbiAgICovXG4gIGJyb3dzZXI6IEJyb3dzZXI7XG5cbiAgLyoqXG4gICAqIFRhcmdldCBwbGF0Zm9ybS4gRGV0ZXJtaW5lcyBXZWJWaWV3IGNvbnRleHQgbmFtaW5nIGFuZCBkZXZpY2UgZmlsZSBwYXRocy5cbiAgICovXG4gIHBsYXRmb3JtOiAnYW5kcm9pZCcgfCAnaW9zJztcblxuICAvKipcbiAgICogQmFzZSBwYXRoIG9uIHRoZSBkZXZpY2Ugd2hlcmUgT2JzaWRpYW4gc3RvcmVzIHZhdWx0cy5cbiAgICpcbiAgICogRGVmYXVsdHM6XG4gICAqIC0gQW5kcm9pZDogYC9zZGNhcmQvRG9jdW1lbnRzL2BcbiAgICogLSBpT1M6IGBAbWQub2JzaWRpYW46ZG9jdW1lbnRzL2BcbiAgICovXG4gIHZhdWx0QmFzZVBhdGg/OiBzdHJpbmc7XG59XG5cbmNvbnN0IE5PX09VVFBVVCA9ICcobm8gb3V0cHV0KSc7XG5jb25zdCBBUFBfU1RBVEVfRk9SRUdST1VORCA9IDQ7XG5jb25zdCBXRUJWSUVXX0NPTlRFWFRfUFJFRklYID0gJ1dFQlZJRVdfbWQub2JzaWRpYW4nO1xuY29uc3QgV0VCVklFV19QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyA9IDUwMDtcbmNvbnN0IFdFQlZJRVdfUE9MTF9USU1FT1VUX0lOX01JTExJU0VDT05EUyA9IDMwMDAwO1xuY29uc3QgTEFZT1VUX1JFQURZX1BPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTID0gNTAwO1xuY29uc3QgTEFZT1VUX1JFQURZX1BPTExfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAzMDAwMDtcbmNvbnN0IEFQUF9SRVNUQVJUX0RFTEFZX0lOX01JTExJU0VDT05EUyA9IDIwMDA7XG5jb25zdCBERUZBVUxUX0FQUF9JRCA9ICdtZC5vYnNpZGlhbic7XG5cbmNvbnN0IERFRkFVTFRfVkFVTFRfQkFTRV9QQVRIOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICBhbmRyb2lkOiAnL3NkY2FyZC9Eb2N1bWVudHMvJyxcbiAgaW9zOiAnQG1kLm9ic2lkaWFuOmRvY3VtZW50cy8nXG59O1xuXG4vKipcbiAqIFRyYW5zcG9ydCB0aGF0IGNvbW11bmljYXRlcyB3aXRoIE9ic2lkaWFuIE1vYmlsZSB2aWEgQXBwaXVtIFdlYlZpZXcgSlMgaW5qZWN0aW9uLlxuICpcbiAqIEV2YWx1YXRlcyBleHByZXNzaW9ucyBieSBzd2l0Y2hpbmcgdG8gdGhlIGBXRUJWSUVXX21kLm9ic2lkaWFuYCBjb250ZXh0IGFuZFxuICogY2FsbGluZyBgZXhlY3V0ZSgpYC4gTWFuYWdlcyB2YXVsdHMgYnkgd3JpdGluZyB0byB0aGUgV2ViVmlldydzIGBsb2NhbFN0b3JhZ2VgXG4gKiAod2hpY2ggT2JzaWRpYW4gdXNlcyBhcyBpdHMgdmF1bHQgcmVnaXN0cnkgb24gbW9iaWxlKSBhbmQgcHVzaGluZyBmaWxlcyB0byB0aGUgZGV2aWNlLlxuICovXG5leHBvcnQgY2xhc3MgQXBwaXVtVHJhbnNwb3J0IGltcGxlbWVudHMgT2JzaWRpYW5UcmFuc3BvcnQge1xuICAvKiogKi9cbiAgcHVibGljIHJlYWRvbmx5IGlzTW9iaWxlID0gdHJ1ZTtcbiAgcHJpdmF0ZSByZWFkb25seSBhcHBJZDogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IGJyb3dzZXI6IEJyb3dzZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgcGxhdGZvcm06ICdhbmRyb2lkJyB8ICdpb3MnO1xuICBwcml2YXRlIHJlYWRvbmx5IHZhdWx0QmFzZVBhdGg6IHN0cmluZztcblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG5ldyBBcHBpdW0gdHJhbnNwb3J0LlxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIC0gQXBwaXVtIHRyYW5zcG9ydCBjb25maWd1cmF0aW9uLlxuICAgKi9cbiAgcHVibGljIGNvbnN0cnVjdG9yKGNvbmZpZzogQXBwaXVtVHJhbnNwb3J0Q29uZmlnKSB7XG4gICAgdGhpcy5icm93c2VyID0gY29uZmlnLmJyb3dzZXI7XG4gICAgdGhpcy5wbGF0Zm9ybSA9IGNvbmZpZy5wbGF0Zm9ybTtcbiAgICB0aGlzLmFwcElkID0gY29uZmlnLmFwcElkID8/IERFRkFVTFRfQVBQX0lEO1xuICAgIHRoaXMudmF1bHRCYXNlUGF0aCA9IGNvbmZpZy52YXVsdEJhc2VQYXRoID8/IERFRkFVTFRfVkFVTFRfQkFTRV9QQVRIW3RoaXMucGxhdGZvcm1dID8/ICcvc2RjYXJkL0RvY3VtZW50cy8nO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuZHMgdGhlIEFwcGl1bSBzZXNzaW9uLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGRpc3Bvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5icm93c2VyLmRlbGV0ZVNlc3Npb24oKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZXMgYSBKYXZhU2NyaXB0IGV4cHJlc3Npb24gaW5zaWRlIE9ic2lkaWFuIE1vYmlsZSdzIFdlYlZpZXcuXG4gICAqXG4gICAqIFN3aXRjaGVzIHRvIHRoZSBgV0VCVklFV19tZC5vYnNpZGlhbmAgY29udGV4dCwgZXhlY3V0ZXMgdGhlIGV4cHJlc3Npb24sXG4gICAqIGFuZCByZXR1cm5zIHRoZSByZXN1bHQgc3RyaW5nLlxuICAgKlxuICAgKiBAcGFyYW0gZXhwcmVzc2lvbiAtIFRoZSBKYXZhU2NyaXB0IGV4cHJlc3Npb24gdG8gZXZhbHVhdGUuXG4gICAqIEBwYXJhbSBfb3B0aW9ucyAtIEV2YWx1YXRpb24gb3B0aW9ucyAoY3dkIGlzIG5vdCB1c2VkIG9uIG1vYmlsZSBcdTIwMTQgdmF1bHQgdGFyZ2V0aW5nIGlzIHZpYSBsb2NhbFN0b3JhZ2UpLlxuICAgKiBAcmV0dXJucyBUaGUgbm9ybWFsaXplZCByZXN1bHQgc3RyaW5nLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGV2YWx1YXRlKGV4cHJlc3Npb246IHN0cmluZywgX29wdGlvbnM6IFRyYW5zcG9ydEV2YWxPcHRpb25zKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVdlYlZpZXdDb250ZXh0KCk7XG5cbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmJyb3dzZXIuZXhlY3V0ZTxudWxsIHwgc3RyaW5nIHwgdW5kZWZpbmVkLCBbXT4oXG4gICAgICBgcmV0dXJuICgke2V4cHJlc3Npb259KWBcbiAgICApO1xuXG4gICAgaWYgKHJlc3VsdCA9PT0gdW5kZWZpbmVkIHx8IHJlc3VsdCA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIE5PX09VVFBVVDtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmaWVzIHRoYXQgdGhlIE9ic2lkaWFuIGFwcCBpcyBydW5uaW5nIGFuZCB0aGUgV2ViVmlldyBpcyBhdmFpbGFibGUuXG4gICAqXG4gICAqIEBwYXJhbSBfdmF1bHRQYXRoIC0gTm90IHVzZWQgb24gbW9iaWxlLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHByZWZsaWdodENoZWNrKF92YXVsdFBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHN0YXRlID0gYXdhaXQgdGhpcy5icm93c2VyLnF1ZXJ5QXBwU3RhdGUodGhpcy5hcHBJZCk7XG4gICAgaWYgKHN0YXRlICE9PSBBUFBfU1RBVEVfRk9SRUdST1VORCkge1xuICAgICAgYXdhaXQgdGhpcy5icm93c2VyLmFjdGl2YXRlQXBwKHRoaXMuYXBwSWQpO1xuICAgICAgYXdhaXQgZGVsYXkoQVBQX1JFU1RBUlRfREVMQVlfSU5fTUlMTElTRUNPTkRTKTtcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmVuc3VyZVdlYlZpZXdDb250ZXh0KCk7XG4gIH1cblxuICAvKipcbiAgICogUHVzaGVzIHZhdWx0IGZpbGVzIHRvIHRoZSBkZXZpY2UuXG4gICAqXG4gICAqIEBwYXJhbSB2YXVsdFBhdGggLSBUaGUgdmF1bHQgcGF0aCAodXNlZCBhcyB0aGUgdmF1bHQgZGlyZWN0b3J5IG5hbWUgb24gZGV2aWNlKS5cbiAgICogQHBhcmFtIGZpbGVzIC0gTWFwIG9mIHJlbGF0aXZlIGZpbGUgcGF0aHMgdG8gY29udGVudCBzdHJpbmdzLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHB1c2hGaWxlcyh2YXVsdFBhdGg6IHN0cmluZywgZmlsZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBkZXZpY2VWYXVsdFBhdGggPSB0aGlzLmdldERldmljZVZhdWx0UGF0aCh2YXVsdFBhdGgpO1xuXG4gICAgZm9yIChjb25zdCBbZmlsZVBhdGgsIGNvbnRlbnRdIG9mIE9iamVjdC5lbnRyaWVzKGZpbGVzKSkge1xuICAgICAgY29uc3QgZGV2aWNlRmlsZVBhdGggPSBgJHtkZXZpY2VWYXVsdFBhdGh9LyR7ZmlsZVBhdGh9YDtcbiAgICAgIGNvbnN0IGJhc2U2NENvbnRlbnQgPSBCdWZmZXIuZnJvbShjb250ZW50LCAndXRmLTgnKS50b1N0cmluZygnYmFzZTY0Jyk7XG4gICAgICBhd2FpdCB0aGlzLmJyb3dzZXIucHVzaEZpbGUoZGV2aWNlRmlsZVBhdGgsIGJhc2U2NENvbnRlbnQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlcnMgYSB2YXVsdCBvbiBtb2JpbGUgYnkgcHVzaGluZyBmaWxlcyBhbmQgY29uZmlndXJpbmcgbG9jYWxTdG9yYWdlLlxuICAgKlxuICAgKiBUaGUgcmVnaXN0cmF0aW9uIGZsb3c6XG4gICAqIDEuIFB1c2ggYSBtaW5pbWFsIGAub2JzaWRpYW4vYXBwLmpzb25gIHRvIHRoZSBkZXZpY2Ugc28gT2JzaWRpYW4gcmVjb2duaXplcyB0aGUgdmF1bHRcbiAgICogMi4gU3dpdGNoIHRvIHRoZSBXZWJWaWV3IGNvbnRleHRcbiAgICogMy4gQWRkIHRoZSB2YXVsdCB0byBsb2NhbFN0b3JhZ2UgKGBtb2JpbGUtZXh0ZXJuYWwtdmF1bHRzYCwgYG1vYmlsZS1zZWxlY3RlZC12YXVsdGAsXG4gICAqICAgIGBlbmFibGUtcGx1Z2luLTxwYXRoPmApXG4gICAqIDQuIFRyaWdnZXIgYGxvY2F0aW9uLnJlbG9hZCgpYCBzbyBPYnNpZGlhbiByZS1yZWFkcyBsb2NhbFN0b3JhZ2UgYW5kIG9wZW5zIHRoZSB2YXVsdFxuICAgKiA1LiBXYWl0IGZvciBgYXBwLndvcmtzcGFjZS5sYXlvdXRSZWFkeWBcbiAgICpcbiAgICogRXhpc3RpbmcgdmF1bHQgcmVnaXN0cmF0aW9ucyBpbiBsb2NhbFN0b3JhZ2UgYXJlIHByZXNlcnZlZCAoYXBwZW5kLCBub3Qgb3ZlcndyaXRlKS5cbiAgICpcbiAgICogQHBhcmFtIHZhdWx0UGF0aCAtIFRoZSBhYnNvbHV0ZSBwYXRoIHRvIHRoZSB2YXVsdCBvbiB0aGUgaG9zdCBtYWNoaW5lLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHJlZ2lzdGVyVmF1bHQodmF1bHRQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBkZXZpY2VWYXVsdFBhdGggPSB0aGlzLmdldERldmljZVZhdWx0UGF0aCh2YXVsdFBhdGgpO1xuXG4gICAgLy8gUHVzaCBhIG1pbmltYWwgLm9ic2lkaWFuIGRpcmVjdG9yeSBzbyBPYnNpZGlhbiByZWNvZ25pemVzIGl0IGFzIGEgdmF1bHQuXG4gICAgY29uc3Qgb2JzaWRpYW5NYXJrZXIgPSBgJHtkZXZpY2VWYXVsdFBhdGh9Ly5vYnNpZGlhbi9hcHAuanNvbmA7XG4gICAgY29uc3QgYmFzZTY0Q29udGVudCA9IEJ1ZmZlci5mcm9tKCd7fScsICd1dGYtOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcbiAgICBhd2FpdCB0aGlzLmJyb3dzZXIucHVzaEZpbGUob2JzaWRpYW5NYXJrZXIsIGJhc2U2NENvbnRlbnQpO1xuXG4gICAgLy8gU3dpdGNoIHRvIFdlYlZpZXcgYW5kIGNvbmZpZ3VyZSBsb2NhbFN0b3JhZ2UuXG4gICAgYXdhaXQgdGhpcy5lbnN1cmVXZWJWaWV3Q29udGV4dCgpO1xuXG4gICAgYXdhaXQgdGhpcy5icm93c2VyLmV4ZWN1dGUoKHBhdGg6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgZXhpc3RpbmcgPSBKU09OLnBhcnNlKGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdtb2JpbGUtZXh0ZXJuYWwtdmF1bHRzJykgPz8gJ1tdJykgYXMgc3RyaW5nW107XG4gICAgICBpZiAoIWV4aXN0aW5nLmluY2x1ZGVzKHBhdGgpKSB7XG4gICAgICAgIGV4aXN0aW5nLnB1c2gocGF0aCk7XG4gICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKCdtb2JpbGUtZXh0ZXJuYWwtdmF1bHRzJywgSlNPTi5zdHJpbmdpZnkoZXhpc3RpbmcpKTtcbiAgICAgIH1cbiAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKCdtb2JpbGUtc2VsZWN0ZWQtdmF1bHQnLCBwYXRoKTtcbiAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKGBlbmFibGUtcGx1Z2luLSR7cGF0aH1gLCAndHJ1ZScpO1xuICAgICAgbG9jYXRpb24ucmVsb2FkKCk7XG4gICAgfSwgZGV2aWNlVmF1bHRQYXRoKTtcblxuICAgIC8vIFdhaXQgZm9yIHJlbG9hZCArIHZhdWx0IGluaXRpYWxpemF0aW9uLlxuICAgIGF3YWl0IHRoaXMud2FpdEZvckxheW91dFJlYWR5KCk7XG4gIH1cblxuICAvKipcbiAgICogVW5yZWdpc3RlcnMgYSB2YXVsdCBvbiBtb2JpbGUgYnkgcmVtb3ZpbmcgaXQgZnJvbSBsb2NhbFN0b3JhZ2UuXG4gICAqXG4gICAqIFByZXNlcnZlcyBvdGhlciB2YXVsdCByZWdpc3RyYXRpb25zLiBJZiB0aGUgdW5yZWdpc3RlcmVkIHZhdWx0IHdhcyBzZWxlY3RlZCxcbiAgICogc3dpdGNoZXMgdG8gdGhlIGZpcnN0IHJlbWFpbmluZyB2YXVsdCAob3IgY2xlYXJzIHRoZSBzZWxlY3Rpb24pLlxuICAgKlxuICAgKiBOb3RlOiBWYXVsdCBmaWxlcyBvbiB0aGUgZGV2aWNlIGFyZSBub3QgZGVsZXRlZC4gVGhlIHRlc3QgaGFybmVzcyBvciBDSVxuICAgKiBlbnZpcm9ubWVudCBzaG91bGQgaGFuZGxlIGRldmljZSBjbGVhbnVwLlxuICAgKlxuICAgKiBAcGFyYW0gdmF1bHRQYXRoIC0gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIHZhdWx0IG9uIHRoZSBob3N0IG1hY2hpbmUuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdW5yZWdpc3RlclZhdWx0KHZhdWx0UGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZGV2aWNlVmF1bHRQYXRoID0gdGhpcy5nZXREZXZpY2VWYXVsdFBhdGgodmF1bHRQYXRoKTtcblxuICAgIGF3YWl0IHRoaXMuZW5zdXJlV2ViVmlld0NvbnRleHQoKTtcblxuICAgIGF3YWl0IHRoaXMuYnJvd3Nlci5leGVjdXRlKChwYXRoOiBzdHJpbmcpID0+IHtcbiAgICAgIGNvbnN0IGV4aXN0aW5nID0gSlNPTi5wYXJzZShsb2NhbFN0b3JhZ2UuZ2V0SXRlbSgnbW9iaWxlLWV4dGVybmFsLXZhdWx0cycpID8/ICdbXScpIGFzIHN0cmluZ1tdO1xuICAgICAgY29uc3QgZmlsdGVyZWQgPSBleGlzdGluZy5maWx0ZXIoKHYpID0+IHYgIT09IHBhdGgpO1xuICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ21vYmlsZS1leHRlcm5hbC12YXVsdHMnLCBKU09OLnN0cmluZ2lmeShmaWx0ZXJlZCkpO1xuICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oYGVuYWJsZS1wbHVnaW4tJHtwYXRofWApO1xuICAgICAgaWYgKGxvY2FsU3RvcmFnZS5nZXRJdGVtKCdtb2JpbGUtc2VsZWN0ZWQtdmF1bHQnKSA9PT0gcGF0aCkge1xuICAgICAgICBpZiAoZmlsdGVyZWQubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKCdtb2JpbGUtc2VsZWN0ZWQtdmF1bHQnLCBmaWx0ZXJlZFswXSA/PyAnJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oJ21vYmlsZS1zZWxlY3RlZC12YXVsdCcpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSwgZGV2aWNlVmF1bHRQYXRoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2hlcyB0aGUgZHJpdmVyIHRvIHRoZSBgV0VCVklFV19tZC5vYnNpZGlhbmAgY29udGV4dC5cbiAgICpcbiAgICogUG9sbHMgdW50aWwgdGhlIGNvbnRleHQgYmVjb21lcyBhdmFpbGFibGUgKHRoZSBhcHAgbWF5IHN0aWxsIGJlIGxvYWRpbmcpLlxuICAgKiBVc2VzIHRoZSBgV0VCVklFV19tZC5vYnNpZGlhbmAgY29udGV4dCBzcGVjaWZpY2FsbHkgdG8gYXZvaWQgY29ubmVjdGluZ1xuICAgKiB0byBDaHJvbWUgb3Igb3RoZXIgV2ViVmlld3Mgb24gdGhlIGRldmljZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZW5zdXJlV2ViVmlld0NvbnRleHQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgV0VCVklFV19QT0xMX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTO1xuXG4gICAgd2hpbGUgKERhdGUubm93KCkgPCBkZWFkbGluZSkge1xuICAgICAgY29uc3QgY29udGV4dHMgPSBhd2FpdCB0aGlzLmJyb3dzZXIuZ2V0Q29udGV4dHMoKTtcbiAgICAgIGNvbnN0IG9ic2lkaWFuQ29udGV4dCA9IGNvbnRleHRzLmZpbmQoKGN0eCk6IGN0eCBpcyBzdHJpbmcgPT4gdHlwZW9mIGN0eCA9PT0gJ3N0cmluZycgJiYgY3R4LnN0YXJ0c1dpdGgoV0VCVklFV19DT05URVhUX1BSRUZJWCkpO1xuXG4gICAgICBpZiAob2JzaWRpYW5Db250ZXh0KSB7XG4gICAgICAgIGF3YWl0IHRoaXMuYnJvd3Nlci5zd2l0Y2hDb250ZXh0KG9ic2lkaWFuQ29udGV4dCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgYXdhaXQgZGVsYXkoV0VCVklFV19QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyk7XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBObyAke1dFQlZJRVdfQ09OVEVYVF9QUkVGSVh9IGNvbnRleHQgZm91bmQgd2l0aGluICR7U3RyaW5nKFdFQlZJRVdfUE9MTF9USU1FT1VUX0lOX01JTExJU0VDT05EUyl9bXMuIElzIHRoZSBPYnNpZGlhbiBhcHAgZnVsbHkgbG9hZGVkP2ApO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnRzIGEgaG9zdC1zaWRlIHZhdWx0IHBhdGggdG8gdGhlIGRldmljZS1zaWRlIHBhdGguXG4gICAqXG4gICAqIEBwYXJhbSB2YXVsdFBhdGggLSBBYnNvbHV0ZSBwYXRoIG9uIHRoZSBob3N0IG1hY2hpbmUuXG4gICAqIEByZXR1cm5zIFRoZSBkZXZpY2Utc2lkZSB2YXVsdCBwYXRoLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXREZXZpY2VWYXVsdFBhdGgodmF1bHRQYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBgJHt0aGlzLnZhdWx0QmFzZVBhdGh9JHtleHRyYWN0VmF1bHROYW1lKHZhdWx0UGF0aCl9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBQb2xscyB1bnRpbCBgYXBwLndvcmtzcGFjZS5sYXlvdXRSZWFkeWAgaXMgYHRydWVgIGluIHRoZSBXZWJWaWV3LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB3YWl0Rm9yTGF5b3V0UmVhZHkoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgTEFZT1VUX1JFQURZX1BPTExfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFM7XG5cbiAgICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBpc1JlYWR5ID0gYXdhaXQgdGhpcy5icm93c2VyLmV4ZWN1dGU8Ym9vbGVhbiwgW10+KFxuICAgICAgICAgICdyZXR1cm4gdHlwZW9mIGFwcCAhPT0gXCJ1bmRlZmluZWRcIiAmJiBhcHAud29ya3NwYWNlICYmIGFwcC53b3Jrc3BhY2UubGF5b3V0UmVhZHkgPT09IHRydWUnXG4gICAgICAgICk7XG4gICAgICAgIGlmIChpc1JlYWR5KSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gQXBwIG5vdCByZWFkeSB5ZXQgKHBhZ2UgbWF5IGJlIHJlbG9hZGluZykuXG4gICAgICB9XG5cbiAgICAgIGF3YWl0IGRlbGF5KExBWU9VVF9SRUFEWV9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyk7XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBPYnNpZGlhbiBsYXlvdXQgZGlkIG5vdCBiZWNvbWUgcmVhZHkgd2l0aGluICR7U3RyaW5nKExBWU9VVF9SRUFEWV9QT0xMX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTKX1tc2ApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJucyBhIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgZ2l2ZW4gZGVsYXkuXG4gKlxuICogQHBhcmFtIG1zIC0gVGhlIGRlbGF5IGluIG1pbGxpc2Vjb25kcy5cbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIGFmdGVyIHRoZSBkZWxheS5cbiAqL1xuZnVuY3Rpb24gZGVsYXkobXM6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICBzZXRUaW1lb3V0KHJlc29sdmUsIG1zKTtcbiAgfSk7XG59XG5cbi8qKlxuICogRXh0cmFjdHMgdGhlIHZhdWx0IGRpcmVjdG9yeSBuYW1lIGZyb20gYSBob3N0LXNpZGUgdmF1bHQgcGF0aC5cbiAqXG4gKiBAcGFyYW0gdmF1bHRQYXRoIC0gQWJzb2x1dGUgcGF0aCBvbiB0aGUgaG9zdCBtYWNoaW5lLlxuICogQHJldHVybnMgVGhlIGxhc3QgcGF0aCBzZWdtZW50ICh2YXVsdCBkaXJlY3RvcnkgbmFtZSkuXG4gKi9cbmZ1bmN0aW9uIGV4dHJhY3RWYXVsdE5hbWUodmF1bHRQYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBub3JtYWxpemVkID0gdmF1bHRQYXRoLnJlcGxhY2UoL1tcXFxcL10rJC8sICcnKTtcbiAgY29uc3QgbGFzdFNlcCA9IE1hdGgubWF4KG5vcm1hbGl6ZWQubGFzdEluZGV4T2YoJy8nKSwgbm9ybWFsaXplZC5sYXN0SW5kZXhPZignXFxcXCcpKTtcbiAgcmV0dXJuIG5vcm1hbGl6ZWQuc2xpY2UobGFzdFNlcCArIDEpO1xufVxuXG4vKiB2OCBpZ25vcmUgc3RvcCAqL1xuIl0sCiAgIm1hcHBpbmdzIjogIkFBZ0ZBLE1BQU0sWUFBWTtBQUNsQixNQUFNLHVCQUF1QjtBQUM3QixNQUFNLHlCQUF5QjtBQUMvQixNQUFNLHdDQUF3QztBQUM5QyxNQUFNLHVDQUF1QztBQUM3QyxNQUFNLDZDQUE2QztBQUNuRCxNQUFNLDRDQUE0QztBQUNsRCxNQUFNLG9DQUFvQztBQUMxQyxNQUFNLGlCQUFpQjtBQUV2QixNQUFNLDBCQUFrRDtBQUFBLEVBQ3RELFNBQVM7QUFBQSxFQUNULEtBQUs7QUFDUDtBQVNPLE1BQU0sZ0JBQTZDO0FBQUE7QUFBQSxFQUV4QyxXQUFXO0FBQUEsRUFDVjtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9WLFlBQVksUUFBK0I7QUFDaEQsU0FBSyxVQUFVLE9BQU87QUFDdEIsU0FBSyxXQUFXLE9BQU87QUFDdkIsU0FBSyxRQUFRLE9BQU8sU0FBUztBQUM3QixTQUFLLGdCQUFnQixPQUFPLGlCQUFpQix3QkFBd0IsS0FBSyxRQUFRLEtBQUs7QUFBQSxFQUN6RjtB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
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Desktop CDP transport — evaluates expressions via Chrome DevTools Protocol
5
+ * over WebSocket and manages vaults via Electron IPC.
6
+ *
7
+ * Obsidian exposes CDP on port 8315 by default. This transport connects to
8
+ * page targets, sends `Runtime.evaluate` commands, and routes to the correct
9
+ * vault target using `getBasePath()` probing.
10
+ *
11
+ * Advantages over CLI transport:
12
+ * - No CLI binary installation needed
13
+ * - No "CLI enabled" setting required in Obsidian
14
+ * - Lower overhead (WebSocket vs. process spawn per eval)
15
+ *
16
+ * Requirements:
17
+ * - Obsidian must be running with remote debugging enabled (port 8315)
18
+ * - Node.js 22+ (uses built-in `WebSocket` and `fetch` globals)
19
+ */
20
+ import type { ObsidianTransport, TransportEvalOptions } from './transport.mjs';
21
+ /**
22
+ * Configuration for the CDP transport.
23
+ */
24
+ export interface DesktopCdpTransportConfig {
25
+ /**
26
+ * CDP host. Defaults to `'localhost'`.
27
+ */
28
+ cdpHost?: string;
29
+ /**
30
+ * CDP port. Defaults to `8315`.
31
+ */
32
+ cdpPort?: number;
33
+ /**
34
+ * Timeout in milliseconds for individual CDP commands.
35
+ * Defaults to `30000`.
36
+ */
37
+ commandTimeoutInMilliseconds?: number;
38
+ }
39
+ /**
40
+ * Transport that communicates with Desktop Obsidian via Chrome DevTools Protocol.
41
+ *
42
+ * Connects to Obsidian's CDP WebSocket endpoint, sends `Runtime.evaluate`
43
+ * commands, and routes expressions to the correct vault target.
44
+ */
45
+ export declare class DesktopCdpTransport implements ObsidianTransport {
46
+ /** */
47
+ readonly isMobile = false;
48
+ private activeVaultPath;
49
+ private readonly cdpPort;
50
+ private readonly cdpUrl;
51
+ private readonly commandTimeoutInMilliseconds;
52
+ private messageId;
53
+ private ws;
54
+ /**
55
+ * Creates a new CDP transport.
56
+ *
57
+ * @param config - CDP connection configuration.
58
+ */
59
+ constructor(config?: DesktopCdpTransportConfig);
60
+ /**
61
+ * Disposes of the active WebSocket connection.
62
+ */
63
+ dispose(): Promise<void>;
64
+ /**
65
+ * Evaluates a JavaScript expression inside Obsidian via CDP `Runtime.evaluate`.
66
+ *
67
+ * Routes to the correct vault target based on `options.cwd`.
68
+ *
69
+ * @param expression - The JavaScript expression to evaluate.
70
+ * @param options - Evaluation options.
71
+ * @returns The normalized result string.
72
+ */
73
+ evaluate(expression: string, options: TransportEvalOptions): Promise<string>;
74
+ /**
75
+ * Verifies that the CDP endpoint is reachable and has Obsidian targets.
76
+ *
77
+ * If Obsidian is not running, attempts to auto-start it via URI protocol
78
+ * and polls until CDP becomes available.
79
+ *
80
+ * @param vaultPath - The vault path (used for vault registration check).
81
+ */
82
+ preflightCheck(vaultPath: string): Promise<void>;
83
+ /**
84
+ * Registers a vault via Electron IPC evaluated through CDP.
85
+ *
86
+ * Uses an existing Obsidian target to send the `vault-open` IPC command,
87
+ * then polls until the new vault's target appears.
88
+ *
89
+ * @param vaultPath - The absolute path to the vault folder.
90
+ */
91
+ registerVault(vaultPath: string): Promise<void>;
92
+ /**
93
+ * Unregisters a vault by destroying its window and removing it from the registry.
94
+ *
95
+ * @param vaultPath - The absolute path to the vault folder.
96
+ */
97
+ unregisterVault(vaultPath: string): Promise<void>;
98
+ /**
99
+ * Connects to a CDP target's WebSocket endpoint.
100
+ *
101
+ * @param target - The CDP target to connect to.
102
+ * @returns The open WebSocket connection.
103
+ */
104
+ private connectToTarget;
105
+ /**
106
+ * Disconnects the active WebSocket connection.
107
+ */
108
+ private disconnect;
109
+ /**
110
+ * Ensures there is an active WebSocket connection to the correct vault target.
111
+ *
112
+ * Reuses the existing connection if it targets the requested vault.
113
+ * Otherwise, finds the correct target and reconnects.
114
+ *
115
+ * @param vaultPath - The vault path to target.
116
+ * @returns The active WebSocket connection.
117
+ */
118
+ private ensureConnection;
119
+ /**
120
+ * Launches Obsidian with `--remote-debugging-port` and polls until CDP becomes available.
121
+ */
122
+ private ensureObsidianRunning;
123
+ /**
124
+ * Finds the CDP target that has the given vault open.
125
+ *
126
+ * For a single target, returns it directly. For multiple targets,
127
+ * probes each by evaluating `getBasePath()` to find the match.
128
+ *
129
+ * @param vaultPath - The vault path to match.
130
+ * @returns The matching CDP target.
131
+ */
132
+ private findTargetForVault;
133
+ /**
134
+ * Fetches the list of page-type targets from the CDP `/json` endpoint.
135
+ *
136
+ * @returns The list of page targets.
137
+ */
138
+ private getPageTargets;
139
+ /**
140
+ * Probes a target to discover which vault path it has open.
141
+ *
142
+ * Creates a temporary WebSocket connection, evaluates `getBasePath()`,
143
+ * and returns the result.
144
+ *
145
+ * @param target - The CDP target to probe.
146
+ * @returns The vault's base path.
147
+ */
148
+ private probeVaultPath;
149
+ /**
150
+ * Sends a CDP command over WebSocket and waits for the response.
151
+ *
152
+ * @param ws - The WebSocket connection.
153
+ * @param method - The CDP method name.
154
+ * @param params - The CDP method parameters.
155
+ * @returns The CDP response.
156
+ */
157
+ private sendCommand;
158
+ }