obsidian-dev-utils 83.3.0 → 84.1.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 (57) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/dev/main.js +2471 -1860
  3. package/dist/lib/cjs/generated-during-build.cjs +2 -2
  4. package/dist/lib/cjs/obsidian/components/plugin-notice-component.cjs +6 -4
  5. package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.cjs +10 -10
  6. package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.d.cts +3 -3
  7. package/dist/lib/cjs/obsidian/editor.cjs +21 -1
  8. package/dist/lib/cjs/obsidian/editor.d.cts +23 -3
  9. package/dist/lib/cjs/obsidian/file-manager.cjs +9 -9
  10. package/dist/lib/cjs/obsidian/file-manager.d.cts +9 -9
  11. package/dist/lib/cjs/obsidian/i18n/locales/en.cjs +8 -8
  12. package/dist/lib/cjs/obsidian/i18n/locales/en.d.cts +7 -7
  13. package/dist/lib/cjs/obsidian/i18n/locales/translations-map.d.cts +7 -7
  14. package/dist/lib/cjs/obsidian/index.cjs +7 -4
  15. package/dist/lib/cjs/obsidian/index.d.cts +2 -1
  16. package/dist/lib/cjs/obsidian/markdown-code-block-processor.cjs +7 -7
  17. package/dist/lib/cjs/obsidian/markdown-code-block-processor.d.cts +9 -9
  18. package/dist/lib/cjs/obsidian/plugin/plugin.cjs +21 -21
  19. package/dist/lib/cjs/obsidian/plugin/plugin.d.cts +15 -15
  20. package/dist/lib/cjs/obsidian/resource-lock.cjs +899 -0
  21. package/dist/lib/cjs/obsidian/resource-lock.d.cts +227 -0
  22. package/dist/lib/cjs/obsidian/vault-transaction.cjs +373 -0
  23. package/dist/lib/cjs/obsidian/vault-transaction.d.cts +146 -0
  24. package/dist/lib/cjs/obsidian/vault.cjs +4 -4
  25. package/dist/lib/cjs/obsidian/vault.d.cts +3 -3
  26. package/dist/lib/esm/generated-during-build.mjs +2 -2
  27. package/dist/lib/esm/obsidian/components/plugin-notice-component.mjs +10 -5
  28. package/dist/lib/esm/obsidian/components/rename-delete-handler-component.d.mts +3 -3
  29. package/dist/lib/esm/obsidian/components/rename-delete-handler-component.mjs +10 -10
  30. package/dist/lib/esm/obsidian/editor.d.mts +23 -3
  31. package/dist/lib/esm/obsidian/editor.mjs +20 -1
  32. package/dist/lib/esm/obsidian/file-manager.d.mts +9 -9
  33. package/dist/lib/esm/obsidian/file-manager.mjs +9 -9
  34. package/dist/lib/esm/obsidian/i18n/locales/en.d.mts +7 -7
  35. package/dist/lib/esm/obsidian/i18n/locales/en.mjs +8 -8
  36. package/dist/lib/esm/obsidian/i18n/locales/translations-map.d.mts +7 -7
  37. package/dist/lib/esm/obsidian/index.d.mts +2 -1
  38. package/dist/lib/esm/obsidian/index.mjs +5 -3
  39. package/dist/lib/esm/obsidian/markdown-code-block-processor.d.mts +9 -9
  40. package/dist/lib/esm/obsidian/markdown-code-block-processor.mjs +7 -7
  41. package/dist/lib/esm/obsidian/plugin/plugin.d.mts +15 -15
  42. package/dist/lib/esm/obsidian/plugin/plugin.mjs +21 -21
  43. package/dist/lib/esm/obsidian/resource-lock.d.mts +227 -0
  44. package/dist/lib/esm/obsidian/resource-lock.mjs +799 -0
  45. package/dist/lib/esm/obsidian/vault-transaction.d.mts +146 -0
  46. package/dist/lib/esm/obsidian/vault-transaction.mjs +273 -0
  47. package/dist/lib/esm/obsidian/vault.d.mts +3 -3
  48. package/dist/lib/esm/obsidian/vault.mjs +4 -4
  49. package/dist/styles.css +8 -1
  50. package/obsidian/resource-lock/package.json +6 -0
  51. package/obsidian/vault-transaction/package.json +6 -0
  52. package/package.json +1 -1
  53. package/dist/lib/cjs/obsidian/editor-lock.cjs +0 -545
  54. package/dist/lib/cjs/obsidian/editor-lock.d.cts +0 -142
  55. package/dist/lib/esm/obsidian/editor-lock.d.mts +0 -142
  56. package/dist/lib/esm/obsidian/editor-lock.mjs +0 -442
  57. package/obsidian/editor-lock/package.json +0 -6
@@ -0,0 +1,227 @@
1
+ /// <reference path="../library.d.cts" />
2
+ /**
3
+ * @file
4
+ *
5
+ * Path-scoped resource locking for vault files and folders, with three enforcement layers over one
6
+ * shared locked-path set:
7
+ *
8
+ * 1. **Editor read-only** — while a path is locked, every current and future {@link MarkdownView} of
9
+ * that note (in any window, including popouts) is made read-only and shows a lock indicator in its
10
+ * tab header, its view action bar, and the status bar; a note inside a `subtree`-locked folder is
11
+ * covered too. Locks are reference-counted per locking plugin (the indicators' tooltip lists which
12
+ * plugins hold a lock), so nested/concurrent operations are safe — the path is unlocked only when
13
+ * the last lock is released.
14
+ * 2. **Vault-mutation blocking** (opt-in via `shouldBlockMutations`) — any edit/delete/rename/move/
15
+ * create of a locked path through the `Vault`/`FileManager` API throws {@link ResourceLockedError},
16
+ * unless the owning operation has opened a {@link ResourceLockComponent.bypassBlockedMutations} scope
17
+ * over that path.
18
+ * 3. **External-change detection** — a vault/metadata event on a locked path that no bypass scope
19
+ * covers is treated as an intruder (Sync / raw filesystem / another plugin) and aborts the owning
20
+ * operation's {@link AbortController}.
21
+ *
22
+ * The acquirers return a {@link Disposable}, so the preferred call style is a `using` declaration that
23
+ * releases automatically at scope exit (including on throw):
24
+ *
25
+ * ```ts
26
+ * using _lock = lockResourceForPath(app, path, this.manifest.id);
27
+ * // ... long-running work (process, processFrontMatter, merge/split) ...
28
+ * // auto-unlocked at scope exit
29
+ * ```
30
+ *
31
+ * The explicit {@link unlockResourceForPath} pair remains available for non-`using` call sites.
32
+ */
33
+ import type { App } from 'obsidian' with { 'resolution-mode': 'import' };
34
+ import type { PathOrFile } from './file-system.cjs';
35
+ import { ComponentEx } from './components/component-ex.cjs';
36
+ /**
37
+ * Options for {@link ResourceLockComponent.lockForPath}.
38
+ */
39
+ export interface ResourceLockComponentLockForPathOptions {
40
+ /**
41
+ * An optional {@link AbortController} associated with the lock. When the lock indicator is
42
+ * right-clicked and the user confirms an unlock (or {@link requestResourceUnlockForPath} is called for
43
+ * the path), this controller is aborted so the operation holding the lock can cancel itself and
44
+ * release the lock in its own cleanup.
45
+ */
46
+ readonly abortController?: AbortController;
47
+ /**
48
+ * The lock scope. `'subtree'` also locks every descendant of a folder path; `'file'` locks only the
49
+ * exact path. A `subtree` lock must be released via the returned {@link Disposable} (not the
50
+ * explicit {@link ResourceLockComponent.unlockForPath}).
51
+ *
52
+ * @default `'file'`
53
+ */
54
+ readonly mode?: ResourceLockMode;
55
+ /**
56
+ * When `true`, the lock also **blocks vault mutations** of the covered path(s): any attempt to edit,
57
+ * delete, rename, move, or (re)create the resource through the `Vault`/`FileManager` API throws a
58
+ * {@link ResourceLockedError}. Installed lazily on the first such lock and removed when the last one
59
+ * is released. When `false` (the default) the lock only makes the editor read-only — legacy behavior,
60
+ * so existing consumers that write to a note they hold an editor lock on are unaffected.
61
+ *
62
+ * @default `false`
63
+ */
64
+ readonly shouldBlockMutations?: boolean;
65
+ }
66
+ /**
67
+ * The scope of a lock.
68
+ *
69
+ * - `'file'` locks only the exact path.
70
+ * - `'subtree'` locks the path and every descendant under it (its whole folder subtree), so a file
71
+ * inside a `subtree`-locked folder is treated as locked too.
72
+ */
73
+ export type ResourceLockMode = 'file' | 'subtree';
74
+ /**
75
+ * A per-plugin handle for path-scoped resource locking (editor read-only, optional vault-mutation
76
+ * blocking, and external-change detection — see the file overview). Add it as a child of your plugin
77
+ * (`this.addChild(new ResourceLockComponent(this.app))`) so that any locks it still holds are released
78
+ * automatically when the plugin unloads — a resource can never be left stuck locked because the plugin
79
+ * that locked it was disabled or reloaded mid-operation.
80
+ *
81
+ * Locks are reference-counted and attributed to this plugin, so the lock indicators' tooltip names it
82
+ * among the plugins currently holding a lock.
83
+ */
84
+ export declare class ResourceLockComponent extends ComponentEx {
85
+ private readonly app;
86
+ private readonly pluginId;
87
+ /**
88
+ * Creates an resource-lock handle owned by a plugin.
89
+ *
90
+ * @param app - The Obsidian app instance.
91
+ * @param pluginId - The id of the owning plugin (e.g. its `manifest.id`). Locks are attributed to
92
+ * it for reference-counting and the indicators' "locked by" tooltip.
93
+ */
94
+ constructor(app: App, pluginId: string);
95
+ /**
96
+ * Opens an **ambient bypass scope**: while the returned {@link Disposable} is not disposed, this
97
+ * plugin's own mutations of the given paths (and, for a folder, its whole subtree) pass through the
98
+ * mutation blocker instead of throwing {@link ResourceLockedError}. Any mutation of a blocked path
99
+ * NOT covered by an active bypass scope is still rejected. Use it with a `using` declaration around
100
+ * the operation that legitimately mutates resources it has locked with
101
+ * {@link ResourceLockComponentLockForPathOptions.shouldBlockMutations}:
102
+ *
103
+ * ```ts
104
+ * using _bypass = component.bypassBlockedMutations([sourceFile, targetFile]);
105
+ * // ... the operation's own vault mutations of those paths are allowed ...
106
+ * // scope end: the paths are enforced again
107
+ * ```
108
+ *
109
+ * @param pathsOrFiles - The paths (or files/folders) whose mutation by this operation is sanctioned.
110
+ * @returns A {@link Disposable} that ends the bypass scope when disposed.
111
+ */
112
+ bypassBlockedMutations(pathsOrFiles: readonly PathOrFile[]): Disposable;
113
+ /**
114
+ * Checks whether the path is locked directly or lies under a `subtree`-locked ancestor folder.
115
+ *
116
+ * @param pathOrFile - The path or file to check.
117
+ * @returns `true` if the path is itself locked or is covered by a `subtree` lock on an ancestor.
118
+ */
119
+ isLockedByAncestorForPath(pathOrFile: PathOrFile): boolean;
120
+ /**
121
+ * Checks whether the note at the given path is currently locked by any plugin.
122
+ *
123
+ * @param pathOrFile - The path or file of the note to check.
124
+ * @returns `true` if the note has at least one active lock, `false` otherwise.
125
+ */
126
+ isLockedForPath(pathOrFile: PathOrFile): boolean;
127
+ /**
128
+ * Checks whether a vault mutation of the path is currently blocked by a `shouldBlockMutations` lock
129
+ * on the path itself or on a `subtree`-locked ancestor folder.
130
+ *
131
+ * @param pathOrFile - The path or file to check.
132
+ * @returns `true` if editing/deleting/renaming/moving/creating the path would throw a
133
+ * {@link ResourceLockedError}.
134
+ */
135
+ isMutationBlockedByAncestorForPath(pathOrFile: PathOrFile): boolean;
136
+ /**
137
+ * Locks the note at the given path on behalf of this plugin, making it read-only in every current
138
+ * and future {@link MarkdownView} until the lock is released. Reference-counted: balance each call
139
+ * with a dispose of the returned {@link Disposable} (ideally via `using`) or {@link unlockForPath}.
140
+ *
141
+ * @param pathOrFile - The path or file of the note to lock.
142
+ * @param options - Optional locking options. Pass an
143
+ * {@link ResourceLockComponentLockForPathOptions.abortController} to make the lock cancelable via the
144
+ * indicator's right-click "unlock" menu or {@link requestResourceUnlockForPath}.
145
+ * @returns A {@link Disposable} that releases this lock when disposed. Disposing more than once is a no-op.
146
+ */
147
+ lockForPath(pathOrFile: PathOrFile, options?: ResourceLockComponentLockForPathOptions): Disposable;
148
+ /**
149
+ * Releases every lock still held by this plugin when the component (and thus the plugin) unloads,
150
+ * so no note is left stuck read-only by an operation that never completed.
151
+ */
152
+ onunload(): void;
153
+ /**
154
+ * Releases one lock previously acquired for the note at the given path via {@link lockForPath}.
155
+ *
156
+ * @param pathOrFile - The path or file of the note to unlock.
157
+ */
158
+ unlockForPath(pathOrFile: PathOrFile): void;
159
+ }
160
+ /**
161
+ * Thrown by the vault-mutation blocker when a plugin tries to edit, delete, rename, move, or create a
162
+ * resource whose path is covered by a lock taken with
163
+ * {@link ResourceLockComponentLockForPathOptions.shouldBlockMutations}.
164
+ */
165
+ export declare class ResourceLockedError extends Error {
166
+ /**
167
+ * The vault path whose mutation was blocked.
168
+ */
169
+ readonly path: string;
170
+ /**
171
+ * Creates a {@link ResourceLockedError}.
172
+ *
173
+ * @param path - The vault path whose mutation was blocked.
174
+ */
175
+ constructor(path: string);
176
+ }
177
+ /**
178
+ * Checks whether the note at the given path is currently locked.
179
+ *
180
+ * @param app - The Obsidian app instance.
181
+ * @param pathOrFile - The path or file of the note to check.
182
+ * @returns `true` if the note has at least one active lock, `false` otherwise.
183
+ */
184
+ export declare function isResourceLockedForPath(app: App, pathOrFile: PathOrFile): boolean;
185
+ /**
186
+ * Locks the note at the given path, making it read-only in every current and future
187
+ * {@link MarkdownView} until the lock is released, and showing a lock indicator in the tab header,
188
+ * view action bar, and (while active) the status bar. The indicators' tooltip lists the plugins
189
+ * that currently hold a lock on the note.
190
+ *
191
+ * The lock is reference-counted per calling plugin: each call must be balanced by exactly one
192
+ * release (either disposing the returned {@link Disposable} — ideally via a `using` declaration — or
193
+ * a matching {@link unlockResourceForPath} call).
194
+ *
195
+ * @param app - The Obsidian app instance.
196
+ * @param pathOrFile - The path or file of the note to lock.
197
+ * @param pluginId - The id of the locking plugin (e.g. its `manifest.id`). The lock is attributed to
198
+ * it for reference-counting and the indicators' "locked by" tooltip.
199
+ * @returns A {@link Disposable} that releases this lock when disposed. Disposing more than once is a no-op.
200
+ */
201
+ export declare function lockResourceForPath(app: App, pathOrFile: PathOrFile, pluginId: string): Disposable;
202
+ /**
203
+ * Requests an unlock of the note at the given path by aborting every {@link AbortController} that was
204
+ * associated with a lock on it (via {@link ResourceLockComponent.lockForPath}'s
205
+ * {@link ResourceLockComponentLockForPathOptions.abortController}). The operations holding the lock
206
+ * observe the abort and release their own locks. A no-op when no abortable lock is registered for the
207
+ * path.
208
+ *
209
+ * This lets a consuming plugin wire its own "unlock active note" command without reaching into the
210
+ * lock manager directly.
211
+ *
212
+ * @param app - The Obsidian app instance.
213
+ * @param pathOrFile - The path or file of the note to request an unlock for.
214
+ */
215
+ export declare function requestResourceUnlockForPath(app: App, pathOrFile: PathOrFile): void;
216
+ /**
217
+ * Releases one lock previously acquired for the note at the given path via {@link lockResourceForPath}.
218
+ *
219
+ * When the last lock is released the note becomes fully editable again and its lock indicators are
220
+ * removed. Calling this when the note is not locked is a no-op.
221
+ *
222
+ * @param app - The Obsidian app instance.
223
+ * @param pathOrFile - The path or file of the note to unlock.
224
+ * @param pluginId - The id of the plugin that holds the lock (e.g. its `manifest.id`); one of its
225
+ * locks on the note is released.
226
+ */
227
+ export declare function unlockResourceForPath(app: App, pathOrFile: PathOrFile, pluginId: string): void;
@@ -0,0 +1,373 @@
1
+ /*
2
+ THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
3
+ if you want to view the source, please visit the github repository of this plugin
4
+ */
5
+
6
+ (function initCjs() {
7
+ // eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
8
+ const globalThisRecord = globalThis;
9
+ globalThisRecord['__name'] ??= name;
10
+ const originalRequire = require;
11
+ if (originalRequire && !originalRequire.__isPatched) {
12
+ // eslint-disable-next-line no-global-assign, no-implicit-globals -- We need to patch the `require()` function.
13
+ require = Object.assign(
14
+ (id) => requirePatched(id),
15
+ originalRequire,
16
+ {
17
+ __isPatched: true
18
+ }
19
+ );
20
+ }
21
+
22
+ const newFuncs = {
23
+ __extractDefault() {
24
+ return extractDefault;
25
+ },
26
+ process() {
27
+ const browserProcess = {
28
+ browser: true,
29
+ cwd() {
30
+ return '/';
31
+ },
32
+ env: {},
33
+ platform: 'android'
34
+ };
35
+ return browserProcess;
36
+ }
37
+ };
38
+
39
+ for (const key of Object.keys(newFuncs)) {
40
+ globalThisRecord[key] ??= newFuncs[key]?.();
41
+ }
42
+
43
+ function name(obj) {
44
+ return obj;
45
+ }
46
+
47
+ function extractDefault(module) {
48
+ return module && module.__esModule && 'default' in module ? module.default : module;
49
+ }
50
+
51
+ const OBSIDIAN_BUILT_IN_MODULE_NAMES = [
52
+ 'obsidian',
53
+ '@codemirror/autocomplete',
54
+ '@codemirror/collab',
55
+ '@codemirror/commands',
56
+ '@codemirror/language',
57
+ '@codemirror/lint',
58
+ '@codemirror/search',
59
+ '@codemirror/state',
60
+ '@codemirror/text',
61
+ '@codemirror/view',
62
+ '@lezer/common',
63
+ '@lezer/lr',
64
+ '@lezer/highlight'];
65
+
66
+
67
+ const DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES = [
68
+ '@codemirror/closebrackets',
69
+ '@codemirror/comment',
70
+ '@codemirror/fold',
71
+ '@codemirror/gutter',
72
+ '@codemirror/highlight',
73
+ '@codemirror/history',
74
+ '@codemirror/matchbrackets',
75
+ '@codemirror/panel',
76
+ '@codemirror/rangeset',
77
+ '@codemirror/rectangular-selection',
78
+ '@codemirror/stream-parser',
79
+ '@codemirror/tooltip'];
80
+
81
+
82
+ function requirePatched(id) {
83
+ if (OBSIDIAN_BUILT_IN_MODULE_NAMES.includes(id) || DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES.includes(id)) {
84
+ return originalRequire?.(id);
85
+ }
86
+
87
+ // eslint-disable-next-line @typescript-eslint/no-deprecated, obsidianmd/no-global-this -- Need access to app. Actively use globalThis.
88
+ if (globalThis.app.isMobile) {
89
+ if (id === 'process' || id === 'node:process') {
90
+ // eslint-disable-next-line no-console -- Valid usage.
91
+ console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Fake process object is returned instead.`);
92
+ // eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
93
+ return globalThis.process;
94
+ }
95
+ } else {
96
+ const module = originalRequire?.(id);
97
+ if (module) {
98
+ return extractDefault(module);
99
+ }
100
+ }
101
+
102
+ // eslint-disable-next-line no-console -- Valid usage.
103
+ console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Empty object is returned instead.`);
104
+ return {};
105
+ }
106
+ })();
107
+
108
+ "use strict";
109
+ var __defProp = Object.defineProperty;
110
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
111
+ var __getOwnPropNames = Object.getOwnPropertyNames;
112
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
113
+ var __export = (target, all) => {
114
+ for (var name in all)
115
+ __defProp(target, name, { get: all[name], enumerable: true });
116
+ };
117
+ var __copyProps = (to, from, except, desc) => {
118
+ if (from && typeof from === "object" || typeof from === "function") {
119
+ for (let key of __getOwnPropNames(from))
120
+ if (!__hasOwnProp.call(to, key) && key !== except)
121
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
122
+ }
123
+ return to;
124
+ };
125
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
126
+ var vault_transaction_exports = {};
127
+ __export(vault_transaction_exports, {
128
+ VaultTransaction: () => VaultTransaction
129
+ });
130
+ module.exports = __toCommonJS(vault_transaction_exports);
131
+ var import_type_guards = require('../type-guards.cjs');
132
+ var import_editor = require('./editor.cjs');
133
+ var import_file_system = require('./file-system.cjs');
134
+ var import_vault = require('./vault.cjs');
135
+ const DEFAULT_STAGING_FOLDER_PATH = ".obsidian-dev-utils-temp";
136
+ class VaultTransaction {
137
+ app;
138
+ hasStagingFolder = false;
139
+ mutationBypass = null;
140
+ stagedTrashPaths = [];
141
+ stagingFolderPath;
142
+ state = "open";
143
+ undoSteps = [];
144
+ /**
145
+ * Creates a new vault transaction.
146
+ *
147
+ * @param params - The parameters.
148
+ */
149
+ constructor(params) {
150
+ this.app = params.app;
151
+ this.stagingFolderPath = params.stagingFolderPath ?? DEFAULT_STAGING_FOLDER_PATH;
152
+ this.mutationBypass = params.openMutationBypass?.() ?? null;
153
+ }
154
+ /**
155
+ * Commits the transaction: finalizes soft-deletes by trashing the staged resources for real and
156
+ * removes the staging folder. After this the transaction can no longer be mutated or rolled back.
157
+ *
158
+ * @returns A {@link Promise} that resolves when the commit completes.
159
+ */
160
+ async commit() {
161
+ this.assertOpen();
162
+ this.state = "committed";
163
+ const adapter = this.app.vault.adapter;
164
+ for (const stagedPath of this.stagedTrashPaths) {
165
+ const wasTrashedToSystem = await adapter.trashSystem(stagedPath);
166
+ if (!wasTrashedToSystem) {
167
+ await adapter.trashLocal(stagedPath);
168
+ }
169
+ }
170
+ await this.removeStagingFolder();
171
+ this.disposeMutationBypass();
172
+ }
173
+ /**
174
+ * Creates a file with the given content, recording its removal as the inverse.
175
+ *
176
+ * @param path - The path of the file to create.
177
+ * @param data - The content of the file.
178
+ * @returns A {@link Promise} resolving to the created {@link TFile}.
179
+ */
180
+ async create(path, data) {
181
+ this.assertOpen();
182
+ const file = await this.app.vault.create(path, data);
183
+ const createdPath = file.path;
184
+ this.pushUndo(async () => {
185
+ await this.hardDelete(createdPath);
186
+ });
187
+ return file;
188
+ }
189
+ /**
190
+ * Creates a folder (including missing ancestors), recording its removal as the inverse. A no-op if
191
+ * the folder already exists.
192
+ *
193
+ * @param path - The path of the folder to create.
194
+ * @returns A {@link Promise} that resolves when the folder exists.
195
+ */
196
+ async createFolder(path) {
197
+ this.assertOpen();
198
+ const wasCreated = await (0, import_vault.createFolderSafe)(this.app, path);
199
+ if (wasCreated) {
200
+ this.pushUndo(async () => {
201
+ await this.hardDelete(path);
202
+ });
203
+ }
204
+ }
205
+ /**
206
+ * Replaces the content of a file, capturing the old content so it can be restored on rollback.
207
+ *
208
+ * @param pathOrFile - The path or file to modify.
209
+ * @param newContent - The new content.
210
+ * @returns A {@link Promise} that resolves when the file is modified.
211
+ */
212
+ async modify(pathOrFile, newContent) {
213
+ await this.process(pathOrFile, () => newContent);
214
+ }
215
+ /**
216
+ * Transforms the content of a file, capturing the old content so it can be restored on rollback.
217
+ *
218
+ * @param pathOrFile - The path or file to process.
219
+ * @param newContentProvider - Maps the current content to the new content.
220
+ * @returns A {@link Promise} that resolves when the file is processed.
221
+ */
222
+ async process(pathOrFile, newContentProvider) {
223
+ this.assertOpen();
224
+ const path = (0, import_file_system.getPath)(this.app, pathOrFile);
225
+ const oldContent = await (0, import_vault.readSafe)(this.app, path);
226
+ await (0, import_vault.process)({
227
+ app: this.app,
228
+ newContentProvider: ({ content }) => newContentProvider(content),
229
+ pathOrFile: path,
230
+ resourceLockComponent: null
231
+ });
232
+ (0, import_type_guards.assertNonNullable)(oldContent);
233
+ this.pushUndo(async () => {
234
+ await (0, import_vault.process)({
235
+ app: this.app,
236
+ newContentProvider: oldContent,
237
+ pathOrFile: path,
238
+ resourceLockComponent: null
239
+ });
240
+ (0, import_editor.syncOpenEditorBuffersForPath)(this.app, path, oldContent);
241
+ });
242
+ }
243
+ /**
244
+ * Renames or moves a resource, recording the reverse rename as the inverse.
245
+ *
246
+ * @param oldPathOrFile - The resource to rename.
247
+ * @param newPath - The desired new path (an available path is chosen if it is taken).
248
+ * @returns A {@link Promise} resolving to the actual new path.
249
+ */
250
+ async rename(oldPathOrFile, newPath) {
251
+ this.assertOpen();
252
+ const oldPath = (0, import_file_system.getPath)(this.app, oldPathOrFile);
253
+ const actualNewPath = await (0, import_vault.renameSafe)({ app: this.app, newPath, oldPathOrAbstractFile: oldPathOrFile });
254
+ this.pushUndo(async () => {
255
+ await (0, import_vault.renameSafe)({ app: this.app, newPath: oldPath, oldPathOrAbstractFile: actualNewPath });
256
+ });
257
+ return actualNewPath;
258
+ }
259
+ /**
260
+ * Rolls back the transaction: undoes the completed steps in reverse order and removes the staging
261
+ * folder. Idempotent.
262
+ *
263
+ * @returns A {@link Promise} that resolves when the rollback completes.
264
+ */
265
+ async rollback() {
266
+ if (this.state === "rolledBack") {
267
+ return;
268
+ }
269
+ this.assertNotCommitted();
270
+ this.state = "rolledBack";
271
+ for (const step of [...this.undoSteps].reverse()) {
272
+ await step.undo();
273
+ }
274
+ await this.removeStagingFolder();
275
+ this.disposeMutationBypass();
276
+ }
277
+ /**
278
+ * Rolls back the transaction on disposal unless it was already committed or rolled back.
279
+ */
280
+ async [Symbol.asyncDispose]() {
281
+ if (this.state === "open") {
282
+ await this.rollback();
283
+ }
284
+ }
285
+ /**
286
+ * Soft-deletes a resource by moving it into the staging folder, recording the move-back as the
287
+ * inverse. The real trash happens on {@link VaultTransaction.commit}. A no-op if the resource does
288
+ * not exist.
289
+ *
290
+ * @param pathOrFile - The path or resource to trash.
291
+ * @returns A {@link Promise} that resolves when the resource has been staged.
292
+ */
293
+ async trash(pathOrFile) {
294
+ this.assertOpen();
295
+ const file = (0, import_file_system.getAbstractFileOrNull)({ app: this.app, pathOrFile });
296
+ if (!file) {
297
+ return;
298
+ }
299
+ const originalPath = file.path;
300
+ await this.ensureStagingFolder();
301
+ const stagedPath = await this.getAvailableStagedPath(file.name);
302
+ const adapter = this.app.vault.adapter;
303
+ await adapter.rename(originalPath, stagedPath);
304
+ this.stagedTrashPaths.push(stagedPath);
305
+ this.pushUndo(async () => {
306
+ if (await adapter.exists(stagedPath)) {
307
+ await adapter.rename(stagedPath, originalPath);
308
+ }
309
+ });
310
+ }
311
+ assertNotCommitted() {
312
+ if (this.state === "committed") {
313
+ throw new Error("Cannot roll back a committed transaction.");
314
+ }
315
+ }
316
+ assertOpen() {
317
+ if (this.state !== "open") {
318
+ throw new Error(`Cannot mutate a ${this.state} transaction.`);
319
+ }
320
+ }
321
+ disposeMutationBypass() {
322
+ this.mutationBypass?.[Symbol.dispose]();
323
+ this.mutationBypass = null;
324
+ }
325
+ async ensureStagingFolder() {
326
+ if (this.hasStagingFolder) {
327
+ return;
328
+ }
329
+ const adapter = this.app.vault.adapter;
330
+ if (!await adapter.exists(this.stagingFolderPath)) {
331
+ await adapter.mkdir(this.stagingFolderPath);
332
+ }
333
+ this.hasStagingFolder = true;
334
+ }
335
+ async getAvailableStagedPath(name) {
336
+ const adapter = this.app.vault.adapter;
337
+ const dotIndex = name.lastIndexOf(".");
338
+ const hasExtension = dotIndex > 0;
339
+ const baseName = hasExtension ? name.slice(0, dotIndex) : name;
340
+ const extension = hasExtension ? name.slice(dotIndex) : "";
341
+ let candidate = `${this.stagingFolderPath}/${name}`;
342
+ let counter = 1;
343
+ while (await adapter.exists(candidate)) {
344
+ candidate = `${this.stagingFolderPath}/${baseName} ${counter.toString()}${extension}`;
345
+ counter++;
346
+ }
347
+ return candidate;
348
+ }
349
+ async hardDelete(path) {
350
+ const file = (0, import_file_system.getAbstractFileOrNull)({ app: this.app, pathOrFile: path });
351
+ if (file) {
352
+ await this.app.vault.delete(file, true);
353
+ }
354
+ }
355
+ pushUndo(undo) {
356
+ this.undoSteps.push({ undo });
357
+ }
358
+ async removeStagingFolder() {
359
+ if (!this.hasStagingFolder) {
360
+ return;
361
+ }
362
+ this.hasStagingFolder = false;
363
+ const adapter = this.app.vault.adapter;
364
+ if (await adapter.exists(this.stagingFolderPath)) {
365
+ await adapter.rmdir(this.stagingFolderPath, true);
366
+ }
367
+ }
368
+ }
369
+ // Annotate the CommonJS export names for ESM import in node:
370
+ 0 && (module.exports = {
371
+ VaultTransaction
372
+ });
373
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vc3JjL29ic2lkaWFuL3ZhdWx0LXRyYW5zYWN0aW9uLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogQSByZXZlcnNpYmxlLCB0cmFuc2FjdGlvbmFsIGxvZyBvZiB2YXVsdCBtdXRhdGlvbnMuIEVhY2ggbXV0YXRpb24gcmVjb3JkcyBob3cgdG8gdW5kbyBpdHNlbGY7XG4gKiB7QGxpbmsgVmF1bHRUcmFuc2FjdGlvbi5yb2xsYmFja30gcmV2ZXJzZXMgdGhlIGNvbXBsZXRlZCBzdGVwcyBpbiByZXZlcnNlIG9yZGVyIGFuZFxuICoge0BsaW5rIFZhdWx0VHJhbnNhY3Rpb24uY29tbWl0fSBmaW5hbGl6ZXMgdGhlbS4gSXQgaXMgYnVpbHQgb24gdGhlIGAqU2FmZWAgdmF1bHQgd3JhcHBlcnMgc28gaXRcbiAqIGluaGVyaXRzIHRoZWlyIGNhc2UtaW5zZW5zaXRpdmUgLyByZXRyeSAvIG1pc3NpbmctZmlsZSBoYW5kbGluZy5cbiAqXG4gKiBBIGRlbGV0aW9uIGlzIHBlcmZvcm1lZCBhcyBhICoqc29mdC1kZWxldGUqKjogdGhlIHJlc291cmNlIGlzIG1vdmVkIGFzaWRlIGludG8gYSBkb3QtcHJlZml4ZWRcbiAqIHN0YWdpbmcgZm9sZGVyIChhIGZvbGRlciBtb3ZlcyBpdHMgd2hvbGUgc3VidHJlZSBpbiBhIHNpbmdsZSByZW5hbWUsIHNvIHN1YnRyZWUgcm9sbGJhY2sgaXMgY2hlYXBcbiAqIGFuZCBmYWl0aGZ1bCkuIEJlY2F1c2UgT2JzaWRpYW4ncyB2YXVsdCBmaWxlLXRyZWUgYW5kIGZpbGUgd2F0Y2hlciBpZ25vcmUgYW55IGAuYC1wcmVmaXhlZCBwYXRoLFxuICogc3RhZ2VkIHJlc291cmNlcyBhcmUgKip1bnRyYWNrZWQqKiBcdTIwMTQgdGhleSBkbyBub3QgY2h1cm4gdGhlIG1ldGFkYXRhIGNhY2hlLCBmbGFzaCBpbiB0aGUgZmlsZVxuICogZXhwbG9yZXIsIG9yIGdldCBwcm9wYWdhdGVkIGJ5IHN5bmMgd2hpbGUgdGhlIHRyYW5zYWN0aW9uIGlzIGluIGZsaWdodC4gVGhlIGZsaXAgc2lkZSBpcyB0aGF0XG4gKiBgdmF1bHQuKmAgbWV0aG9kcyBjYW5ub3Qgc2VlIHRoZSBzdGFnaW5nIGFyZWEsIHNvIEFMTCBzdGFnaW5nIEkvTyBnb2VzIHRocm91Z2hcbiAqIHtAbGluayBBcHAudmF1bHQuYWRhcHRlcn0gKGBta2RpcmAvYHJlbmFtZWAvYGV4aXN0c2AvYHRyYXNoU3lzdGVtYC9gcm1kaXJgKS5cbiAqIHtAbGluayBWYXVsdFRyYW5zYWN0aW9uLmNvbW1pdH0gdGhlbiB0cmFzaGVzIHRoZSBzdGFnZWQgcmVzb3VyY2VzIGZvciByZWFsOyB7QGxpbmsgVmF1bHRUcmFuc2FjdGlvbi5yb2xsYmFja31cbiAqIG1vdmVzIHRoZW0gYmFjay5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7XG4gIEFwcCxcbiAgVEZpbGVcbn0gZnJvbSAnb2JzaWRpYW4nO1xuXG5pbXBvcnQgdHlwZSB7IFBhdGhPckFic3RyYWN0RmlsZSB9IGZyb20gJy4vZmlsZS1zeXN0ZW0udHMnO1xuXG5pbXBvcnQgeyBhc3NlcnROb25OdWxsYWJsZSB9IGZyb20gJy4uL3R5cGUtZ3VhcmRzLnRzJztcbmltcG9ydCB7IHN5bmNPcGVuRWRpdG9yQnVmZmVyc0ZvclBhdGggfSBmcm9tICcuL2VkaXRvci50cyc7XG5pbXBvcnQge1xuICBnZXRBYnN0cmFjdEZpbGVPck51bGwsXG4gIGdldFBhdGhcbn0gZnJvbSAnLi9maWxlLXN5c3RlbS50cyc7XG5pbXBvcnQge1xuICBjcmVhdGVGb2xkZXJTYWZlLFxuICBwcm9jZXNzLFxuICByZWFkU2FmZSxcbiAgcmVuYW1lU2FmZVxufSBmcm9tICcuL3ZhdWx0LnRzJztcblxuY29uc3QgREVGQVVMVF9TVEFHSU5HX0ZPTERFUl9QQVRIID0gJy5vYnNpZGlhbi1kZXYtdXRpbHMtdGVtcCc7XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3IgdGhlIHtAbGluayBWYXVsdFRyYW5zYWN0aW9ufSBjb25zdHJ1Y3Rvci5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBWYXVsdFRyYW5zYWN0aW9uQ29uc3RydWN0b3JQYXJhbXMge1xuICAvKipcbiAgICogVGhlIE9ic2lkaWFuIGFwcGxpY2F0aW9uIGluc3RhbmNlLlxuICAgKi9cbiAgcmVhZG9ubHkgYXBwOiBBcHA7XG5cbiAgLyoqXG4gICAqIE9wZW5zIGFuIGFtYmllbnQgbXV0YXRpb24tYnlwYXNzIHNjb3BlIGNvdmVyaW5nIHRoZSBwYXRocyB0aGlzIHRyYW5zYWN0aW9uIHRvdWNoZXMsIHNvIHRoZVxuICAgKiB0cmFuc2FjdGlvbidzIG93biB3cml0ZXMgcGFzcyBhIGBzaG91bGRCbG9ja011dGF0aW9uc2AgbG9jayB0aGF0IHRoZSBvd25pbmcgb3BlcmF0aW9uIGhvbGRzLiBJdCBpc1xuICAgKiBjYWxsZWQgb25jZSB3aGVuIHRoZSB0cmFuc2FjdGlvbiBpcyBjb25zdHJ1Y3RlZCwgYW5kIHRoZSByZXR1cm5lZCB7QGxpbmsgRGlzcG9zYWJsZX0gaXMgZGlzcG9zZWRcbiAgICogd2hlbiB0aGUgdHJhbnNhY3Rpb24gc2V0dGxlcyAob24gYGNvbW1pdGAsIGByb2xsYmFja2AsIG9yIGBhd2FpdCB1c2luZ2AgZGlzcG9zYWwpIFx1MjAxNCBzbyB0aGUgYnlwYXNzXG4gICAqIHN0YXlzIGFjdGl2ZSB0aHJvdWdoIHJvbGxiYWNrLCBpbmNsdWRpbmcgYXV0by1yb2xsYmFjaywgd2hpY2ggYSBjYWxsZXIgbWFuYWdpbmcgdGhlIHNjb3BlIGl0c2VsZlxuICAgKiBjb3VsZCBlYXNpbHkgZ2V0IHdyb25nLiBUeXBpY2FsbHlcbiAgICogYCgpID0+IHJlc291cmNlTG9ja0NvbXBvbmVudC5ieXBhc3NCbG9ja2VkTXV0YXRpb25zKGxvY2tlZFBhdGhzT3JGaWxlcylgLiBPbWl0IGl0IGZvciB0aGUgY29tbW9uXG4gICAqIHN0YW5kYWxvbmUgY2FzZSB3aGVyZSB0aGUgdHJhbnNhY3Rpb24gb3BlcmF0ZXMgb24gdW5sb2NrZWQgcGF0aHMuXG4gICAqXG4gICAqIEByZXR1cm5zIEEge0BsaW5rIERpc3Bvc2FibGV9IHRoYXQgZW5kcyB0aGUgYnlwYXNzIHNjb3BlLlxuICAgKi9cbiAgb3Blbk11dGF0aW9uQnlwYXNzPyh0aGlzOiB2b2lkKTogRGlzcG9zYWJsZTtcblxuICAvKipcbiAgICogVGhlIGRvdC1wcmVmaXhlZCBmb2xkZXIgdGhhdCBzb2Z0LWRlbGV0ZWQgcmVzb3VyY2VzIGFyZSBtb3ZlZCBpbnRvIHVudGlsIHRoZSB0cmFuc2FjdGlvbiBpc1xuICAgKiBjb21taXR0ZWQgb3Igcm9sbGVkIGJhY2suIEl0IG11c3Qgc3RheSBgLmAtcHJlZml4ZWQgc28gT2JzaWRpYW4gbGVhdmVzIHRoZSBzdGFnZWQgcmVzb3VyY2VzXG4gICAqIHVudHJhY2tlZC5cbiAgICpcbiAgICogQGRlZmF1bHQgYCcub2JzaWRpYW4tZGV2LXV0aWxzLXRlbXAnYFxuICAgKi9cbiAgcmVhZG9ubHkgc3RhZ2luZ0ZvbGRlclBhdGg/OiBzdHJpbmc7XG59XG5cbnR5cGUgVHJhbnNhY3Rpb25TdGF0ZSA9ICdjb21taXR0ZWQnIHwgJ29wZW4nIHwgJ3JvbGxlZEJhY2snO1xuXG5pbnRlcmZhY2UgVW5kb1N0ZXAge1xuICB1bmRvKHRoaXM6IHZvaWQpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG4vKipcbiAqIEEgcmV2ZXJzaWJsZSBsb2cgb2YgdmF1bHQgbXV0YXRpb25zLiBQZXJmb3JtIG11dGF0aW9ucyB0aHJvdWdoIGl0cyBtZXRob2RzLCB0aGVuIGNhbGxcbiAqIHtAbGluayBWYXVsdFRyYW5zYWN0aW9uLmNvbW1pdH0gb24gc3VjY2VzcyBvciB7QGxpbmsgVmF1bHRUcmFuc2FjdGlvbi5yb2xsYmFja30gb24gZmFpbHVyZS4gSXQgaXNcbiAqIGBhd2FpdCB1c2luZ2AtZnJpZW5kbHk6IGlmIG5laXRoZXIgaXMgY2FsbGVkLCBkaXNwb3NhbCByb2xscyBiYWNrLlxuICovXG5leHBvcnQgY2xhc3MgVmF1bHRUcmFuc2FjdGlvbiB7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXBwOiBBcHA7XG4gIHByaXZhdGUgaGFzU3RhZ2luZ0ZvbGRlciA9IGZhbHNlO1xuICBwcml2YXRlIG11dGF0aW9uQnlwYXNzOiBEaXNwb3NhYmxlIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgc3RhZ2VkVHJhc2hQYXRoczogc3RyaW5nW10gPSBbXTtcbiAgcHJpdmF0ZSByZWFkb25seSBzdGFnaW5nRm9sZGVyUGF0aDogc3RyaW5nO1xuICBwcml2YXRlIHN0YXRlOiBUcmFuc2FjdGlvblN0YXRlID0gJ29wZW4nO1xuICBwcml2YXRlIHJlYWRvbmx5IHVuZG9TdGVwczogVW5kb1N0ZXBbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmV3IHZhdWx0IHRyYW5zYWN0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gcGFyYW1zIC0gVGhlIHBhcmFtZXRlcnMuXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3IocGFyYW1zOiBWYXVsdFRyYW5zYWN0aW9uQ29uc3RydWN0b3JQYXJhbXMpIHtcbiAgICB0aGlzLmFwcCA9IHBhcmFtcy5hcHA7XG4gICAgdGhpcy5zdGFnaW5nRm9sZGVyUGF0aCA9IHBhcmFtcy5zdGFnaW5nRm9sZGVyUGF0aCA/PyBERUZBVUxUX1NUQUdJTkdfRk9MREVSX1BBVEg7XG4gICAgdGhpcy5tdXRhdGlvbkJ5cGFzcyA9IHBhcmFtcy5vcGVuTXV0YXRpb25CeXBhc3M/LigpID8/IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogQ29tbWl0cyB0aGUgdHJhbnNhY3Rpb246IGZpbmFsaXplcyBzb2Z0LWRlbGV0ZXMgYnkgdHJhc2hpbmcgdGhlIHN0YWdlZCByZXNvdXJjZXMgZm9yIHJlYWwgYW5kXG4gICAqIHJlbW92ZXMgdGhlIHN0YWdpbmcgZm9sZGVyLiBBZnRlciB0aGlzIHRoZSB0cmFuc2FjdGlvbiBjYW4gbm8gbG9uZ2VyIGJlIG11dGF0ZWQgb3Igcm9sbGVkIGJhY2suXG4gICAqXG4gICAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgY29tbWl0IGNvbXBsZXRlcy5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBjb21taXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5hc3NlcnRPcGVuKCk7XG4gICAgdGhpcy5zdGF0ZSA9ICdjb21taXR0ZWQnO1xuICAgIGNvbnN0IGFkYXB0ZXIgPSB0aGlzLmFwcC52YXVsdC5hZGFwdGVyO1xuICAgIGZvciAoY29uc3Qgc3RhZ2VkUGF0aCBvZiB0aGlzLnN0YWdlZFRyYXNoUGF0aHMpIHtcbiAgICAgIGNvbnN0IHdhc1RyYXNoZWRUb1N5c3RlbSA9IGF3YWl0IGFkYXB0ZXIudHJhc2hTeXN0ZW0oc3RhZ2VkUGF0aCk7XG4gICAgICBpZiAoIXdhc1RyYXNoZWRUb1N5c3RlbSkge1xuICAgICAgICBhd2FpdCBhZGFwdGVyLnRyYXNoTG9jYWwoc3RhZ2VkUGF0aCk7XG4gICAgICB9XG4gICAgfVxuICAgIGF3YWl0IHRoaXMucmVtb3ZlU3RhZ2luZ0ZvbGRlcigpO1xuICAgIHRoaXMuZGlzcG9zZU11dGF0aW9uQnlwYXNzKCk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIGZpbGUgd2l0aCB0aGUgZ2l2ZW4gY29udGVudCwgcmVjb3JkaW5nIGl0cyByZW1vdmFsIGFzIHRoZSBpbnZlcnNlLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBmaWxlIHRvIGNyZWF0ZS5cbiAgICogQHBhcmFtIGRhdGEgLSBUaGUgY29udGVudCBvZiB0aGUgZmlsZS5cbiAgICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gcmVzb2x2aW5nIHRvIHRoZSBjcmVhdGVkIHtAbGluayBURmlsZX0uXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgY3JlYXRlKHBhdGg6IHN0cmluZywgZGF0YTogc3RyaW5nKTogUHJvbWlzZTxURmlsZT4ge1xuICAgIHRoaXMuYXNzZXJ0T3BlbigpO1xuICAgIGNvbnN0IGZpbGUgPSBhd2FpdCB0aGlzLmFwcC52YXVsdC5jcmVhdGUocGF0aCwgZGF0YSk7XG4gICAgY29uc3QgY3JlYXRlZFBhdGggPSBmaWxlLnBhdGg7XG4gICAgdGhpcy5wdXNoVW5kbyhhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB0aGlzLmhhcmREZWxldGUoY3JlYXRlZFBhdGgpO1xuICAgIH0pO1xuICAgIHJldHVybiBmaWxlO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBmb2xkZXIgKGluY2x1ZGluZyBtaXNzaW5nIGFuY2VzdG9ycyksIHJlY29yZGluZyBpdHMgcmVtb3ZhbCBhcyB0aGUgaW52ZXJzZS4gQSBuby1vcCBpZlxuICAgKiB0aGUgZm9sZGVyIGFscmVhZHkgZXhpc3RzLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBmb2xkZXIgdG8gY3JlYXRlLlxuICAgKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGZvbGRlciBleGlzdHMuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgY3JlYXRlRm9sZGVyKHBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuYXNzZXJ0T3BlbigpO1xuICAgIGNvbnN0IHdhc0NyZWF0ZWQgPSBhd2FpdCBjcmVhdGVGb2xkZXJTYWZlKHRoaXMuYXBwLCBwYXRoKTtcbiAgICBpZiAod2FzQ3JlYXRlZCkge1xuICAgICAgdGhpcy5wdXNoVW5kbyhhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaGFyZERlbGV0ZShwYXRoKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYWNlcyB0aGUgY29udGVudCBvZiBhIGZpbGUsIGNhcHR1cmluZyB0aGUgb2xkIGNvbnRlbnQgc28gaXQgY2FuIGJlIHJlc3RvcmVkIG9uIHJvbGxiYWNrLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aE9yRmlsZSAtIFRoZSBwYXRoIG9yIGZpbGUgdG8gbW9kaWZ5LlxuICAgKiBAcGFyYW0gbmV3Q29udGVudCAtIFRoZSBuZXcgY29udGVudC5cbiAgICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBmaWxlIGlzIG1vZGlmaWVkLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIG1vZGlmeShwYXRoT3JGaWxlOiBQYXRoT3JBYnN0cmFjdEZpbGUsIG5ld0NvbnRlbnQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMucHJvY2VzcyhwYXRoT3JGaWxlLCAoKSA9PiBuZXdDb250ZW50KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm1zIHRoZSBjb250ZW50IG9mIGEgZmlsZSwgY2FwdHVyaW5nIHRoZSBvbGQgY29udGVudCBzbyBpdCBjYW4gYmUgcmVzdG9yZWQgb24gcm9sbGJhY2suXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSB0byBwcm9jZXNzLlxuICAgKiBAcGFyYW0gbmV3Q29udGVudFByb3ZpZGVyIC0gTWFwcyB0aGUgY3VycmVudCBjb250ZW50IHRvIHRoZSBuZXcgY29udGVudC5cbiAgICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBmaWxlIGlzIHByb2Nlc3NlZC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBwcm9jZXNzKHBhdGhPckZpbGU6IFBhdGhPckFic3RyYWN0RmlsZSwgbmV3Q29udGVudFByb3ZpZGVyOiAoY29udGVudDogc3RyaW5nKSA9PiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmFzc2VydE9wZW4oKTtcbiAgICBjb25zdCBwYXRoID0gZ2V0UGF0aCh0aGlzLmFwcCwgcGF0aE9yRmlsZSk7XG4gICAgY29uc3Qgb2xkQ29udGVudCA9IGF3YWl0IHJlYWRTYWZlKHRoaXMuYXBwLCBwYXRoKTtcbiAgICBhd2FpdCBwcm9jZXNzKHtcbiAgICAgIGFwcDogdGhpcy5hcHAsXG4gICAgICBuZXdDb250ZW50UHJvdmlkZXI6ICh7IGNvbnRlbnQgfSkgPT4gbmV3Q29udGVudFByb3ZpZGVyKGNvbnRlbnQpLFxuICAgICAgcGF0aE9yRmlsZTogcGF0aCxcbiAgICAgIHJlc291cmNlTG9ja0NvbXBvbmVudDogbnVsbFxuICAgIH0pO1xuICAgIC8vIFJlYWNoaW5nIHRoaXMgbGluZSBtZWFucyBgcHJvY2Vzc2Agc3VjY2VlZGVkLiBBIG1pc3NpbmcgZmlsZSB3b3VsZCBoYXZlIHRocm93biBhYm92ZSAoaXRzXG4gICAgLy8gYHNob3VsZEZhaWxPbk1pc3NpbmdGaWxlYCBkZWZhdWx0KSwgc28gdGhlIHByZS1pbWFnZSB3YXMgY2FwdHVyZWQgYW5kIGNhbiBiZSByZXN0b3JlZCBvbiByb2xsYmFjay5cbiAgICBhc3NlcnROb25OdWxsYWJsZShvbGRDb250ZW50KTtcbiAgICB0aGlzLnB1c2hVbmRvKGFzeW5jICgpID0+IHtcbiAgICAgIGF3YWl0IHByb2Nlc3Moe1xuICAgICAgICBhcHA6IHRoaXMuYXBwLFxuICAgICAgICBuZXdDb250ZW50UHJvdmlkZXI6IG9sZENvbnRlbnQsXG4gICAgICAgIHBhdGhPckZpbGU6IHBhdGgsXG4gICAgICAgIHJlc291cmNlTG9ja0NvbXBvbmVudDogbnVsbFxuICAgICAgfSk7XG4gICAgICAvLyBBIGNvbnN1bWVyIG1heSBoYXZlIGVkaXRlZCB0aGlzIG5vdGUgdGhyb3VnaCB0aGUgZWRpdG9yLCBsZWF2aW5nIGEgZGlydHkgYnVmZmVyLlxuICAgICAgLy8gSXRzIHBlbmRpbmcgYXV0b3NhdmUgY291bGQgcmUtd3JpdGUgdGhhdCBzdGFsZSBidWZmZXIgb3ZlciB0aGUgZGlzayByZXN0b3JlIGFib3ZlIGFuZCBjbG9iYmVyIGl0LlxuICAgICAgLy8gUmVzZXR0aW5nIHRoZSBvcGVuIGVkaXRvcidzIGJ1ZmZlciB0byB0aGUgcmVzdG9yZWQgY29udGVudCBrZWVwcyBpdHMgbmV4dCBzYXZlIGEgbm8tb3AuXG4gICAgICAvLyBPbmx5IHRoZSB2aWV3IHN0aWxsIHNob3dpbmcgdGhpcyBwYXRoIGlzIHRvdWNoZWQuXG4gICAgICAvLyBWYXVsdFRyYW5zYWN0aW9uIGFscmVhZHkgZmx1c2hlcyB2aWEgYHNhdmVOb3RlYCwgc28gdGhpcyByZXNldCBpcyBiZWx0LWFuZC1zdXNwZW5kZXJzLlxuICAgICAgc3luY09wZW5FZGl0b3JCdWZmZXJzRm9yUGF0aCh0aGlzLmFwcCwgcGF0aCwgb2xkQ29udGVudCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVuYW1lcyBvciBtb3ZlcyBhIHJlc291cmNlLCByZWNvcmRpbmcgdGhlIHJldmVyc2UgcmVuYW1lIGFzIHRoZSBpbnZlcnNlLlxuICAgKlxuICAgKiBAcGFyYW0gb2xkUGF0aE9yRmlsZSAtIFRoZSByZXNvdXJjZSB0byByZW5hbWUuXG4gICAqIEBwYXJhbSBuZXdQYXRoIC0gVGhlIGRlc2lyZWQgbmV3IHBhdGggKGFuIGF2YWlsYWJsZSBwYXRoIGlzIGNob3NlbiBpZiBpdCBpcyB0YWtlbikuXG4gICAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHJlc29sdmluZyB0byB0aGUgYWN0dWFsIG5ldyBwYXRoLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHJlbmFtZShvbGRQYXRoT3JGaWxlOiBQYXRoT3JBYnN0cmFjdEZpbGUsIG5ld1BhdGg6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgdGhpcy5hc3NlcnRPcGVuKCk7XG4gICAgY29uc3Qgb2xkUGF0aCA9IGdldFBhdGgodGhpcy5hcHAsIG9sZFBhdGhPckZpbGUpO1xuICAgIGNvbnN0IGFjdHVhbE5ld1BhdGggPSBhd2FpdCByZW5hbWVTYWZlKHsgYXBwOiB0aGlzLmFwcCwgbmV3UGF0aCwgb2xkUGF0aE9yQWJzdHJhY3RGaWxlOiBvbGRQYXRoT3JGaWxlIH0pO1xuICAgIHRoaXMucHVzaFVuZG8oYXN5bmMgKCkgPT4ge1xuICAgICAgYXdhaXQgcmVuYW1lU2FmZSh7IGFwcDogdGhpcy5hcHAsIG5ld1BhdGg6IG9sZFBhdGgsIG9sZFBhdGhPckFic3RyYWN0RmlsZTogYWN0dWFsTmV3UGF0aCB9KTtcbiAgICB9KTtcbiAgICByZXR1cm4gYWN0dWFsTmV3UGF0aDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSb2xscyBiYWNrIHRoZSB0cmFuc2FjdGlvbjogdW5kb2VzIHRoZSBjb21wbGV0ZWQgc3RlcHMgaW4gcmV2ZXJzZSBvcmRlciBhbmQgcmVtb3ZlcyB0aGUgc3RhZ2luZ1xuICAgKiBmb2xkZXIuIElkZW1wb3RlbnQuXG4gICAqXG4gICAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgcm9sbGJhY2sgY29tcGxldGVzLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHJvbGxiYWNrKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLnN0YXRlID09PSAncm9sbGVkQmFjaycpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5hc3NlcnROb3RDb21taXR0ZWQoKTtcbiAgICB0aGlzLnN0YXRlID0gJ3JvbGxlZEJhY2snO1xuICAgIGZvciAoY29uc3Qgc3RlcCBvZiBbLi4udGhpcy51bmRvU3RlcHNdLnJldmVyc2UoKSkge1xuICAgICAgYXdhaXQgc3RlcC51bmRvKCk7XG4gICAgfVxuICAgIGF3YWl0IHRoaXMucmVtb3ZlU3RhZ2luZ0ZvbGRlcigpO1xuICAgIHRoaXMuZGlzcG9zZU11dGF0aW9uQnlwYXNzKCk7XG4gIH1cblxuICAvKipcbiAgICogUm9sbHMgYmFjayB0aGUgdHJhbnNhY3Rpb24gb24gZGlzcG9zYWwgdW5sZXNzIGl0IHdhcyBhbHJlYWR5IGNvbW1pdHRlZCBvciByb2xsZWQgYmFjay5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBbU3ltYm9sLmFzeW5jRGlzcG9zZV0oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuc3RhdGUgPT09ICdvcGVuJykge1xuICAgICAgYXdhaXQgdGhpcy5yb2xsYmFjaygpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTb2Z0LWRlbGV0ZXMgYSByZXNvdXJjZSBieSBtb3ZpbmcgaXQgaW50byB0aGUgc3RhZ2luZyBmb2xkZXIsIHJlY29yZGluZyB0aGUgbW92ZS1iYWNrIGFzIHRoZVxuICAgKiBpbnZlcnNlLiBUaGUgcmVhbCB0cmFzaCBoYXBwZW5zIG9uIHtAbGluayBWYXVsdFRyYW5zYWN0aW9uLmNvbW1pdH0uIEEgbm8tb3AgaWYgdGhlIHJlc291cmNlIGRvZXNcbiAgICogbm90IGV4aXN0LlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aE9yRmlsZSAtIFRoZSBwYXRoIG9yIHJlc291cmNlIHRvIHRyYXNoLlxuICAgKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHJlc291cmNlIGhhcyBiZWVuIHN0YWdlZC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyB0cmFzaChwYXRoT3JGaWxlOiBQYXRoT3JBYnN0cmFjdEZpbGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmFzc2VydE9wZW4oKTtcbiAgICBjb25zdCBmaWxlID0gZ2V0QWJzdHJhY3RGaWxlT3JOdWxsKHsgYXBwOiB0aGlzLmFwcCwgcGF0aE9yRmlsZSB9KTtcbiAgICBpZiAoIWZpbGUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgb3JpZ2luYWxQYXRoID0gZmlsZS5wYXRoO1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlU3RhZ2luZ0ZvbGRlcigpO1xuICAgIGNvbnN0IHN0YWdlZFBhdGggPSBhd2FpdCB0aGlzLmdldEF2YWlsYWJsZVN0YWdlZFBhdGgoZmlsZS5uYW1lKTtcbiAgICBjb25zdCBhZGFwdGVyID0gdGhpcy5hcHAudmF1bHQuYWRhcHRlcjtcbiAgICAvLyBNb3ZlIHRocm91Z2ggdGhlIGFkYXB0ZXIsIG5vdCBgdmF1bHQucmVuYW1lYDogdGhlIHN0YWdpbmcgZm9sZGVyIGlzIGRvdC1wcmVmaXhlZCBhbmQgdGhlcmVmb3JlXG4gICAgLy8gVW50cmFja2VkLCBzbyBhIGB2YXVsdC4qYCBtb3ZlIGNhbm5vdCB0YXJnZXQgaXQgKG5vciBzaG91bGQgaXQgcmV3cml0ZSBsaW5rcyB0byBhIHNvZnQtZGVsZXRlKS5cbiAgICBhd2FpdCBhZGFwdGVyLnJlbmFtZShvcmlnaW5hbFBhdGgsIHN0YWdlZFBhdGgpO1xuICAgIHRoaXMuc3RhZ2VkVHJhc2hQYXRocy5wdXNoKHN0YWdlZFBhdGgpO1xuICAgIHRoaXMucHVzaFVuZG8oYXN5bmMgKCkgPT4ge1xuICAgICAgaWYgKGF3YWl0IGFkYXB0ZXIuZXhpc3RzKHN0YWdlZFBhdGgpKSB7XG4gICAgICAgIGF3YWl0IGFkYXB0ZXIucmVuYW1lKHN0YWdlZFBhdGgsIG9yaWdpbmFsUGF0aCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzc2VydE5vdENvbW1pdHRlZCgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zdGF0ZSA9PT0gJ2NvbW1pdHRlZCcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IHJvbGwgYmFjayBhIGNvbW1pdHRlZCB0cmFuc2FjdGlvbi4nKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzc2VydE9wZW4oKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuc3RhdGUgIT09ICdvcGVuJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDYW5ub3QgbXV0YXRlIGEgJHt0aGlzLnN0YXRlfSB0cmFuc2FjdGlvbi5gKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGRpc3Bvc2VNdXRhdGlvbkJ5cGFzcygpOiB2b2lkIHtcbiAgICB0aGlzLm11dGF0aW9uQnlwYXNzPy5bU3ltYm9sLmRpc3Bvc2VdKCk7XG4gICAgdGhpcy5tdXRhdGlvbkJ5cGFzcyA9IG51bGw7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGVuc3VyZVN0YWdpbmdGb2xkZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuaGFzU3RhZ2luZ0ZvbGRlcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBhZGFwdGVyID0gdGhpcy5hcHAudmF1bHQuYWRhcHRlcjtcbiAgICBpZiAoIWF3YWl0IGFkYXB0ZXIuZXhpc3RzKHRoaXMuc3RhZ2luZ0ZvbGRlclBhdGgpKSB7XG4gICAgICBhd2FpdCBhZGFwdGVyLm1rZGlyKHRoaXMuc3RhZ2luZ0ZvbGRlclBhdGgpO1xuICAgIH1cbiAgICB0aGlzLmhhc1N0YWdpbmdGb2xkZXIgPSB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXRBdmFpbGFibGVTdGFnZWRQYXRoKG5hbWU6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgYWRhcHRlciA9IHRoaXMuYXBwLnZhdWx0LmFkYXB0ZXI7XG4gICAgY29uc3QgZG90SW5kZXggPSBuYW1lLmxhc3RJbmRleE9mKCcuJyk7XG4gICAgY29uc3QgaGFzRXh0ZW5zaW9uID0gZG90SW5kZXggPiAwO1xuICAgIGNvbnN0IGJhc2VOYW1lID0gaGFzRXh0ZW5zaW9uID8gbmFtZS5zbGljZSgwLCBkb3RJbmRleCkgOiBuYW1lO1xuICAgIGNvbnN0IGV4dGVuc2lvbiA9IGhhc0V4dGVuc2lvbiA/IG5hbWUuc2xpY2UoZG90SW5kZXgpIDogJyc7XG4gICAgbGV0IGNhbmRpZGF0ZSA9IGAke3RoaXMuc3RhZ2luZ0ZvbGRlclBhdGh9LyR7bmFtZX1gO1xuICAgIGxldCBjb3VudGVyID0gMTtcbiAgICB3aGlsZSAoYXdhaXQgYWRhcHRlci5leGlzdHMoY2FuZGlkYXRlKSkge1xuICAgICAgY2FuZGlkYXRlID0gYCR7dGhpcy5zdGFnaW5nRm9sZGVyUGF0aH0vJHtiYXNlTmFtZX0gJHtjb3VudGVyLnRvU3RyaW5nKCl9JHtleHRlbnNpb259YDtcbiAgICAgIGNvdW50ZXIrKztcbiAgICB9XG4gICAgcmV0dXJuIGNhbmRpZGF0ZTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFyZERlbGV0ZShwYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBmaWxlID0gZ2V0QWJzdHJhY3RGaWxlT3JOdWxsKHsgYXBwOiB0aGlzLmFwcCwgcGF0aE9yRmlsZTogcGF0aCB9KTtcbiAgICBpZiAoZmlsZSkge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG9ic2lkaWFubWQvcHJlZmVyLWZpbGUtbWFuYWdlci10cmFzaC1maWxlIC0tIFVuZG9pbmcgYSB0cmFuc2FjdGlvbi1sb2NhbCBjcmVhdGUgbXVzdCB2YW5pc2ggaXQgZW50aXJlbHksIG5vdCBsYW5kIGl0IGluIHRoZSB1c2VyJ3MgdHJhc2guXG4gICAgICBhd2FpdCB0aGlzLmFwcC52YXVsdC5kZWxldGUoZmlsZSwgdHJ1ZSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBwdXNoVW5kbyh1bmRvOiAoKSA9PiBQcm9taXNlPHZvaWQ+KTogdm9pZCB7XG4gICAgdGhpcy51bmRvU3RlcHMucHVzaCh7IHVuZG8gfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlbW92ZVN0YWdpbmdGb2xkZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLmhhc1N0YWdpbmdGb2xkZXIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5oYXNTdGFnaW5nRm9sZGVyID0gZmFsc2U7XG4gICAgY29uc3QgYWRhcHRlciA9IHRoaXMuYXBwLnZhdWx0LmFkYXB0ZXI7XG4gICAgaWYgKGF3YWl0IGFkYXB0ZXIuZXhpc3RzKHRoaXMuc3RhZ2luZ0ZvbGRlclBhdGgpKSB7XG4gICAgICBhd2FpdCBhZGFwdGVyLnJtZGlyKHRoaXMuc3RhZ2luZ0ZvbGRlclBhdGgsIHRydWUpO1xuICAgIH1cbiAgfVxufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBMEJBLHlCQUFrQztBQUNsQyxvQkFBNkM7QUFDN0Ms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