obsidian-e2e 0.4.0 → 0.6.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/dist/vitest.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as VaultSeedEntry, a as acquireVaultRunLock, c as readVaultRunLockMarker, d as ObsidianFixtures, f as ObsidianTest, g as VaultSeed, h as SharedVaultLockOptions, i as VaultRunLockState, l as CreateObsidianTestOptions, m as PluginTest, n as VaultRunLock, o as clearVaultRunLockMarker, p as PluginFixtures, r as VaultRunLockMetadata, s as inspectVaultRunLock, t as AcquireVaultRunLockOptions, u as CreatePluginTestOptions } from "./vault-lock-LmqAsLDT.mjs";
1
+ import { a as acquireVaultRunLock, b as VaultSeedEntry, c as readVaultRunLockMarker, d as ObsidianFixtures, f as ObsidianTest, g as SharedVaultLockOptions, h as PluginTest, i as VaultRunLockState, l as CreateObsidianTestOptions, n as VaultRunLock, o as clearVaultRunLockMarker, p as PluginFixtures, r as VaultRunLockMetadata, s as inspectVaultRunLock, t as AcquireVaultRunLockOptions, u as CreatePluginTestOptions, y as VaultSeed } from "./vault-lock-XcBHtwm9.mjs";
2
2
 
3
3
  //#region src/fixtures/create-obsidian-test.d.ts
4
4
  declare function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest;
package/dist/vitest.mjs CHANGED
@@ -1,78 +1,6 @@
1
- import { a as clearVaultRunLockMarker, d as createObsidianClient, f as getClientInternals, i as acquireVaultRunLock, l as captureFailureArtifacts, n as createVaultApi, o as inspectVaultRunLock, r as resolveFilesystemPath, s as readVaultRunLockMarker, t as createSandboxApi, u as capturePluginFailureArtifacts } from "./sandbox--mUbNsh7.mjs";
1
+ import { a as clearVaultRunLockMarker, d as createVaultApi, f as resolveFilesystemPath, i as acquireVaultRunLock, m as getClientInternals, o as inspectVaultRunLock, r as createBaseFixtures, s as readVaultRunLockMarker } from "./test-context-Bl-e-83H.mjs";
2
+ import { t as createNoteDocument } from "./document-DunL2Moz.mjs";
2
3
  import { test } from "vite-plus/test";
3
- //#region src/fixtures/failure-artifacts.ts
4
- function registerFailureArtifacts(context, obsidian, options, plugin) {
5
- if (!options.captureOnFailure) return;
6
- context.onTestFailed(async () => {
7
- await captureFailureArtifacts(context.task, obsidian, {
8
- ...options,
9
- plugin
10
- });
11
- });
12
- }
13
- function registerPluginFailureArtifacts(context, plugin, options) {
14
- if (!options.captureOnFailure) return;
15
- context.onTestFailed(async () => {
16
- await capturePluginFailureArtifacts(context.task, plugin, options);
17
- });
18
- }
19
- function createBaseFixtures(options, fixtureOptions = {}) {
20
- const createVault = fixtureOptions.createVault ?? ((obsidian) => createVaultApi({ obsidian }));
21
- return {
22
- _vaultLock: [async ({}, use) => {
23
- if (!options.sharedVaultLock) {
24
- await use(null);
25
- return;
26
- }
27
- const lockClient = createObsidianClient(options);
28
- await lockClient.verify();
29
- const vaultLock = await acquireVaultRunLock({
30
- ...options.sharedVaultLock === true ? {} : options.sharedVaultLock,
31
- vaultName: options.vault,
32
- vaultPath: await lockClient.vaultPath()
33
- });
34
- await vaultLock.publishMarker(lockClient);
35
- try {
36
- await use(vaultLock);
37
- } finally {
38
- try {
39
- await clearVaultRunLockMarker(lockClient);
40
- } catch {}
41
- await vaultLock.release();
42
- }
43
- }, { scope: "worker" }],
44
- obsidian: async ({ _vaultLock, onTestFailed, task }, use) => {
45
- const obsidian = createObsidianClient(options);
46
- await obsidian.verify();
47
- if (_vaultLock) await _vaultLock.publishMarker(obsidian);
48
- registerFailureArtifacts({
49
- onTestFailed,
50
- task
51
- }, obsidian, options);
52
- try {
53
- await use(obsidian);
54
- } finally {
55
- await getClientInternals(obsidian).restoreAll();
56
- }
57
- },
58
- sandbox: async ({ obsidian }, use) => {
59
- const sandbox = await createSandboxApi({
60
- obsidian,
61
- sandboxRoot: options.sandboxRoot ?? "__obsidian_e2e__",
62
- testName: "test"
63
- });
64
- try {
65
- await use(sandbox);
66
- } finally {
67
- await sandbox.cleanup();
68
- }
69
- },
70
- vault: async ({ obsidian }, use) => {
71
- await use(await createVault(obsidian));
72
- }
73
- };
74
- }
75
- //#endregion
76
4
  //#region src/fixtures/create-obsidian-test.ts
77
5
  function createObsidianTest(options) {
78
6
  return test.extend(createBaseFixtures(options));
@@ -85,20 +13,11 @@ function createPluginTest(options) {
85
13
  if (options.seedVault) await applyVaultSeed(obsidian, options.seedVault);
86
14
  return createVaultApi({ obsidian });
87
15
  } }),
88
- plugin: async ({ obsidian, onTestFailed, task }, use) => {
89
- const plugin = obsidian.plugin(options.pluginId);
90
- const wasEnabled = await plugin.isEnabled();
91
- if (!wasEnabled) await plugin.enable({ filter: options.pluginFilter });
92
- if (options.seedPluginData !== void 0) await plugin.data().write(options.seedPluginData);
93
- registerPluginFailureArtifacts({
94
- onTestFailed,
95
- task
96
- }, plugin, options);
97
- try {
98
- await use(plugin);
99
- } finally {
100
- if (!wasEnabled) await plugin.disable({ filter: options.pluginFilter });
101
- }
16
+ plugin: async ({ _testContext }, use) => {
17
+ await use(await _testContext.plugin(options.pluginId, {
18
+ filter: options.pluginFilter,
19
+ seedData: options.seedPluginData
20
+ }));
102
21
  }
103
22
  };
104
23
  return test.extend(fixtures);
@@ -116,6 +35,10 @@ async function writeSeedValue(vault, targetPath, value) {
116
35
  await vault.write(targetPath, value, { waitForContent: true });
117
36
  return;
118
37
  }
38
+ if ("note" in value) {
39
+ await vault.write(targetPath, createNoteDocument(value.note).raw, { waitForContent: true });
40
+ return;
41
+ }
119
42
  await vault.write(targetPath, `${JSON.stringify(value.json, null, 2)}\n`, { waitForContent: true });
120
43
  }
121
44
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.mjs","names":["base","base"],"sources":["../src/fixtures/failure-artifacts.ts","../src/fixtures/base-fixtures.ts","../src/fixtures/create-obsidian-test.ts","../src/fixtures/create-plugin-test.ts"],"sourcesContent":["import type { TestContext } from \"vite-plus/test\";\n\nimport {\n captureFailureArtifacts,\n capturePluginFailureArtifacts,\n} from \"../artifacts/failure-artifacts\";\nimport type { ObsidianClient, PluginHandle } from \"../core/types\";\nimport type { CreateObsidianTestOptions } from \"./types\";\n\nexport function registerFailureArtifacts(\n context: Pick<TestContext, \"onTestFailed\" | \"task\">,\n obsidian: ObsidianClient,\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n plugin?: PluginHandle,\n): void {\n if (!options.captureOnFailure) {\n return;\n }\n\n context.onTestFailed(async () => {\n await captureFailureArtifacts(context.task, obsidian, {\n ...options,\n plugin,\n });\n });\n}\n\nexport function registerPluginFailureArtifacts(\n context: Pick<TestContext, \"onTestFailed\" | \"task\">,\n plugin: PluginHandle,\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): void {\n if (!options.captureOnFailure) {\n return;\n }\n\n context.onTestFailed(async () => {\n await capturePluginFailureArtifacts(context.task, plugin, options);\n });\n}\n","import type { TestContext } from \"vite-plus/test\";\n\nimport { createObsidianClient } from \"../core/client\";\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient, VaultApi } from \"../core/types\";\nimport { createSandboxApi } from \"../vault/sandbox\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { registerFailureArtifacts } from \"./failure-artifacts\";\nimport type { CreateObsidianTestOptions } from \"./types\";\nimport { acquireVaultRunLock, clearVaultRunLockMarker, type VaultRunLock } from \"./vault-lock\";\n\nexport const DEFAULT_SANDBOX_ROOT = \"__obsidian_e2e__\";\n\nexport interface BaseFixtureState {\n _vaultLock: VaultRunLock | null;\n}\n\ninterface BaseFixtureOptions {\n createVault?: (obsidian: ObsidianClient) => Promise<VaultApi> | VaultApi;\n}\n\nexport function createBaseFixtures(\n options: CreateObsidianTestOptions,\n fixtureOptions: BaseFixtureOptions = {},\n) {\n const createVault =\n fixtureOptions.createVault ?? ((obsidian: ObsidianClient) => createVaultApi({ obsidian }));\n\n return {\n _vaultLock: [\n // eslint-disable-next-line no-empty-pattern\n async ({}, use: (vaultLock: VaultRunLock | null) => Promise<void>) => {\n if (!options.sharedVaultLock) {\n await use(null);\n return;\n }\n\n const lockClient = createObsidianClient(options);\n await lockClient.verify();\n\n const lockOptions = options.sharedVaultLock === true ? {} : options.sharedVaultLock;\n const vaultLock = await acquireVaultRunLock({\n ...lockOptions,\n vaultName: options.vault,\n vaultPath: await lockClient.vaultPath(),\n });\n\n await vaultLock.publishMarker(lockClient);\n try {\n await use(vaultLock);\n } finally {\n try {\n await clearVaultRunLockMarker(lockClient);\n } catch {}\n\n await vaultLock.release();\n }\n },\n { scope: \"worker\" },\n ],\n // oxlint-disable-next-line no-empty-pattern\n obsidian: async (\n {\n _vaultLock,\n onTestFailed,\n task,\n }: Pick<BaseFixtureState & TestContext, \"_vaultLock\" | \"onTestFailed\" | \"task\">,\n use: (obsidian: ObsidianClient) => Promise<void>,\n ) => {\n const obsidian = createObsidianClient(options);\n\n await obsidian.verify();\n if (_vaultLock) {\n await _vaultLock.publishMarker(obsidian);\n }\n registerFailureArtifacts({ onTestFailed, task }, obsidian, options);\n\n try {\n await use(obsidian);\n } finally {\n await getClientInternals(obsidian).restoreAll();\n }\n },\n sandbox: async (\n { obsidian }: { obsidian: ObsidianClient },\n use: (sandbox: Awaited<ReturnType<typeof createSandboxApi>>) => Promise<void>,\n ) => {\n const sandbox = await createSandboxApi({\n obsidian,\n sandboxRoot: options.sandboxRoot ?? DEFAULT_SANDBOX_ROOT,\n testName: \"test\",\n });\n\n try {\n await use(sandbox);\n } finally {\n await sandbox.cleanup();\n }\n },\n vault: async (\n { obsidian }: { obsidian: ObsidianClient },\n use: (vault: VaultApi) => Promise<void>,\n ) => {\n await use(await createVault(obsidian));\n },\n };\n}\n","import { test as base } from \"vite-plus/test\";\n\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport type { CreateObsidianTestOptions, ObsidianFixtures, ObsidianTest } from \"./types\";\n\nexport function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest {\n return base.extend<ObsidianFixtures & BaseFixtureState>(\n createBaseFixtures(options) as never,\n ) as ObsidianTest;\n}\n","import { test as base } from \"vite-plus/test\";\nimport type { TestContext } from \"vite-plus/test\";\n\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient, VaultApi } from \"../core/types\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { resolveFilesystemPath } from \"../vault/paths\";\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport { registerPluginFailureArtifacts } from \"./failure-artifacts\";\nimport type {\n CreatePluginTestOptions,\n PluginFixtures,\n PluginTest,\n VaultSeed,\n VaultSeedEntry,\n} from \"./types\";\n\nexport function createPluginTest(options: CreatePluginTestOptions): PluginTest {\n const fixtures = {\n ...createBaseFixtures(options, {\n async createVault(obsidian) {\n if (options.seedVault) {\n await applyVaultSeed(obsidian, options.seedVault);\n }\n\n return createVaultApi({ obsidian });\n },\n }),\n plugin: async (\n {\n obsidian,\n onTestFailed,\n task,\n }: Pick<PluginFixtures & TestContext, \"obsidian\" | \"onTestFailed\" | \"task\">,\n use: (plugin: PluginFixtures[\"plugin\"]) => Promise<void>,\n ) => {\n const plugin = obsidian.plugin(options.pluginId);\n const wasEnabled = await plugin.isEnabled();\n\n if (!wasEnabled) {\n await plugin.enable({ filter: options.pluginFilter });\n }\n\n if (options.seedPluginData !== undefined) {\n await plugin.data().write(options.seedPluginData);\n }\n\n registerPluginFailureArtifacts({ onTestFailed, task }, plugin, options);\n\n try {\n await use(plugin);\n } finally {\n if (!wasEnabled) {\n await plugin.disable({ filter: options.pluginFilter });\n }\n }\n },\n };\n\n return base.extend<PluginFixtures & BaseFixtureState>(fixtures as never) as PluginTest;\n}\n\nasync function applyVaultSeed(obsidian: ObsidianClient, seedVault: VaultSeed): Promise<void> {\n const vault = createVaultApi({ obsidian });\n\n for (const [targetPath, value] of Object.entries(seedVault)) {\n const resolvedPath = await resolveFilesystemPath(obsidian, \"\", targetPath);\n await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);\n await writeSeedValue(vault, targetPath, value);\n }\n}\n\nasync function writeSeedValue(\n vault: VaultApi,\n targetPath: string,\n value: VaultSeedEntry,\n): Promise<void> {\n if (typeof value === \"string\") {\n await vault.write(targetPath, value, {\n waitForContent: true,\n });\n return;\n }\n\n await vault.write(targetPath, `${JSON.stringify(value.json, null, 2)}\\n`, {\n waitForContent: true,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vitest.mjs","names":["base","base"],"sources":["../src/fixtures/create-obsidian-test.ts","../src/fixtures/create-plugin-test.ts"],"sourcesContent":["import { test as base } from \"vite-plus/test\";\n\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport type { CreateObsidianTestOptions, ObsidianFixtures, ObsidianTest } from \"./types\";\n\nexport function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest {\n return base.extend<ObsidianFixtures & BaseFixtureState>(\n createBaseFixtures(options) as never,\n ) as ObsidianTest;\n}\n","import { test as base } from \"vite-plus/test\";\n\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient, VaultApi } from \"../core/types\";\nimport { createNoteDocument } from \"../note/document\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { resolveFilesystemPath } from \"../vault/paths\";\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport type {\n CreatePluginTestOptions,\n PluginFixtures,\n PluginTest,\n VaultSeed,\n VaultSeedEntry,\n} from \"./types\";\n\nexport function createPluginTest(options: CreatePluginTestOptions): PluginTest {\n const fixtures = {\n ...createBaseFixtures(options, {\n async createVault(obsidian) {\n if (options.seedVault) {\n await applyVaultSeed(obsidian, options.seedVault);\n }\n\n return createVaultApi({ obsidian });\n },\n }),\n plugin: async (\n { _testContext }: Pick<BaseFixtureState, \"_testContext\">,\n use: (plugin: PluginFixtures[\"plugin\"]) => Promise<void>,\n ) => {\n await use(\n await _testContext.plugin(options.pluginId, {\n filter: options.pluginFilter,\n seedData: options.seedPluginData,\n }),\n );\n },\n };\n\n return base.extend<PluginFixtures & BaseFixtureState>(fixtures as never) as PluginTest;\n}\n\nasync function applyVaultSeed(obsidian: ObsidianClient, seedVault: VaultSeed): Promise<void> {\n const vault = createVaultApi({ obsidian });\n\n for (const [targetPath, value] of Object.entries(seedVault)) {\n const resolvedPath = await resolveFilesystemPath(obsidian, \"\", targetPath);\n await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);\n await writeSeedValue(vault, targetPath, value);\n }\n}\n\nasync function writeSeedValue(\n vault: VaultApi,\n targetPath: string,\n value: VaultSeedEntry,\n): Promise<void> {\n if (typeof value === \"string\") {\n await vault.write(targetPath, value, {\n waitForContent: true,\n });\n return;\n }\n\n if (\"note\" in value) {\n await vault.write(targetPath, createNoteDocument(value.note).raw, {\n waitForContent: true,\n });\n return;\n }\n\n await vault.write(targetPath, `${JSON.stringify(value.json, null, 2)}\\n`, {\n waitForContent: true,\n });\n}\n"],"mappings":";;;;AAKA,SAAgB,mBAAmB,SAAkD;AACnF,QAAOA,KAAK,OACV,mBAAmB,QAAQ,CAC5B;;;;ACQH,SAAgB,iBAAiB,SAA8C;CAC7E,MAAM,WAAW;EACf,GAAG,mBAAmB,SAAS,EAC7B,MAAM,YAAY,UAAU;AAC1B,OAAI,QAAQ,UACV,OAAM,eAAe,UAAU,QAAQ,UAAU;AAGnD,UAAO,eAAe,EAAE,UAAU,CAAC;KAEtC,CAAC;EACF,QAAQ,OACN,EAAE,gBACF,QACG;AACH,SAAM,IACJ,MAAM,aAAa,OAAO,QAAQ,UAAU;IAC1C,QAAQ,QAAQ;IAChB,UAAU,QAAQ;IACnB,CAAC,CACH;;EAEJ;AAED,QAAOC,KAAK,OAA0C,SAAkB;;AAG1E,eAAe,eAAe,UAA0B,WAAqC;CAC3F,MAAM,QAAQ,eAAe,EAAE,UAAU,CAAC;AAE1C,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,UAAU,EAAE;EAC3D,MAAM,eAAe,MAAM,sBAAsB,UAAU,IAAI,WAAW;AAC1E,QAAM,mBAAmB,SAAS,CAAC,iBAAiB,aAAa;AACjE,QAAM,eAAe,OAAO,YAAY,MAAM;;;AAIlD,eAAe,eACb,OACA,YACA,OACe;AACf,KAAI,OAAO,UAAU,UAAU;AAC7B,QAAM,MAAM,MAAM,YAAY,OAAO,EACnC,gBAAgB,MACjB,CAAC;AACF;;AAGF,KAAI,UAAU,OAAO;AACnB,QAAM,MAAM,MAAM,YAAY,mBAAmB,MAAM,KAAK,CAAC,KAAK,EAChE,gBAAgB,MACjB,CAAC;AACF;;AAGF,OAAM,MAAM,MAAM,YAAY,GAAG,KAAK,UAAU,MAAM,MAAM,MAAM,EAAE,CAAC,KAAK,EACxE,gBAAgB,MACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-e2e",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Vitest-first end-to-end test utilities for Obsidian plugins.",
5
5
  "homepage": "https://github.com/chhoumann/obsidian-e2e#readme",
6
6
  "bugs": {
@@ -35,6 +35,9 @@
35
35
  },
36
36
  "./package.json": "./package.json"
37
37
  },
38
+ "dependencies": {
39
+ "yaml": "^2.8.2"
40
+ },
38
41
  "devDependencies": {
39
42
  "@changesets/cli": "^2.30.0",
40
43
  "@types/node": "^25.3.5",
@@ -1 +0,0 @@
1
- {"version":3,"file":"sandbox--mUbNsh7.mjs","names":["DEFAULT_TIMEOUT_MS","DEFAULT_TIMEOUT_MS","sleep","sleep","pathPosix","pathPosix"],"sources":["../src/core/args.ts","../src/core/exec-options.ts","../src/core/internals.ts","../src/vault/json-file.ts","../src/plugin/plugin.ts","../src/core/errors.ts","../src/core/transport.ts","../src/core/wait.ts","../src/core/client.ts","../src/artifacts/failure-artifacts.ts","../src/fixtures/vault-lock.ts","../src/vault/paths.ts","../src/vault/vault.ts","../src/vault/sandbox.ts"],"sourcesContent":["import type { ObsidianArg } from \"./types\";\n\nexport function buildCommandArgv(\n vaultName: string,\n command: string,\n args: Record<string, ObsidianArg> = {},\n): string[] {\n const argv = [`vault=${vaultName}`, command];\n\n for (const [key, value] of Object.entries(args)) {\n if (value === false || value === null || value === undefined) {\n continue;\n }\n\n if (value === true) {\n argv.push(key);\n continue;\n }\n\n argv.push(`${key}=${String(value)}`);\n }\n\n return argv;\n}\n","import type { ExecOptions } from \"./types\";\n\nexport function mergeExecOptions(\n defaults: ExecOptions | undefined,\n overrides: ExecOptions | undefined,\n): ExecOptions {\n if (!defaults) {\n return overrides ? { ...overrides } : {};\n }\n\n if (!overrides) {\n return { ...defaults };\n }\n\n return {\n ...defaults,\n ...overrides,\n env: mergeEnvironments(defaults.env, overrides.env),\n };\n}\n\nfunction mergeEnvironments(\n defaults: NodeJS.ProcessEnv | undefined,\n overrides: NodeJS.ProcessEnv | undefined,\n): NodeJS.ProcessEnv | undefined {\n if (!defaults) {\n return overrides ? { ...overrides } : undefined;\n }\n\n if (!overrides) {\n return { ...defaults };\n }\n\n return {\n ...defaults,\n ...overrides,\n };\n}\n","import { rm, writeFile } from \"node:fs/promises\";\n\nimport type { ObsidianClient } from \"./types\";\n\ninterface SnapshotEntry {\n exists: boolean;\n value: string;\n}\n\ninterface ClientInternals {\n restoreAll(): Promise<void>;\n restoreFile(filePath: string): Promise<void>;\n snapshotFileOnce(filePath: string): Promise<void>;\n}\n\nconst clientInternals = new WeakMap<ObsidianClient, ClientInternals>();\n\nexport function attachClientInternals(client: ObsidianClient, internals: ClientInternals): void {\n clientInternals.set(client, internals);\n}\n\nexport function getClientInternals(client: ObsidianClient): ClientInternals {\n const internals = clientInternals.get(client);\n\n if (!internals) {\n throw new Error(\"Missing obsidian client internals.\");\n }\n\n return internals;\n}\n\nexport function createRestoreManager(readFile: (filePath: string) => Promise<string>) {\n const snapshots = new Map<string, SnapshotEntry>();\n\n return {\n async restoreAll() {\n const entries = [...snapshots.entries()].reverse();\n\n for (const [filePath, snapshot] of entries) {\n await restoreSnapshot(filePath, snapshot);\n }\n\n snapshots.clear();\n },\n async restoreFile(filePath: string) {\n const snapshot = snapshots.get(filePath);\n\n if (!snapshot) {\n return;\n }\n\n await restoreSnapshot(filePath, snapshot);\n snapshots.delete(filePath);\n },\n async snapshotFileOnce(filePath: string) {\n if (snapshots.has(filePath)) {\n return;\n }\n\n try {\n snapshots.set(filePath, {\n exists: true,\n value: await readFile(filePath),\n });\n } catch (error) {\n if (isMissingFileError(error)) {\n snapshots.set(filePath, {\n exists: false,\n value: \"\",\n });\n return;\n }\n\n throw error;\n }\n },\n };\n}\n\nasync function restoreSnapshot(filePath: string, snapshot: SnapshotEntry): Promise<void> {\n if (snapshot.exists) {\n await writeFile(filePath, snapshot.value, \"utf8\");\n return;\n }\n\n await rm(filePath, { force: true, recursive: true });\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return Boolean(error && typeof error === \"object\" && \"code\" in error && error.code === \"ENOENT\");\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { JsonFile, JsonFileUpdater } from \"../core/types\";\n\nexport function createJsonFile<T = unknown>(\n filePath: string,\n beforeMutate?: () => Promise<void>,\n): JsonFile<T> {\n return {\n async patch(updater: JsonFileUpdater<T>) {\n await beforeMutate?.();\n\n const currentValue = await this.read();\n const draft = structuredClone(currentValue);\n const result = await updater(draft);\n const nextValue = result ?? draft;\n\n await this.write(nextValue);\n\n return nextValue;\n },\n async read() {\n const value = await readFile(filePath, \"utf8\");\n return JSON.parse(value) as T;\n },\n async write(value: T) {\n await beforeMutate?.();\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n },\n };\n}\n","import path from \"node:path\";\n\nimport { getClientInternals } from \"../core/internals\";\nimport type {\n JsonFile,\n ObsidianClient,\n PluginDataPredicate,\n PluginHandle,\n PluginReloadOptions,\n PluginToggleOptions,\n PluginWaitForDataOptions,\n PluginWaitUntilReadyOptions,\n} from \"../core/types\";\nimport { createJsonFile } from \"../vault/json-file\";\n\nexport function createPluginHandle(client: ObsidianClient, id: string): PluginHandle {\n async function resolveDataPath() {\n const vaultPath = await client.vaultPath();\n return path.join(vaultPath, \".obsidian\", \"plugins\", id, \"data.json\");\n }\n\n async function isLoadedInApp(): Promise<boolean> {\n try {\n return await client.dev.eval<boolean>(`(() => {\n const plugins = app?.plugins;\n return Boolean(\n plugins?.enabledPlugins?.has?.(${JSON.stringify(id)}) &&\n plugins?.plugins?.[${JSON.stringify(id)}],\n );\n })()`);\n } catch {\n return false;\n }\n }\n\n return {\n data<T = unknown>(): JsonFile<T> {\n return {\n async patch(updater) {\n const dataPath = await resolveDataPath();\n return createJsonFile<T>(dataPath, () =>\n getClientInternals(client).snapshotFileOnce(dataPath),\n ).patch(updater);\n },\n async read() {\n const dataPath = await resolveDataPath();\n return createJsonFile<T>(dataPath).read();\n },\n async write(value) {\n const dataPath = await resolveDataPath();\n await createJsonFile<T>(dataPath, () =>\n getClientInternals(client).snapshotFileOnce(dataPath),\n ).write(value);\n },\n };\n },\n async dataPath() {\n return resolveDataPath();\n },\n async disable(options: PluginToggleOptions = {}) {\n await client.exec(\"plugin:disable\", {\n filter: options.filter,\n id,\n });\n },\n async enable(options: PluginToggleOptions = {}) {\n await client.exec(\"plugin:enable\", {\n filter: options.filter,\n id,\n });\n },\n id,\n async isEnabled() {\n const output = await client.execText(\"plugin\", { id }, { allowNonZeroExit: true });\n return /enabled\\s+true/i.test(output);\n },\n async reload(options: PluginReloadOptions = {}) {\n const { readyOptions, waitUntilReady, ...execOptions } = options;\n\n await client.exec(\"plugin:reload\", { id }, execOptions);\n\n if (waitUntilReady) {\n await this.waitUntilReady(readyOptions);\n }\n },\n async restoreData() {\n await getClientInternals(client).restoreFile(await resolveDataPath());\n },\n async waitForData<T = unknown>(\n predicate: PluginDataPredicate<T>,\n options: PluginWaitForDataOptions = {},\n ) {\n return client.waitFor(async () => {\n try {\n const data = await this.data<T>().read();\n return (await predicate(data)) ? data : false;\n } catch {\n return false;\n }\n }, options);\n },\n async waitUntilReady(options: PluginWaitUntilReadyOptions = {}) {\n await client.waitFor(\n async () => {\n if (!(await isLoadedInApp())) {\n return false;\n }\n\n if (options.commandId) {\n return await client.command(options.commandId).exists();\n }\n\n return true;\n },\n {\n ...options,\n message:\n options.message ??\n (options.commandId\n ? `plugin \"${id}\" to be ready with command \"${options.commandId}\"`\n : `plugin \"${id}\" to be ready`),\n },\n );\n },\n };\n}\n","import type { ExecResult } from \"./types\";\n\nexport class ObsidianCommandError extends Error {\n readonly result: ExecResult;\n\n constructor(message: string, result: ExecResult) {\n super(message);\n this.name = \"ObsidianCommandError\";\n this.result = result;\n }\n}\n\nexport class WaitForTimeoutError extends Error {\n readonly causeError?: unknown;\n\n constructor(message: string, causeError?: unknown) {\n super(message);\n this.name = \"WaitForTimeoutError\";\n this.causeError = causeError;\n }\n}\n","import { spawn } from \"node:child_process\";\n\nimport { ObsidianCommandError } from \"./errors\";\nimport type { CommandTransport, ExecuteRequest, ExecResult } from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport const executeCommand: CommandTransport = async ({\n allowNonZeroExit = false,\n argv,\n bin,\n cwd,\n env,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n}: ExecuteRequest): Promise<ExecResult> => {\n const child = spawn(bin, argv, {\n cwd,\n env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n child.stdout.on(\"data\", (chunk) => {\n stdoutChunks.push(Buffer.from(chunk));\n });\n\n child.stderr.on(\"data\", (chunk) => {\n stderrChunks.push(Buffer.from(chunk));\n });\n\n const exitCode = await new Promise<number>((resolve, reject) => {\n const timer = setTimeout(() => {\n child.kill(\"SIGTERM\");\n reject(new Error(`Command timed out after ${timeoutMs}ms: ${bin} ${argv.join(\" \")}`));\n }, timeoutMs);\n\n child.on(\"error\", (error) => {\n clearTimeout(timer);\n reject(error);\n });\n\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n resolve(code ?? 0);\n });\n });\n\n const result: ExecResult = {\n argv,\n command: bin,\n exitCode,\n stderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n stdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n };\n\n if (exitCode !== 0 && !allowNonZeroExit) {\n throw new ObsidianCommandError(\n `Obsidian command failed with exit code ${exitCode}: ${bin} ${argv.join(\" \")}`,\n result,\n );\n }\n\n return result;\n};\n","import { WaitForTimeoutError } from \"./errors\";\nimport type { WaitForOptions } from \"./types\";\n\nconst DEFAULT_INTERVAL_MS = 100;\nconst DEFAULT_TIMEOUT_MS = 5_000;\n\nexport async function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function waitForValue<T>(\n fn: () => Promise<T | false | null | undefined> | T | false | null | undefined,\n options: WaitForOptions = {},\n): Promise<T> {\n const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const startTime = Date.now();\n\n let lastError: unknown;\n\n while (Date.now() - startTime <= timeoutMs) {\n try {\n const result = await fn();\n if (result !== false && result !== null && result !== undefined) {\n return result;\n }\n } catch (error) {\n lastError = error;\n }\n\n await sleep(intervalMs);\n }\n\n const label = options.message ?? \"condition\";\n throw new WaitForTimeoutError(`Timed out waiting for ${label} after ${timeoutMs}ms.`, lastError);\n}\n","import { buildCommandArgv } from \"./args\";\nimport { mergeExecOptions } from \"./exec-options\";\nimport { attachClientInternals, createRestoreManager } from \"./internals\";\nimport { createPluginHandle } from \"../plugin/plugin\";\nimport { executeCommand } from \"./transport\";\nimport type {\n CommandListOptions,\n CreateObsidianClientOptions,\n DevDomQueryOptions,\n DevDomResult,\n ExecOptions,\n ObsidianArg,\n ObsidianAppHandle,\n ObsidianCommandHandle,\n ObsidianClient,\n ObsidianDevHandle,\n OpenFileOptions,\n OpenTabOptions,\n RestartAppOptions,\n TabsOptions,\n WaitForOptions,\n WorkspaceNode,\n WorkspaceOptions,\n WorkspaceTab,\n} from \"./types\";\nimport { sleep, waitForValue } from \"./wait\";\n\nexport function createObsidianClient(options: CreateObsidianClientOptions): ObsidianClient {\n const transport = options.transport ?? executeCommand;\n const defaultExecOptions = options.defaultExecOptions;\n const waitDefaults = {\n intervalMs: options.intervalMs,\n timeoutMs: options.timeoutMs,\n };\n\n const restoreManager = createRestoreManager(async (filePath) => {\n const { readFile } = await import(\"node:fs/promises\");\n return readFile(filePath, \"utf8\");\n });\n\n let cachedVaultPath: string | undefined;\n\n const client = {} as ObsidianClient;\n\n const app: ObsidianAppHandle = {\n async reload(execOptions: ExecOptions = {}) {\n await client.exec(\"reload\", {}, execOptions);\n },\n async restart({\n readyOptions,\n waitUntilReady = true,\n ...execOptions\n }: RestartAppOptions & ExecOptions = {}) {\n await client.exec(\"restart\", {}, execOptions);\n\n if (waitUntilReady) {\n await app.waitUntilReady(readyOptions);\n }\n },\n version(execOptions: ExecOptions = {}) {\n return client.execText(\"version\", {}, execOptions);\n },\n async waitUntilReady(waitOptions?: WaitForOptions) {\n await client.waitFor(async () => {\n try {\n await client.vaultPath();\n await client.commands();\n return true;\n } catch {\n return false;\n }\n }, waitOptions);\n },\n };\n\n const dev: ObsidianDevHandle = {\n async dom(options: DevDomQueryOptions, execOptions: ExecOptions = {}): Promise<DevDomResult> {\n const output = await client.execText(\n \"dev:dom\",\n {\n all: options.all,\n attr: options.attr,\n css: options.css,\n inner: options.inner,\n selector: options.selector,\n text: options.text,\n total: options.total,\n },\n execOptions,\n );\n\n if (options.total) {\n return Number.parseInt(output, 10);\n }\n\n if (options.all) {\n return output ? output.split(/\\r?\\n/u).filter(Boolean) : [];\n }\n\n return output;\n },\n async eval<T = unknown>(code: string, execOptions: ExecOptions = {}) {\n const output = await client.execText(\n \"eval\",\n {\n code,\n },\n execOptions,\n );\n return parseDevEvalOutput<T>(output);\n },\n async screenshot(targetPath: string, execOptions: ExecOptions = {}) {\n await client.exec(\n \"dev:screenshot\",\n {\n path: targetPath,\n },\n execOptions,\n );\n\n return targetPath;\n },\n };\n\n Object.assign(client, {\n app,\n bin: options.bin ?? \"obsidian\",\n dev,\n command(id: string): ObsidianCommandHandle {\n return {\n async exists(commandOptions: CommandListOptions = {}) {\n const commands = await client.commands({\n ...commandOptions,\n filter: commandOptions.filter ?? id,\n });\n\n return commands.includes(id);\n },\n id,\n async run(execOptions: ExecOptions = {}) {\n await client.exec(\"command\", { id }, execOptions);\n },\n };\n },\n async commands(\n commandOptions: CommandListOptions = {},\n execOptions: ExecOptions = {},\n ): Promise<string[]> {\n const output = await client.execText(\n \"commands\",\n {\n filter: commandOptions.filter,\n },\n execOptions,\n );\n return parseCommandIds(output);\n },\n exec(command: string, args: Record<string, ObsidianArg> = {}, execOptions: ExecOptions = {}) {\n return transport({\n ...mergeExecOptions(defaultExecOptions, execOptions),\n argv: buildCommandArgv(options.vault, command, args),\n bin: this.bin,\n });\n },\n async execJson<T = unknown>(\n command: string,\n args: Record<string, ObsidianArg> = {},\n execOptions: ExecOptions = {},\n ) {\n const output = await this.execText(command, args, execOptions);\n return JSON.parse(output) as T;\n },\n async execText(\n command: string,\n args: Record<string, ObsidianArg> = {},\n execOptions: ExecOptions = {},\n ) {\n const result = await this.exec(command, args, execOptions);\n return result.stdout.trimEnd();\n },\n async open(openOptions: OpenFileOptions, execOptions: ExecOptions = {}) {\n await client.exec(\n \"open\",\n {\n file: openOptions.file,\n newtab: openOptions.newTab,\n path: openOptions.path,\n },\n execOptions,\n );\n },\n async openTab(tabOptions: OpenTabOptions = {}, execOptions: ExecOptions = {}) {\n await client.exec(\n \"tab:open\",\n {\n file: tabOptions.file,\n group: tabOptions.group,\n view: tabOptions.view,\n },\n execOptions,\n );\n },\n plugin(id: string) {\n return createPluginHandle(this, id);\n },\n sleep(ms: number) {\n return sleep(ms);\n },\n async tabs(\n tabOptions: TabsOptions = {},\n execOptions: ExecOptions = {},\n ): Promise<WorkspaceTab[]> {\n const output = await client.execText(\n \"tabs\",\n {\n ids: tabOptions.ids ?? true,\n },\n execOptions,\n );\n return parseTabs(output);\n },\n async vaultPath() {\n if (!cachedVaultPath) {\n cachedVaultPath = await this.execText(\"vault\", { info: \"path\" });\n }\n\n return cachedVaultPath;\n },\n async verify() {\n await transport({\n ...mergeExecOptions(defaultExecOptions, undefined),\n argv: [\"--help\"],\n bin: this.bin,\n });\n\n await this.vaultPath();\n },\n vaultName: options.vault,\n waitFor<T>(\n fn: () => Promise<T | false | null | undefined> | T | false | null | undefined,\n waitOptions?: WaitForOptions,\n ) {\n return waitForValue(fn, {\n ...waitDefaults,\n ...waitOptions,\n });\n },\n async workspace(\n workspaceOptions: WorkspaceOptions = {},\n execOptions: ExecOptions = {},\n ): Promise<WorkspaceNode[]> {\n const output = await client.execText(\n \"workspace\",\n {\n ids: workspaceOptions.ids ?? true,\n },\n execOptions,\n );\n return parseWorkspace(output);\n },\n });\n\n attachClientInternals(client, restoreManager);\n\n return client;\n}\n\nfunction parseCommandIds(output: string): string[] {\n return output\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => line.split(\"\\t\", 1)[0]?.trim() ?? \"\")\n .filter(Boolean);\n}\n\nfunction parseDevEvalOutput<T>(output: string): T {\n const normalized = output.startsWith(\"=> \") ? output.slice(3) : output;\n\n try {\n return JSON.parse(normalized) as T;\n } catch {\n return normalized as T;\n }\n}\n\nfunction parseTabs(output: string): WorkspaceTab[] {\n return output\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter(Boolean)\n .map(parseTabLine);\n}\n\nfunction parseTabLine(line: string): WorkspaceTab {\n const [descriptor, id] = line.split(\"\\t\");\n const match = descriptor?.match(/^\\[(.+?)\\]\\s+(.*)$/u);\n\n if (!match) {\n return {\n id: id?.trim() || undefined,\n title: descriptor?.trim() ?? \"\",\n viewType: \"unknown\",\n };\n }\n\n return {\n id: id?.trim() || undefined,\n title: match[2]!,\n viewType: match[1]!,\n };\n}\n\nfunction parseWorkspace(output: string): WorkspaceNode[] {\n const roots: WorkspaceNode[] = [];\n const stack: Array<{ depth: number; node: WorkspaceNode }> = [];\n\n for (const rawLine of output.split(/\\r?\\n/u)) {\n if (!rawLine.trim()) {\n continue;\n }\n\n const depth = getWorkspaceDepth(rawLine);\n const node = parseWorkspaceNode(rawLine);\n\n while (stack.length > 0 && stack.at(-1)!.depth >= depth) {\n stack.pop();\n }\n\n const parent = stack.at(-1)?.node;\n\n if (parent) {\n parent.children.push(node);\n } else {\n roots.push(node);\n }\n\n stack.push({ depth, node });\n }\n\n return roots;\n}\n\nfunction getWorkspaceDepth(line: string): number {\n let depth = 0;\n let remainder = line;\n\n while (true) {\n if (\n remainder.startsWith(\"│ \") ||\n remainder.startsWith(\" \") ||\n remainder.startsWith(\"├── \") ||\n remainder.startsWith(\"└── \")\n ) {\n depth += 1;\n remainder = remainder.slice(4);\n continue;\n }\n\n return depth;\n }\n}\n\nfunction parseWorkspaceNode(line: string): WorkspaceNode {\n let withoutTree = line;\n\n while (true) {\n if (\n withoutTree.startsWith(\"│ \") ||\n withoutTree.startsWith(\" \") ||\n withoutTree.startsWith(\"├── \") ||\n withoutTree.startsWith(\"└── \")\n ) {\n withoutTree = withoutTree.slice(4);\n continue;\n }\n\n break;\n }\n\n withoutTree = withoutTree.trim();\n const idMatch = withoutTree.match(/^(.*?)(?: \\(([a-z0-9]+)\\))?$/iu);\n const content = idMatch?.[1]?.trim() ?? withoutTree;\n const id = idMatch?.[2];\n const leafMatch = content.match(/^\\[(.+?)\\]\\s+(.*)$/u);\n\n if (leafMatch) {\n return {\n children: [],\n id,\n label: leafMatch[2]!,\n title: leafMatch[2]!,\n viewType: leafMatch[1]!,\n };\n }\n\n return {\n children: [],\n id,\n label: content,\n };\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { ObsidianClient, PluginHandle } from \"../core/types\";\n\nexport const DEFAULT_FAILURE_ARTIFACTS_DIR = \".obsidian-e2e-artifacts\";\n\nexport interface FailureArtifactOptions {\n activeFile?: boolean;\n dom?: boolean;\n editorText?: boolean;\n screenshot?: boolean;\n tabs?: boolean;\n workspace?: boolean;\n}\n\nexport interface FailureArtifactTask {\n id: string;\n name: string;\n}\n\nexport interface FailureArtifactConfig {\n artifactsDir: string;\n capture: Required<FailureArtifactOptions>;\n enabled: boolean;\n}\n\nexport interface FailureArtifactRegistrationOptions {\n artifactsDir?: string;\n captureOnFailure?: boolean | FailureArtifactOptions;\n}\n\nexport interface CaptureFailureArtifactsOptions extends FailureArtifactRegistrationOptions {\n plugin?: PluginHandle;\n}\n\nexport function getFailureArtifactConfig(\n options: FailureArtifactRegistrationOptions,\n): FailureArtifactConfig {\n if (!options.captureOnFailure) {\n return {\n artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_FAILURE_ARTIFACTS_DIR),\n capture: {\n activeFile: true,\n dom: true,\n editorText: true,\n screenshot: true,\n tabs: true,\n workspace: true,\n },\n enabled: false,\n };\n }\n\n const overrides = options.captureOnFailure === true ? {} : options.captureOnFailure;\n\n return {\n artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_FAILURE_ARTIFACTS_DIR),\n capture: {\n activeFile: overrides.activeFile ?? true,\n dom: overrides.dom ?? true,\n editorText: overrides.editorText ?? true,\n screenshot: overrides.screenshot ?? true,\n tabs: overrides.tabs ?? true,\n workspace: overrides.workspace ?? true,\n },\n enabled: true,\n };\n}\n\nexport function getFailureArtifactDirectory(\n artifactsDir: string,\n task: FailureArtifactTask,\n): string {\n const suffix = task.id.split(\"_\").at(-1) ?? \"test\";\n return path.join(artifactsDir, `${sanitizeForPath(task.name)}-${suffix}`);\n}\n\nexport async function captureFailureArtifacts(\n task: FailureArtifactTask,\n obsidian: ObsidianClient,\n options: CaptureFailureArtifactsOptions,\n): Promise<string | undefined> {\n const config = getFailureArtifactConfig(options);\n\n if (!config.enabled) {\n return undefined;\n }\n\n const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, task);\n await mkdir(artifactDirectory, { recursive: true });\n\n await Promise.all([\n captureJsonArtifact(\n artifactDirectory,\n \"active-file.json\",\n config.capture.activeFile,\n async () => ({\n activeFile: await obsidian.dev.eval<string | null>(\n \"app.workspace.getActiveFile()?.path ?? null\",\n ),\n }),\n ),\n captureTextArtifact(artifactDirectory, \"dom.txt\", config.capture.dom, async () =>\n String(\n await obsidian.dev.dom({\n inner: true,\n selector: \".workspace\",\n }),\n ),\n ),\n captureJsonArtifact(artifactDirectory, \"editor.json\", config.capture.editorText, async () => ({\n text: await obsidian.dev.eval<string | null>(\n \"app.workspace.activeLeaf?.view?.editor?.getValue?.() ?? null\",\n ),\n })),\n captureScreenshotArtifact(artifactDirectory, config.capture.screenshot, obsidian),\n captureJsonArtifact(artifactDirectory, \"tabs.json\", config.capture.tabs, () => obsidian.tabs()),\n captureJsonArtifact(artifactDirectory, \"workspace.json\", config.capture.workspace, () =>\n obsidian.workspace(),\n ),\n options.plugin\n ? captureJsonArtifact(artifactDirectory, `${options.plugin.id}-data.json`, true, () =>\n options.plugin!.data().read(),\n )\n : Promise.resolve(),\n ]);\n\n return artifactDirectory;\n}\n\nexport async function capturePluginFailureArtifacts(\n task: FailureArtifactTask,\n plugin: PluginHandle,\n options: FailureArtifactRegistrationOptions,\n): Promise<string | undefined> {\n const config = getFailureArtifactConfig(options);\n\n if (!config.enabled) {\n return undefined;\n }\n\n const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, task);\n await mkdir(artifactDirectory, { recursive: true });\n await captureJsonArtifact(artifactDirectory, `${plugin.id}-data.json`, true, () =>\n plugin.data().read(),\n );\n\n return artifactDirectory;\n}\n\nasync function captureJsonArtifact(\n artifactDirectory: string,\n filename: string,\n enabled: boolean,\n readValue: () => Promise<unknown>,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n try {\n const value = await readValue();\n await writeFile(\n path.join(artifactDirectory, filename),\n `${JSON.stringify(value, null, 2)}\\n`,\n \"utf8\",\n );\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, `${filename}.error.txt`),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nasync function captureScreenshotArtifact(\n artifactDirectory: string,\n enabled: boolean,\n obsidian: ObsidianClient,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n const screenshotPath = path.join(artifactDirectory, \"screenshot.png\");\n\n try {\n await obsidian.dev.screenshot(screenshotPath);\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, \"screenshot.error.txt\"),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nasync function captureTextArtifact(\n artifactDirectory: string,\n filename: string,\n enabled: boolean,\n readValue: () => Promise<string>,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n try {\n await writeFile(path.join(artifactDirectory, filename), await readValue(), \"utf8\");\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, `${filename}.error.txt`),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nfunction formatArtifactError(error: unknown): string {\n return error instanceof Error ? `${error.name}: ${error.message}\\n` : `${String(error)}\\n`;\n}\n\nfunction sanitizeForPath(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"test\"\n );\n}\n","import { mkdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { createHash, randomUUID } from \"node:crypto\";\n\nimport type { ObsidianClient } from \"../core/types\";\nimport type { SharedVaultLockOptions } from \"./types\";\n\nconst DEFAULT_HEARTBEAT_MS = 2_000;\nconst DEFAULT_STALE_MS = 15_000;\nconst DEFAULT_TIMEOUT_MS = 60_000;\nconst DEFAULT_WAIT_INTERVAL_MS = 500;\nconst DEFAULT_LOCK_ROOT = path.join(os.tmpdir(), \"obsidian-e2e-locks\");\nconst LOCK_METADATA_FILE = \"lock.json\";\nconst APP_LOCK_KEY = \"__obsidianE2ELock\";\nconst heldLocks = new Map<string, HeldVaultRunLock>();\n\nexport interface VaultRunLockMetadata {\n acquiredAt: number;\n cwd: string;\n heartbeatAt: number;\n hostname: string;\n ownerId: string;\n pid: number;\n staleMs: number;\n vaultName: string;\n vaultPath: string;\n}\n\nexport interface VaultRunLock {\n readonly lockDir: string;\n readonly metadata: VaultRunLockMetadata;\n\n publishMarker(obsidian: ObsidianClient): Promise<void>;\n release(): Promise<void>;\n}\n\nexport interface VaultRunLockState {\n heartbeatAgeMs: number;\n isStale: boolean;\n lockDir: string;\n metadata: VaultRunLockMetadata;\n}\n\nexport interface AcquireVaultRunLockOptions extends SharedVaultLockOptions {\n vaultName: string;\n vaultPath: string;\n}\n\ninterface HeldVaultRunLock {\n heartbeat: NodeJS.Timeout;\n lockDir: string;\n metadata: VaultRunLockMetadata;\n metadataPath: string;\n refs: number;\n}\n\nexport async function acquireVaultRunLock({\n heartbeatMs = DEFAULT_HEARTBEAT_MS,\n lockRoot = DEFAULT_LOCK_ROOT,\n onBusy = \"wait\",\n staleMs = DEFAULT_STALE_MS,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n vaultName,\n vaultPath,\n}: AcquireVaultRunLockOptions): Promise<VaultRunLock> {\n const lockDir = path.join(lockRoot, createVaultLockKey(vaultPath));\n const heldLock = heldLocks.get(lockDir);\n\n if (heldLock) {\n heldLock.refs += 1;\n return createVaultRunLockHandle(heldLock);\n }\n\n const ownerId = randomUUID();\n const metadataPath = path.join(lockDir, LOCK_METADATA_FILE);\n const metadata: VaultRunLockMetadata = {\n acquiredAt: Date.now(),\n cwd: process.cwd(),\n heartbeatAt: Date.now(),\n hostname: os.hostname(),\n ownerId,\n pid: process.pid,\n staleMs,\n vaultName,\n vaultPath,\n };\n\n await mkdir(lockRoot, { recursive: true });\n\n const startedAt = Date.now();\n\n while (true) {\n try {\n await mkdir(lockDir);\n await writeMetadata(metadataPath, metadata);\n break;\n } catch (error) {\n if (!isAlreadyExistsError(error)) {\n throw error;\n }\n\n const currentLock = await inspectVaultRunLock({\n lockRoot,\n staleMs,\n vaultPath,\n });\n\n if (currentLock && !currentLock.isStale) {\n if (onBusy === \"fail\") {\n throw new Error(formatBusyLockMessage(vaultPath, currentLock));\n }\n } else {\n await rm(lockDir, { force: true, recursive: true });\n continue;\n }\n\n if (Date.now() - startedAt >= timeoutMs) {\n throw new Error(\n currentLock\n ? `Timed out waiting for shared vault lock: ${formatBusyLockMessage(vaultPath, currentLock)}`\n : `Timed out waiting for shared vault lock on ${vaultPath}`,\n );\n }\n\n await sleep(Math.min(DEFAULT_WAIT_INTERVAL_MS, heartbeatMs));\n }\n }\n\n const heartbeat = setInterval(() => {\n metadata.heartbeatAt = Date.now();\n void writeMetadata(metadataPath, metadata).catch(() => {});\n }, heartbeatMs);\n heartbeat.unref();\n\n const nextHeldLock: HeldVaultRunLock = {\n heartbeat,\n lockDir,\n metadata,\n metadataPath,\n refs: 1,\n };\n\n heldLocks.set(lockDir, nextHeldLock);\n return createVaultRunLockHandle(nextHeldLock);\n}\n\nexport async function clearVaultRunLockMarker(obsidian: ObsidianClient): Promise<void> {\n await obsidian.dev.eval(`delete window.${APP_LOCK_KEY}; delete app.${APP_LOCK_KEY}; \"cleared\"`, {\n allowNonZeroExit: true,\n });\n}\n\nexport async function inspectVaultRunLock({\n lockRoot = DEFAULT_LOCK_ROOT,\n staleMs = DEFAULT_STALE_MS,\n vaultPath,\n}: Pick<\n AcquireVaultRunLockOptions,\n \"lockRoot\" | \"staleMs\" | \"vaultPath\"\n>): Promise<VaultRunLockState | null> {\n const lockDir = path.join(lockRoot, createVaultLockKey(vaultPath));\n const metadata = await readLockState(lockDir);\n\n if (!metadata) {\n return null;\n }\n\n return {\n heartbeatAgeMs: Date.now() - metadata.heartbeatAt,\n isStale: isLockStale(metadata, staleMs),\n lockDir,\n metadata,\n };\n}\n\nexport async function readVaultRunLockMarker(\n obsidian: ObsidianClient,\n): Promise<VaultRunLockMetadata | null> {\n return obsidian.dev.eval<VaultRunLockMetadata | null>(\n `window.${APP_LOCK_KEY} ?? app.${APP_LOCK_KEY} ?? null`,\n { allowNonZeroExit: true },\n );\n}\n\nfunction createVaultLockKey(vaultPath: string): string {\n return createHash(\"sha256\").update(path.resolve(vaultPath)).digest(\"hex\");\n}\n\nfunction buildSetMarkerCode(metadata: VaultRunLockMetadata): string {\n const encodedMetadata = JSON.stringify(metadata);\n return `(() => {\n const lock = ${encodedMetadata};\n window.${APP_LOCK_KEY} = lock;\n app.${APP_LOCK_KEY} = lock;\n return lock;\n })()`;\n}\n\nfunction createVaultRunLockHandle(heldLock: HeldVaultRunLock): VaultRunLock {\n return {\n get lockDir() {\n return heldLock.lockDir;\n },\n get metadata() {\n return heldLock.metadata;\n },\n async publishMarker(obsidian: ObsidianClient) {\n await obsidian.dev.eval(buildSetMarkerCode(heldLock.metadata));\n },\n async release() {\n if (heldLock.refs > 1) {\n heldLock.refs -= 1;\n return;\n }\n\n heldLocks.delete(heldLock.lockDir);\n clearInterval(heldLock.heartbeat);\n\n const currentLock = await readLockState(heldLock.lockDir);\n\n if (currentLock?.ownerId !== heldLock.metadata.ownerId) {\n return;\n }\n\n await rm(heldLock.lockDir, { force: true, recursive: true });\n },\n };\n}\n\nasync function readLockState(lockDir: string): Promise<VaultRunLockMetadata | null> {\n const metadataPath = path.join(lockDir, LOCK_METADATA_FILE);\n\n try {\n return JSON.parse(await readFile(metadataPath, \"utf8\")) as VaultRunLockMetadata;\n } catch {\n try {\n const directoryStat = await stat(lockDir);\n return {\n acquiredAt: directoryStat.mtimeMs,\n cwd: \"\",\n heartbeatAt: directoryStat.mtimeMs,\n hostname: \"\",\n ownerId: \"\",\n pid: 0,\n staleMs: DEFAULT_STALE_MS,\n vaultName: \"\",\n vaultPath: \"\",\n };\n } catch {\n return null;\n }\n }\n}\n\nfunction formatBusyLockMessage(vaultPath: string, state: VaultRunLockState): string {\n const ownerDetails = state.metadata.ownerId\n ? `owner=${state.metadata.ownerId} pid=${state.metadata.pid} cwd=${state.metadata.cwd || \"<unknown>\"}`\n : \"owner=<unknown>\";\n const ageDetails = `heartbeatAgeMs=${state.heartbeatAgeMs} stale=${state.isStale}`;\n\n return `vault ${vaultPath} is locked by ${ownerDetails} ${ageDetails}`;\n}\n\nfunction isAlreadyExistsError(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"EEXIST\";\n}\n\nfunction isLockStale(metadata: VaultRunLockMetadata, staleMs: number): boolean {\n return Date.now() - metadata.heartbeatAt > staleMs;\n}\n\nasync function sleep(durationMs: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, durationMs));\n}\n\nasync function writeMetadata(metadataPath: string, metadata: VaultRunLockMetadata): Promise<void> {\n await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}\\n`, \"utf8\");\n}\n","import path from \"node:path\";\nimport { posix as pathPosix } from \"node:path\";\n\nimport type { ObsidianClient } from \"../core/types\";\n\nexport function normalizeScope(scope?: string): string {\n if (!scope || scope === \".\") {\n return \"\";\n }\n\n return scope.replace(/^\\/+|\\/+$/g, \"\");\n}\n\nexport function resolveVaultPath(scopeRoot: string, targetPath: string): string {\n if (!targetPath || targetPath === \".\") {\n return scopeRoot;\n }\n\n return scopeRoot ? pathPosix.join(scopeRoot, targetPath) : pathPosix.normalize(targetPath);\n}\n\nexport async function resolveFilesystemPath(\n obsidian: ObsidianClient,\n scopeRoot: string,\n targetPath: string,\n): Promise<string> {\n const vaultPath = await obsidian.vaultPath();\n const scopedPath = resolveVaultPath(scopeRoot, targetPath);\n const relativePath = scopedPath.split(\"/\").filter(Boolean);\n const resolvedPath = path.resolve(vaultPath, ...relativePath);\n const normalizedVaultPath = path.resolve(vaultPath);\n\n if (\n resolvedPath !== normalizedVaultPath &&\n !resolvedPath.startsWith(`${normalizedVaultPath}${path.sep}`)\n ) {\n throw new Error(`Resolved path escapes the vault root: ${targetPath}`);\n }\n\n return resolvedPath;\n}\n","import { access, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type {\n DeleteOptions,\n JsonFile,\n ObsidianClient,\n VaultApi,\n VaultWaitForContentOptions,\n VaultWriteOptions,\n} from \"../core/types\";\nimport { normalizeScope, resolveFilesystemPath, resolveVaultPath } from \"./paths\";\n\ninterface CreateVaultApiOptions {\n obsidian: ObsidianClient;\n root?: string;\n}\n\nexport function createVaultApi(options: CreateVaultApiOptions): VaultApi {\n const scopeRoot = normalizeScope(options.root);\n\n return {\n async delete(targetPath, deleteOptions: DeleteOptions = {}) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n await rm(resolvedPath, {\n force: true,\n recursive: true,\n });\n\n if (deleteOptions.permanent === false) {\n return;\n }\n },\n async exists(targetPath) {\n try {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n await access(resolvedPath);\n return true;\n } catch {\n return false;\n }\n },\n json<T = unknown>(targetPath: string) {\n const jsonFile: JsonFile<T> = {\n async patch(updater) {\n const currentValue = await jsonFile.read();\n const draft = structuredClone(currentValue);\n const result = await updater(draft);\n const nextValue = result ?? draft;\n\n await jsonFile.write(nextValue);\n\n return nextValue;\n },\n async read() {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n const rawValue = await readFile(resolvedPath, \"utf8\");\n return JSON.parse(rawValue) as T;\n },\n async write(value) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n await mkdir(path.dirname(resolvedPath), { recursive: true });\n await writeFile(resolvedPath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n },\n };\n\n return jsonFile;\n },\n async mkdir(targetPath) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n await mkdir(resolvedPath, { recursive: true });\n },\n async read(targetPath) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n return readFile(resolvedPath, \"utf8\");\n },\n async waitForContent(targetPath, predicate, waitOptions: VaultWaitForContentOptions = {}) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n\n return options.obsidian.waitFor(\n async () => {\n try {\n const content = await readFile(resolvedPath, \"utf8\");\n return (await predicate(content)) ? content : false;\n } catch {\n return false;\n }\n },\n {\n message: `vault path \"${resolveVaultPath(scopeRoot, targetPath)}\" to match content`,\n ...waitOptions,\n },\n );\n },\n async waitForExists(targetPath, waitOptions) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n\n await options.obsidian.waitFor(\n async () => {\n try {\n await access(resolvedPath);\n return true;\n } catch {\n return false;\n }\n },\n {\n message: `vault path \"${resolveVaultPath(scopeRoot, targetPath)}\" to exist`,\n ...waitOptions,\n },\n );\n },\n async waitForMissing(targetPath, waitOptions) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n\n await options.obsidian.waitFor(\n async () => {\n try {\n await access(resolvedPath);\n return false;\n } catch {\n return true;\n }\n },\n {\n message: `vault path \"${resolveVaultPath(scopeRoot, targetPath)}\" to be removed`,\n ...waitOptions,\n },\n );\n },\n async write(targetPath, content, writeOptions: VaultWriteOptions = {}) {\n const resolvedPath = await resolveFilesystemPath(options.obsidian, scopeRoot, targetPath);\n await mkdir(path.dirname(resolvedPath), { recursive: true });\n await writeFile(resolvedPath, content, \"utf8\");\n\n if (!writeOptions.waitForContent) {\n return;\n }\n\n const predicate =\n typeof writeOptions.waitForContent === \"function\"\n ? writeOptions.waitForContent\n : (value: string) => value === content;\n\n await this.waitForContent(targetPath, predicate, writeOptions.waitOptions);\n },\n };\n}\n","import { posix as pathPosix } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\nimport type { ObsidianClient, SandboxApi } from \"../core/types\";\nimport { createVaultApi } from \"./vault\";\n\ninterface CreateSandboxApiOptions {\n obsidian: ObsidianClient;\n sandboxRoot: string;\n testName: string;\n}\n\nexport async function createSandboxApi(options: CreateSandboxApiOptions): Promise<SandboxApi> {\n const root = pathPosix.join(\n options.sandboxRoot,\n `${sanitizeSegment(options.testName)}-${randomUUID().slice(0, 8)}`,\n );\n const vault = createVaultApi({\n obsidian: options.obsidian,\n root,\n });\n\n await vault.mkdir(\".\");\n\n return {\n ...vault,\n async cleanup() {\n await vault.delete(\".\", { permanent: true });\n },\n path(...segments: string[]) {\n return pathPosix.join(root, ...segments);\n },\n root,\n };\n}\n\nfunction sanitizeSegment(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 80) || \"test\"\n );\n}\n"],"mappings":";;;;;;AAEA,SAAgB,iBACd,WACA,SACA,OAAoC,EAAE,EAC5B;CACV,MAAM,OAAO,CAAC,SAAS,aAAa,QAAQ;AAE5C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;AAC/C,MAAI,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAA,EACjD;AAGF,MAAI,UAAU,MAAM;AAClB,QAAK,KAAK,IAAI;AACd;;AAGF,OAAK,KAAK,GAAG,IAAI,GAAG,OAAO,MAAM,GAAG;;AAGtC,QAAO;;;;ACpBT,SAAgB,iBACd,UACA,WACa;AACb,KAAI,CAAC,SACH,QAAO,YAAY,EAAE,GAAG,WAAW,GAAG,EAAE;AAG1C,KAAI,CAAC,UACH,QAAO,EAAE,GAAG,UAAU;AAGxB,QAAO;EACL,GAAG;EACH,GAAG;EACH,KAAK,kBAAkB,SAAS,KAAK,UAAU,IAAI;EACpD;;AAGH,SAAS,kBACP,UACA,WAC+B;AAC/B,KAAI,CAAC,SACH,QAAO,YAAY,EAAE,GAAG,WAAW,GAAG,KAAA;AAGxC,KAAI,CAAC,UACH,QAAO,EAAE,GAAG,UAAU;AAGxB,QAAO;EACL,GAAG;EACH,GAAG;EACJ;;;;ACrBH,MAAM,kCAAkB,IAAI,SAA0C;AAEtE,SAAgB,sBAAsB,QAAwB,WAAkC;AAC9F,iBAAgB,IAAI,QAAQ,UAAU;;AAGxC,SAAgB,mBAAmB,QAAyC;CAC1E,MAAM,YAAY,gBAAgB,IAAI,OAAO;AAE7C,KAAI,CAAC,UACH,OAAM,IAAI,MAAM,qCAAqC;AAGvD,QAAO;;AAGT,SAAgB,qBAAqB,UAAiD;CACpF,MAAM,4BAAY,IAAI,KAA4B;AAElD,QAAO;EACL,MAAM,aAAa;GACjB,MAAM,UAAU,CAAC,GAAG,UAAU,SAAS,CAAC,CAAC,SAAS;AAElD,QAAK,MAAM,CAAC,UAAU,aAAa,QACjC,OAAM,gBAAgB,UAAU,SAAS;AAG3C,aAAU,OAAO;;EAEnB,MAAM,YAAY,UAAkB;GAClC,MAAM,WAAW,UAAU,IAAI,SAAS;AAExC,OAAI,CAAC,SACH;AAGF,SAAM,gBAAgB,UAAU,SAAS;AACzC,aAAU,OAAO,SAAS;;EAE5B,MAAM,iBAAiB,UAAkB;AACvC,OAAI,UAAU,IAAI,SAAS,CACzB;AAGF,OAAI;AACF,cAAU,IAAI,UAAU;KACtB,QAAQ;KACR,OAAO,MAAM,SAAS,SAAS;KAChC,CAAC;YACK,OAAO;AACd,QAAI,mBAAmB,MAAM,EAAE;AAC7B,eAAU,IAAI,UAAU;MACtB,QAAQ;MACR,OAAO;MACR,CAAC;AACF;;AAGF,UAAM;;;EAGX;;AAGH,eAAe,gBAAgB,UAAkB,UAAwC;AACvF,KAAI,SAAS,QAAQ;AACnB,QAAM,UAAU,UAAU,SAAS,OAAO,OAAO;AACjD;;AAGF,OAAM,GAAG,UAAU;EAAE,OAAO;EAAM,WAAW;EAAM,CAAC;;AAGtD,SAAS,mBAAmB,OAAgD;AAC1E,QAAO,QAAQ,SAAS,OAAO,UAAU,YAAY,UAAU,SAAS,MAAM,SAAS,SAAS;;;;ACpFlG,SAAgB,eACd,UACA,cACa;AACb,QAAO;EACL,MAAM,MAAM,SAA6B;AACvC,SAAM,gBAAgB;GAEtB,MAAM,eAAe,MAAM,KAAK,MAAM;GACtC,MAAM,QAAQ,gBAAgB,aAAa;GAE3C,MAAM,YADS,MAAM,QAAQ,MAAM,IACP;AAE5B,SAAM,KAAK,MAAM,UAAU;AAE3B,UAAO;;EAET,MAAM,OAAO;GACX,MAAM,QAAQ,MAAM,SAAS,UAAU,OAAO;AAC9C,UAAO,KAAK,MAAM,MAAM;;EAE1B,MAAM,MAAM,OAAU;AACpB,SAAM,gBAAgB;AACtB,SAAM,MAAM,KAAK,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACxD,SAAM,UAAU,UAAU,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO;;EAE3E;;;;AChBH,SAAgB,mBAAmB,QAAwB,IAA0B;CACnF,eAAe,kBAAkB;EAC/B,MAAM,YAAY,MAAM,OAAO,WAAW;AAC1C,SAAO,KAAK,KAAK,WAAW,aAAa,WAAW,IAAI,YAAY;;CAGtE,eAAe,gBAAkC;AAC/C,MAAI;AACF,UAAO,MAAM,OAAO,IAAI,KAAc;;;2CAGD,KAAK,UAAU,GAAG,CAAC;+BAC/B,KAAK,UAAU,GAAG,CAAC;;YAEtC;UACA;AACN,UAAO;;;AAIX,QAAO;EACL,OAAiC;AAC/B,UAAO;IACL,MAAM,MAAM,SAAS;KACnB,MAAM,WAAW,MAAM,iBAAiB;AACxC,YAAO,eAAkB,gBACvB,mBAAmB,OAAO,CAAC,iBAAiB,SAAS,CACtD,CAAC,MAAM,QAAQ;;IAElB,MAAM,OAAO;AAEX,YAAO,eADU,MAAM,iBAAiB,CACN,CAAC,MAAM;;IAE3C,MAAM,MAAM,OAAO;KACjB,MAAM,WAAW,MAAM,iBAAiB;AACxC,WAAM,eAAkB,gBACtB,mBAAmB,OAAO,CAAC,iBAAiB,SAAS,CACtD,CAAC,MAAM,MAAM;;IAEjB;;EAEH,MAAM,WAAW;AACf,UAAO,iBAAiB;;EAE1B,MAAM,QAAQ,UAA+B,EAAE,EAAE;AAC/C,SAAM,OAAO,KAAK,kBAAkB;IAClC,QAAQ,QAAQ;IAChB;IACD,CAAC;;EAEJ,MAAM,OAAO,UAA+B,EAAE,EAAE;AAC9C,SAAM,OAAO,KAAK,iBAAiB;IACjC,QAAQ,QAAQ;IAChB;IACD,CAAC;;EAEJ;EACA,MAAM,YAAY;GAChB,MAAM,SAAS,MAAM,OAAO,SAAS,UAAU,EAAE,IAAI,EAAE,EAAE,kBAAkB,MAAM,CAAC;AAClF,UAAO,kBAAkB,KAAK,OAAO;;EAEvC,MAAM,OAAO,UAA+B,EAAE,EAAE;GAC9C,MAAM,EAAE,cAAc,gBAAgB,GAAG,gBAAgB;AAEzD,SAAM,OAAO,KAAK,iBAAiB,EAAE,IAAI,EAAE,YAAY;AAEvD,OAAI,eACF,OAAM,KAAK,eAAe,aAAa;;EAG3C,MAAM,cAAc;AAClB,SAAM,mBAAmB,OAAO,CAAC,YAAY,MAAM,iBAAiB,CAAC;;EAEvE,MAAM,YACJ,WACA,UAAoC,EAAE,EACtC;AACA,UAAO,OAAO,QAAQ,YAAY;AAChC,QAAI;KACF,MAAM,OAAO,MAAM,KAAK,MAAS,CAAC,MAAM;AACxC,YAAQ,MAAM,UAAU,KAAK,GAAI,OAAO;YAClC;AACN,YAAO;;MAER,QAAQ;;EAEb,MAAM,eAAe,UAAuC,EAAE,EAAE;AAC9D,SAAM,OAAO,QACX,YAAY;AACV,QAAI,CAAE,MAAM,eAAe,CACzB,QAAO;AAGT,QAAI,QAAQ,UACV,QAAO,MAAM,OAAO,QAAQ,QAAQ,UAAU,CAAC,QAAQ;AAGzD,WAAO;MAET;IACE,GAAG;IACH,SACE,QAAQ,YACP,QAAQ,YACL,WAAW,GAAG,8BAA8B,QAAQ,UAAU,KAC9D,WAAW,GAAG;IACrB,CACF;;EAEJ;;;;AC1HH,IAAa,uBAAb,cAA0C,MAAM;CAC9C;CAEA,YAAY,SAAiB,QAAoB;AAC/C,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS;;;AAIlB,IAAa,sBAAb,cAAyC,MAAM;CAC7C;CAEA,YAAY,SAAiB,YAAsB;AACjD,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa;;;;;ACbtB,MAAMA,uBAAqB;AAE3B,MAAa,iBAAmC,OAAO,EACrD,mBAAmB,OACnB,MACA,KACA,KACA,KACA,YAAYA,2BAC6B;CACzC,MAAM,QAAQ,MAAM,KAAK,MAAM;EAC7B;EACA;EACA,OAAO;GAAC;GAAU;GAAQ;GAAO;EAClC,CAAC;CAEF,MAAM,eAAyB,EAAE;CACjC,MAAM,eAAyB,EAAE;AAEjC,OAAM,OAAO,GAAG,SAAS,UAAU;AACjC,eAAa,KAAK,OAAO,KAAK,MAAM,CAAC;GACrC;AAEF,OAAM,OAAO,GAAG,SAAS,UAAU;AACjC,eAAa,KAAK,OAAO,KAAK,MAAM,CAAC;GACrC;CAEF,MAAM,WAAW,MAAM,IAAI,SAAiB,SAAS,WAAW;EAC9D,MAAM,QAAQ,iBAAiB;AAC7B,SAAM,KAAK,UAAU;AACrB,0BAAO,IAAI,MAAM,2BAA2B,UAAU,MAAM,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC;KACpF,UAAU;AAEb,QAAM,GAAG,UAAU,UAAU;AAC3B,gBAAa,MAAM;AACnB,UAAO,MAAM;IACb;AAEF,QAAM,GAAG,UAAU,SAAS;AAC1B,gBAAa,MAAM;AACnB,WAAQ,QAAQ,EAAE;IAClB;GACF;CAEF,MAAM,SAAqB;EACzB;EACA,SAAS;EACT;EACA,QAAQ,OAAO,OAAO,aAAa,CAAC,SAAS,OAAO;EACpD,QAAQ,OAAO,OAAO,aAAa,CAAC,SAAS,OAAO;EACrD;AAED,KAAI,aAAa,KAAK,CAAC,iBACrB,OAAM,IAAI,qBACR,0CAA0C,SAAS,IAAI,IAAI,GAAG,KAAK,KAAK,IAAI,IAC5E,OACD;AAGH,QAAO;;;;AC7DT,MAAM,sBAAsB;AAC5B,MAAMC,uBAAqB;AAE3B,eAAsBC,QAAM,IAA2B;AACrD,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;;AAGzD,eAAsB,aACpB,IACA,UAA0B,EAAE,EAChB;CACZ,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,YAAY,QAAQ,aAAaD;CACvC,MAAM,YAAY,KAAK,KAAK;CAE5B,IAAI;AAEJ,QAAO,KAAK,KAAK,GAAG,aAAa,WAAW;AAC1C,MAAI;GACF,MAAM,SAAS,MAAM,IAAI;AACzB,OAAI,WAAW,SAAS,WAAW,QAAQ,WAAW,KAAA,EACpD,QAAO;WAEF,OAAO;AACd,eAAY;;AAGd,QAAMC,QAAM,WAAW;;AAIzB,OAAM,IAAI,oBAAoB,yBADhB,QAAQ,WAAW,YAC4B,SAAS,UAAU,MAAM,UAAU;;;;ACPlG,SAAgB,qBAAqB,SAAsD;CACzF,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,qBAAqB,QAAQ;CACnC,MAAM,eAAe;EACnB,YAAY,QAAQ;EACpB,WAAW,QAAQ;EACpB;CAED,MAAM,iBAAiB,qBAAqB,OAAO,aAAa;EAC9D,MAAM,EAAE,aAAa,MAAM,OAAO;AAClC,SAAO,SAAS,UAAU,OAAO;GACjC;CAEF,IAAI;CAEJ,MAAM,SAAS,EAAE;CAEjB,MAAM,MAAyB;EAC7B,MAAM,OAAO,cAA2B,EAAE,EAAE;AAC1C,SAAM,OAAO,KAAK,UAAU,EAAE,EAAE,YAAY;;EAE9C,MAAM,QAAQ,EACZ,cACA,iBAAiB,MACjB,GAAG,gBACgC,EAAE,EAAE;AACvC,SAAM,OAAO,KAAK,WAAW,EAAE,EAAE,YAAY;AAE7C,OAAI,eACF,OAAM,IAAI,eAAe,aAAa;;EAG1C,QAAQ,cAA2B,EAAE,EAAE;AACrC,UAAO,OAAO,SAAS,WAAW,EAAE,EAAE,YAAY;;EAEpD,MAAM,eAAe,aAA8B;AACjD,SAAM,OAAO,QAAQ,YAAY;AAC/B,QAAI;AACF,WAAM,OAAO,WAAW;AACxB,WAAM,OAAO,UAAU;AACvB,YAAO;YACD;AACN,YAAO;;MAER,YAAY;;EAElB;AAmDD,QAAO,OAAO,QAAQ;EACpB;EACA,KAAK,QAAQ,OAAO;EACpB,KApD6B;GAC7B,MAAM,IAAI,SAA6B,cAA2B,EAAE,EAAyB;IAC3F,MAAM,SAAS,MAAM,OAAO,SAC1B,WACA;KACE,KAAK,QAAQ;KACb,MAAM,QAAQ;KACd,KAAK,QAAQ;KACb,OAAO,QAAQ;KACf,UAAU,QAAQ;KAClB,MAAM,QAAQ;KACd,OAAO,QAAQ;KAChB,EACD,YACD;AAED,QAAI,QAAQ,MACV,QAAO,OAAO,SAAS,QAAQ,GAAG;AAGpC,QAAI,QAAQ,IACV,QAAO,SAAS,OAAO,MAAM,SAAS,CAAC,OAAO,QAAQ,GAAG,EAAE;AAG7D,WAAO;;GAET,MAAM,KAAkB,MAAc,cAA2B,EAAE,EAAE;AAQnE,WAAO,mBAPQ,MAAM,OAAO,SAC1B,QACA,EACE,MACD,EACD,YACD,CACmC;;GAEtC,MAAM,WAAW,YAAoB,cAA2B,EAAE,EAAE;AAClE,UAAM,OAAO,KACX,kBACA,EACE,MAAM,YACP,EACD,YACD;AAED,WAAO;;GAEV;EAMC,QAAQ,IAAmC;AACzC,UAAO;IACL,MAAM,OAAO,iBAAqC,EAAE,EAAE;AAMpD,aALiB,MAAM,OAAO,SAAS;MACrC,GAAG;MACH,QAAQ,eAAe,UAAU;MAClC,CAAC,EAEc,SAAS,GAAG;;IAE9B;IACA,MAAM,IAAI,cAA2B,EAAE,EAAE;AACvC,WAAM,OAAO,KAAK,WAAW,EAAE,IAAI,EAAE,YAAY;;IAEpD;;EAEH,MAAM,SACJ,iBAAqC,EAAE,EACvC,cAA2B,EAAE,EACV;AAQnB,UAAO,gBAPQ,MAAM,OAAO,SAC1B,YACA,EACE,QAAQ,eAAe,QACxB,EACD,YACD,CAC6B;;EAEhC,KAAK,SAAiB,OAAoC,EAAE,EAAE,cAA2B,EAAE,EAAE;AAC3F,UAAO,UAAU;IACf,GAAG,iBAAiB,oBAAoB,YAAY;IACpD,MAAM,iBAAiB,QAAQ,OAAO,SAAS,KAAK;IACpD,KAAK,KAAK;IACX,CAAC;;EAEJ,MAAM,SACJ,SACA,OAAoC,EAAE,EACtC,cAA2B,EAAE,EAC7B;GACA,MAAM,SAAS,MAAM,KAAK,SAAS,SAAS,MAAM,YAAY;AAC9D,UAAO,KAAK,MAAM,OAAO;;EAE3B,MAAM,SACJ,SACA,OAAoC,EAAE,EACtC,cAA2B,EAAE,EAC7B;AAEA,WADe,MAAM,KAAK,KAAK,SAAS,MAAM,YAAY,EAC5C,OAAO,SAAS;;EAEhC,MAAM,KAAK,aAA8B,cAA2B,EAAE,EAAE;AACtE,SAAM,OAAO,KACX,QACA;IACE,MAAM,YAAY;IAClB,QAAQ,YAAY;IACpB,MAAM,YAAY;IACnB,EACD,YACD;;EAEH,MAAM,QAAQ,aAA6B,EAAE,EAAE,cAA2B,EAAE,EAAE;AAC5E,SAAM,OAAO,KACX,YACA;IACE,MAAM,WAAW;IACjB,OAAO,WAAW;IAClB,MAAM,WAAW;IAClB,EACD,YACD;;EAEH,OAAO,IAAY;AACjB,UAAO,mBAAmB,MAAM,GAAG;;EAErC,MAAM,IAAY;AAChB,UAAOC,QAAM,GAAG;;EAElB,MAAM,KACJ,aAA0B,EAAE,EAC5B,cAA2B,EAAE,EACJ;AAQzB,UAAO,UAPQ,MAAM,OAAO,SAC1B,QACA,EACE,KAAK,WAAW,OAAO,MACxB,EACD,YACD,CACuB;;EAE1B,MAAM,YAAY;AAChB,OAAI,CAAC,gBACH,mBAAkB,MAAM,KAAK,SAAS,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGlE,UAAO;;EAET,MAAM,SAAS;AACb,SAAM,UAAU;IACd,GAAG,iBAAiB,oBAAoB,KAAA,EAAU;IAClD,MAAM,CAAC,SAAS;IAChB,KAAK,KAAK;IACX,CAAC;AAEF,SAAM,KAAK,WAAW;;EAExB,WAAW,QAAQ;EACnB,QACE,IACA,aACA;AACA,UAAO,aAAa,IAAI;IACtB,GAAG;IACH,GAAG;IACJ,CAAC;;EAEJ,MAAM,UACJ,mBAAqC,EAAE,EACvC,cAA2B,EAAE,EACH;AAQ1B,UAAO,eAPQ,MAAM,OAAO,SAC1B,aACA,EACE,KAAK,iBAAiB,OAAO,MAC9B,EACD,YACD,CAC4B;;EAEhC,CAAC;AAEF,uBAAsB,QAAQ,eAAe;AAE7C,QAAO;;AAGT,SAAS,gBAAgB,QAA0B;AACjD,QAAO,OACJ,MAAM,SAAS,CACf,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,MAAM,KAAM,EAAE,CAAC,IAAI,MAAM,IAAI,GAAG,CACnD,OAAO,QAAQ;;AAGpB,SAAS,mBAAsB,QAAmB;CAChD,MAAM,aAAa,OAAO,WAAW,MAAM,GAAG,OAAO,MAAM,EAAE,GAAG;AAEhE,KAAI;AACF,SAAO,KAAK,MAAM,WAAW;SACvB;AACN,SAAO;;;AAIX,SAAS,UAAU,QAAgC;AACjD,QAAO,OACJ,MAAM,SAAS,CACf,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,CACf,IAAI,aAAa;;AAGtB,SAAS,aAAa,MAA4B;CAChD,MAAM,CAAC,YAAY,MAAM,KAAK,MAAM,IAAK;CACzC,MAAM,QAAQ,YAAY,MAAM,sBAAsB;AAEtD,KAAI,CAAC,MACH,QAAO;EACL,IAAI,IAAI,MAAM,IAAI,KAAA;EAClB,OAAO,YAAY,MAAM,IAAI;EAC7B,UAAU;EACX;AAGH,QAAO;EACL,IAAI,IAAI,MAAM,IAAI,KAAA;EAClB,OAAO,MAAM;EACb,UAAU,MAAM;EACjB;;AAGH,SAAS,eAAe,QAAiC;CACvD,MAAM,QAAyB,EAAE;CACjC,MAAM,QAAuD,EAAE;AAE/D,MAAK,MAAM,WAAW,OAAO,MAAM,SAAS,EAAE;AAC5C,MAAI,CAAC,QAAQ,MAAM,CACjB;EAGF,MAAM,QAAQ,kBAAkB,QAAQ;EACxC,MAAM,OAAO,mBAAmB,QAAQ;AAExC,SAAO,MAAM,SAAS,KAAK,MAAM,GAAG,GAAG,CAAE,SAAS,MAChD,OAAM,KAAK;EAGb,MAAM,SAAS,MAAM,GAAG,GAAG,EAAE;AAE7B,MAAI,OACF,QAAO,SAAS,KAAK,KAAK;MAE1B,OAAM,KAAK,KAAK;AAGlB,QAAM,KAAK;GAAE;GAAO;GAAM,CAAC;;AAG7B,QAAO;;AAGT,SAAS,kBAAkB,MAAsB;CAC/C,IAAI,QAAQ;CACZ,IAAI,YAAY;AAEhB,QAAO,MAAM;AACX,MACE,UAAU,WAAW,OAAO,IAC5B,UAAU,WAAW,OAAO,IAC5B,UAAU,WAAW,OAAO,IAC5B,UAAU,WAAW,OAAO,EAC5B;AACA,YAAS;AACT,eAAY,UAAU,MAAM,EAAE;AAC9B;;AAGF,SAAO;;;AAIX,SAAS,mBAAmB,MAA6B;CACvD,IAAI,cAAc;AAElB,QAAO,MAAM;AACX,MACE,YAAY,WAAW,OAAO,IAC9B,YAAY,WAAW,OAAO,IAC9B,YAAY,WAAW,OAAO,IAC9B,YAAY,WAAW,OAAO,EAC9B;AACA,iBAAc,YAAY,MAAM,EAAE;AAClC;;AAGF;;AAGF,eAAc,YAAY,MAAM;CAChC,MAAM,UAAU,YAAY,MAAM,iCAAiC;CACnE,MAAM,UAAU,UAAU,IAAI,MAAM,IAAI;CACxC,MAAM,KAAK,UAAU;CACrB,MAAM,YAAY,QAAQ,MAAM,sBAAsB;AAEtD,KAAI,UACF,QAAO;EACL,UAAU,EAAE;EACZ;EACA,OAAO,UAAU;EACjB,OAAO,UAAU;EACjB,UAAU,UAAU;EACrB;AAGH,QAAO;EACL,UAAU,EAAE;EACZ;EACA,OAAO;EACR;;;;AC3YH,MAAa,gCAAgC;AA+B7C,SAAgB,yBACd,SACuB;AACvB,KAAI,CAAC,QAAQ,iBACX,QAAO;EACL,cAAc,KAAK,QAAQ,QAAQ,gBAAA,0BAA8C;EACjF,SAAS;GACP,YAAY;GACZ,KAAK;GACL,YAAY;GACZ,YAAY;GACZ,MAAM;GACN,WAAW;GACZ;EACD,SAAS;EACV;CAGH,MAAM,YAAY,QAAQ,qBAAqB,OAAO,EAAE,GAAG,QAAQ;AAEnE,QAAO;EACL,cAAc,KAAK,QAAQ,QAAQ,gBAAA,0BAA8C;EACjF,SAAS;GACP,YAAY,UAAU,cAAc;GACpC,KAAK,UAAU,OAAO;GACtB,YAAY,UAAU,cAAc;GACpC,YAAY,UAAU,cAAc;GACpC,MAAM,UAAU,QAAQ;GACxB,WAAW,UAAU,aAAa;GACnC;EACD,SAAS;EACV;;AAGH,SAAgB,4BACd,cACA,MACQ;CACR,MAAM,SAAS,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,GAAG,IAAI;AAC5C,QAAO,KAAK,KAAK,cAAc,GAAG,gBAAgB,KAAK,KAAK,CAAC,GAAG,SAAS;;AAG3E,eAAsB,wBACpB,MACA,UACA,SAC6B;CAC7B,MAAM,SAAS,yBAAyB,QAAQ;AAEhD,KAAI,CAAC,OAAO,QACV;CAGF,MAAM,oBAAoB,4BAA4B,OAAO,cAAc,KAAK;AAChF,OAAM,MAAM,mBAAmB,EAAE,WAAW,MAAM,CAAC;AAEnD,OAAM,QAAQ,IAAI;EAChB,oBACE,mBACA,oBACA,OAAO,QAAQ,YACf,aAAa,EACX,YAAY,MAAM,SAAS,IAAI,KAC7B,8CACD,EACF,EACF;EACD,oBAAoB,mBAAmB,WAAW,OAAO,QAAQ,KAAK,YACpE,OACE,MAAM,SAAS,IAAI,IAAI;GACrB,OAAO;GACP,UAAU;GACX,CAAC,CACH,CACF;EACD,oBAAoB,mBAAmB,eAAe,OAAO,QAAQ,YAAY,aAAa,EAC5F,MAAM,MAAM,SAAS,IAAI,KACvB,+DACD,EACF,EAAE;EACH,0BAA0B,mBAAmB,OAAO,QAAQ,YAAY,SAAS;EACjF,oBAAoB,mBAAmB,aAAa,OAAO,QAAQ,YAAY,SAAS,MAAM,CAAC;EAC/F,oBAAoB,mBAAmB,kBAAkB,OAAO,QAAQ,iBACtE,SAAS,WAAW,CACrB;EACD,QAAQ,SACJ,oBAAoB,mBAAmB,GAAG,QAAQ,OAAO,GAAG,aAAa,YACvE,QAAQ,OAAQ,MAAM,CAAC,MAAM,CAC9B,GACD,QAAQ,SAAS;EACtB,CAAC;AAEF,QAAO;;AAGT,eAAsB,8BACpB,MACA,QACA,SAC6B;CAC7B,MAAM,SAAS,yBAAyB,QAAQ;AAEhD,KAAI,CAAC,OAAO,QACV;CAGF,MAAM,oBAAoB,4BAA4B,OAAO,cAAc,KAAK;AAChF,OAAM,MAAM,mBAAmB,EAAE,WAAW,MAAM,CAAC;AACnD,OAAM,oBAAoB,mBAAmB,GAAG,OAAO,GAAG,aAAa,YACrE,OAAO,MAAM,CAAC,MAAM,CACrB;AAED,QAAO;;AAGT,eAAe,oBACb,mBACA,UACA,SACA,WACe;AACf,KAAI,CAAC,QACH;AAGF,KAAI;EACF,MAAM,QAAQ,MAAM,WAAW;AAC/B,QAAM,UACJ,KAAK,KAAK,mBAAmB,SAAS,EACtC,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAClC,OACD;UACM,OAAO;AACd,QAAM,UACJ,KAAK,KAAK,mBAAmB,GAAG,SAAS,YAAY,EACrD,oBAAoB,MAAM,EAC1B,OACD;;;AAIL,eAAe,0BACb,mBACA,SACA,UACe;AACf,KAAI,CAAC,QACH;CAGF,MAAM,iBAAiB,KAAK,KAAK,mBAAmB,iBAAiB;AAErE,KAAI;AACF,QAAM,SAAS,IAAI,WAAW,eAAe;UACtC,OAAO;AACd,QAAM,UACJ,KAAK,KAAK,mBAAmB,uBAAuB,EACpD,oBAAoB,MAAM,EAC1B,OACD;;;AAIL,eAAe,oBACb,mBACA,UACA,SACA,WACe;AACf,KAAI,CAAC,QACH;AAGF,KAAI;AACF,QAAM,UAAU,KAAK,KAAK,mBAAmB,SAAS,EAAE,MAAM,WAAW,EAAE,OAAO;UAC3E,OAAO;AACd,QAAM,UACJ,KAAK,KAAK,mBAAmB,GAAG,SAAS,YAAY,EACrD,oBAAoB,MAAM,EAC1B,OACD;;;AAIL,SAAS,oBAAoB,OAAwB;AACnD,QAAO,iBAAiB,QAAQ,GAAG,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO,MAAM,CAAC;;AAGzF,SAAS,gBAAgB,OAAuB;AAC9C,QACE,MACG,MAAM,CACN,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,MAAM,GAAG,GAAG,IAAI;;;;AC/NvB,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,2BAA2B;AACjC,MAAM,oBAAoB,KAAK,KAAK,GAAG,QAAQ,EAAE,qBAAqB;AACtE,MAAM,qBAAqB;AAC3B,MAAM,eAAe;AACrB,MAAM,4BAAY,IAAI,KAA+B;AA0CrD,eAAsB,oBAAoB,EACxC,cAAc,sBACd,WAAW,mBACX,SAAS,QACT,UAAU,kBACV,YAAY,oBACZ,WACA,aACoD;CACpD,MAAM,UAAU,KAAK,KAAK,UAAU,mBAAmB,UAAU,CAAC;CAClE,MAAM,WAAW,UAAU,IAAI,QAAQ;AAEvC,KAAI,UAAU;AACZ,WAAS,QAAQ;AACjB,SAAO,yBAAyB,SAAS;;CAG3C,MAAM,UAAU,YAAY;CAC5B,MAAM,eAAe,KAAK,KAAK,SAAS,mBAAmB;CAC3D,MAAM,WAAiC;EACrC,YAAY,KAAK,KAAK;EACtB,KAAK,QAAQ,KAAK;EAClB,aAAa,KAAK,KAAK;EACvB,UAAU,GAAG,UAAU;EACvB;EACA,KAAK,QAAQ;EACb;EACA;EACA;EACD;AAED,OAAM,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;CAE1C,MAAM,YAAY,KAAK,KAAK;AAE5B,QAAO,KACL,KAAI;AACF,QAAM,MAAM,QAAQ;AACpB,QAAM,cAAc,cAAc,SAAS;AAC3C;UACO,OAAO;AACd,MAAI,CAAC,qBAAqB,MAAM,CAC9B,OAAM;EAGR,MAAM,cAAc,MAAM,oBAAoB;GAC5C;GACA;GACA;GACD,CAAC;AAEF,MAAI,eAAe,CAAC,YAAY;OAC1B,WAAW,OACb,OAAM,IAAI,MAAM,sBAAsB,WAAW,YAAY,CAAC;SAE3D;AACL,SAAM,GAAG,SAAS;IAAE,OAAO;IAAM,WAAW;IAAM,CAAC;AACnD;;AAGF,MAAI,KAAK,KAAK,GAAG,aAAa,UAC5B,OAAM,IAAI,MACR,cACI,4CAA4C,sBAAsB,WAAW,YAAY,KACzF,8CAA8C,YACnD;AAGH,QAAM,MAAM,KAAK,IAAI,0BAA0B,YAAY,CAAC;;CAIhE,MAAM,YAAY,kBAAkB;AAClC,WAAS,cAAc,KAAK,KAAK;AAC5B,gBAAc,cAAc,SAAS,CAAC,YAAY,GAAG;IACzD,YAAY;AACf,WAAU,OAAO;CAEjB,MAAM,eAAiC;EACrC;EACA;EACA;EACA;EACA,MAAM;EACP;AAED,WAAU,IAAI,SAAS,aAAa;AACpC,QAAO,yBAAyB,aAAa;;AAG/C,eAAsB,wBAAwB,UAAyC;AACrF,OAAM,SAAS,IAAI,KAAK,iBAAiB,aAAa,eAAe,aAAa,cAAc,EAC9F,kBAAkB,MACnB,CAAC;;AAGJ,eAAsB,oBAAoB,EACxC,WAAW,mBACX,UAAU,kBACV,aAIoC;CACpC,MAAM,UAAU,KAAK,KAAK,UAAU,mBAAmB,UAAU,CAAC;CAClE,MAAM,WAAW,MAAM,cAAc,QAAQ;AAE7C,KAAI,CAAC,SACH,QAAO;AAGT,QAAO;EACL,gBAAgB,KAAK,KAAK,GAAG,SAAS;EACtC,SAAS,YAAY,UAAU,QAAQ;EACvC;EACA;EACD;;AAGH,eAAsB,uBACpB,UACsC;AACtC,QAAO,SAAS,IAAI,KAClB,UAAU,aAAa,UAAU,aAAa,WAC9C,EAAE,kBAAkB,MAAM,CAC3B;;AAGH,SAAS,mBAAmB,WAA2B;AACrD,QAAO,WAAW,SAAS,CAAC,OAAO,KAAK,QAAQ,UAAU,CAAC,CAAC,OAAO,MAAM;;AAG3E,SAAS,mBAAmB,UAAwC;AAElE,QAAO;mBADiB,KAAK,UAAU,SAAS,CAEf;aACtB,aAAa;UAChB,aAAa;;;;AAKvB,SAAS,yBAAyB,UAA0C;AAC1E,QAAO;EACL,IAAI,UAAU;AACZ,UAAO,SAAS;;EAElB,IAAI,WAAW;AACb,UAAO,SAAS;;EAElB,MAAM,cAAc,UAA0B;AAC5C,SAAM,SAAS,IAAI,KAAK,mBAAmB,SAAS,SAAS,CAAC;;EAEhE,MAAM,UAAU;AACd,OAAI,SAAS,OAAO,GAAG;AACrB,aAAS,QAAQ;AACjB;;AAGF,aAAU,OAAO,SAAS,QAAQ;AAClC,iBAAc,SAAS,UAAU;AAIjC,QAFoB,MAAM,cAAc,SAAS,QAAQ,GAExC,YAAY,SAAS,SAAS,QAC7C;AAGF,SAAM,GAAG,SAAS,SAAS;IAAE,OAAO;IAAM,WAAW;IAAM,CAAC;;EAE/D;;AAGH,eAAe,cAAc,SAAuD;CAClF,MAAM,eAAe,KAAK,KAAK,SAAS,mBAAmB;AAE3D,KAAI;AACF,SAAO,KAAK,MAAM,MAAM,SAAS,cAAc,OAAO,CAAC;SACjD;AACN,MAAI;GACF,MAAM,gBAAgB,MAAM,KAAK,QAAQ;AACzC,UAAO;IACL,YAAY,cAAc;IAC1B,KAAK;IACL,aAAa,cAAc;IAC3B,UAAU;IACV,SAAS;IACT,KAAK;IACL,SAAS;IACT,WAAW;IACX,WAAW;IACZ;UACK;AACN,UAAO;;;;AAKb,SAAS,sBAAsB,WAAmB,OAAkC;AAMlF,QAAO,SAAS,UAAU,gBALL,MAAM,SAAS,UAChC,SAAS,MAAM,SAAS,QAAQ,OAAO,MAAM,SAAS,IAAI,OAAO,MAAM,SAAS,OAAO,gBACvF,kBAGmD,GAFpC,kBAAkB,MAAM,eAAe,SAAS,MAAM;;AAK3E,SAAS,qBAAqB,OAAyB;AACrD,QAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;;AAGrE,SAAS,YAAY,UAAgC,SAA0B;AAC7E,QAAO,KAAK,KAAK,GAAG,SAAS,cAAc;;AAG7C,eAAe,MAAM,YAAmC;AACtD,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,WAAW,CAAC;;AAGjE,eAAe,cAAc,cAAsB,UAA+C;AAChG,OAAM,UAAU,cAAc,GAAG,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC,KAAK,OAAO;;;;AChRjF,SAAgB,eAAe,OAAwB;AACrD,KAAI,CAAC,SAAS,UAAU,IACtB,QAAO;AAGT,QAAO,MAAM,QAAQ,cAAc,GAAG;;AAGxC,SAAgB,iBAAiB,WAAmB,YAA4B;AAC9E,KAAI,CAAC,cAAc,eAAe,IAChC,QAAO;AAGT,QAAO,YAAYC,MAAU,KAAK,WAAW,WAAW,GAAGA,MAAU,UAAU,WAAW;;AAG5F,eAAsB,sBACpB,UACA,WACA,YACiB;CACjB,MAAM,YAAY,MAAM,SAAS,WAAW;CAE5C,MAAM,eADa,iBAAiB,WAAW,WAAW,CAC1B,MAAM,IAAI,CAAC,OAAO,QAAQ;CAC1D,MAAM,eAAe,KAAK,QAAQ,WAAW,GAAG,aAAa;CAC7D,MAAM,sBAAsB,KAAK,QAAQ,UAAU;AAEnD,KACE,iBAAiB,uBACjB,CAAC,aAAa,WAAW,GAAG,sBAAsB,KAAK,MAAM,CAE7D,OAAM,IAAI,MAAM,yCAAyC,aAAa;AAGxE,QAAO;;;;ACrBT,SAAgB,eAAe,SAA0C;CACvE,MAAM,YAAY,eAAe,QAAQ,KAAK;AAE9C,QAAO;EACL,MAAM,OAAO,YAAY,gBAA+B,EAAE,EAAE;AAE1D,SAAM,GADe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW,EAClE;IACrB,OAAO;IACP,WAAW;IACZ,CAAC;AAEF,OAAI,cAAc,cAAc,MAC9B;;EAGJ,MAAM,OAAO,YAAY;AACvB,OAAI;AAEF,UAAM,OADe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW,CAC/D;AAC1B,WAAO;WACD;AACN,WAAO;;;EAGX,KAAkB,YAAoB;GACpC,MAAM,WAAwB;IAC5B,MAAM,MAAM,SAAS;KACnB,MAAM,eAAe,MAAM,SAAS,MAAM;KAC1C,MAAM,QAAQ,gBAAgB,aAAa;KAE3C,MAAM,YADS,MAAM,QAAQ,MAAM,IACP;AAE5B,WAAM,SAAS,MAAM,UAAU;AAE/B,YAAO;;IAET,MAAM,OAAO;KAEX,MAAM,WAAW,MAAM,SADF,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW,EAC3C,OAAO;AACrD,YAAO,KAAK,MAAM,SAAS;;IAE7B,MAAM,MAAM,OAAO;KACjB,MAAM,eAAe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW;AACzF,WAAM,MAAM,KAAK,QAAQ,aAAa,EAAE,EAAE,WAAW,MAAM,CAAC;AAC5D,WAAM,UAAU,cAAc,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO;;IAE/E;AAED,UAAO;;EAET,MAAM,MAAM,YAAY;AAEtB,SAAM,MADe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW,EAC/D,EAAE,WAAW,MAAM,CAAC;;EAEhD,MAAM,KAAK,YAAY;AAErB,UAAO,SADc,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW,EAC3D,OAAO;;EAEvC,MAAM,eAAe,YAAY,WAAW,cAA0C,EAAE,EAAE;GACxF,MAAM,eAAe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW;AAEzF,UAAO,QAAQ,SAAS,QACtB,YAAY;AACV,QAAI;KACF,MAAM,UAAU,MAAM,SAAS,cAAc,OAAO;AACpD,YAAQ,MAAM,UAAU,QAAQ,GAAI,UAAU;YACxC;AACN,YAAO;;MAGX;IACE,SAAS,eAAe,iBAAiB,WAAW,WAAW,CAAC;IAChE,GAAG;IACJ,CACF;;EAEH,MAAM,cAAc,YAAY,aAAa;GAC3C,MAAM,eAAe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW;AAEzF,SAAM,QAAQ,SAAS,QACrB,YAAY;AACV,QAAI;AACF,WAAM,OAAO,aAAa;AAC1B,YAAO;YACD;AACN,YAAO;;MAGX;IACE,SAAS,eAAe,iBAAiB,WAAW,WAAW,CAAC;IAChE,GAAG;IACJ,CACF;;EAEH,MAAM,eAAe,YAAY,aAAa;GAC5C,MAAM,eAAe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW;AAEzF,SAAM,QAAQ,SAAS,QACrB,YAAY;AACV,QAAI;AACF,WAAM,OAAO,aAAa;AAC1B,YAAO;YACD;AACN,YAAO;;MAGX;IACE,SAAS,eAAe,iBAAiB,WAAW,WAAW,CAAC;IAChE,GAAG;IACJ,CACF;;EAEH,MAAM,MAAM,YAAY,SAAS,eAAkC,EAAE,EAAE;GACrE,MAAM,eAAe,MAAM,sBAAsB,QAAQ,UAAU,WAAW,WAAW;AACzF,SAAM,MAAM,KAAK,QAAQ,aAAa,EAAE,EAAE,WAAW,MAAM,CAAC;AAC5D,SAAM,UAAU,cAAc,SAAS,OAAO;AAE9C,OAAI,CAAC,aAAa,eAChB;GAGF,MAAM,YACJ,OAAO,aAAa,mBAAmB,aACnC,aAAa,kBACZ,UAAkB,UAAU;AAEnC,SAAM,KAAK,eAAe,YAAY,WAAW,aAAa,YAAY;;EAE7E;;;;ACtIH,eAAsB,iBAAiB,SAAuD;CAC5F,MAAM,OAAOC,MAAU,KACrB,QAAQ,aACR,GAAG,gBAAgB,QAAQ,SAAS,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,EAAE,GACjE;CACD,MAAM,QAAQ,eAAe;EAC3B,UAAU,QAAQ;EAClB;EACD,CAAC;AAEF,OAAM,MAAM,MAAM,IAAI;AAEtB,QAAO;EACL,GAAG;EACH,MAAM,UAAU;AACd,SAAM,MAAM,OAAO,KAAK,EAAE,WAAW,MAAM,CAAC;;EAE9C,KAAK,GAAG,UAAoB;AAC1B,UAAOA,MAAU,KAAK,MAAM,GAAG,SAAS;;EAE1C;EACD;;AAGH,SAAS,gBAAgB,OAAuB;AAC9C,QACE,MACG,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,MAAM,GAAG,GAAG,IAAI"}