obsidian-dev-utils 94.2.0 → 94.4.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.
@@ -0,0 +1,249 @@
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 initEsm() {
7
+ // eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
8
+ if (globalThis.process) {
9
+ return;
10
+ }
11
+
12
+ const browserProcess = {
13
+ browser: true,
14
+ cwd() {
15
+ return '/';
16
+ },
17
+ env: {},
18
+ platform: 'android'
19
+ };
20
+ // eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
21
+ globalThis.process = browserProcess;
22
+ })();
23
+
24
+ import {
25
+ readdirSync,
26
+ readFileSync
27
+ } from "node:fs";
28
+ import { join } from "node:path";
29
+ import process from "node:process";
30
+ import { evalInObsidian } from "obsidian-integration-testing";
31
+ import { getTemporaryVault } from "obsidian-integration-testing/vitest-global-setup-plugin";
32
+ import {
33
+ beforeAll,
34
+ describe,
35
+ expect,
36
+ it
37
+ } from "vitest";
38
+ import { getRootFolder } from "./root.mjs";
39
+ const CODE_SCRIPT_TOOLKIT_PLUGIN_ID = "fix-require-modules";
40
+ const DEFAULT_BUTTON_RESULT_TIMEOUT_IN_MILLISECONDS = 15e3;
41
+ const DEFAULT_SETTLE_TIMEOUT_IN_MILLISECONDS = 2e4;
42
+ const POLL_INTERVAL_IN_MILLISECONDS = 100;
43
+ const OUTPUT_EXCERPT_LENGTH = 400;
44
+ const DEFAULT_EXCLUDED_NOTES = ["README.md"];
45
+ const CODE_BUTTON_FENCE_REG_EXP = /^\s*```code-button/gm;
46
+ function formatFailures(noteName, failures) {
47
+ if (failures.length === 0) {
48
+ return "";
49
+ }
50
+ const details = failures.map((failure) => ` - "${failure.caption}" [${failure.status}]: ${failure.output.replaceAll(/\s+/g, " ").trim()}`).join("\n");
51
+ return `${String(failures.length)} button(s) in ${noteName} did not run cleanly:
52
+ ${details}`;
53
+ }
54
+ function listNotesWithButtons(demoVaultPath, excludedNotes) {
55
+ return readdirSync(demoVaultPath, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md") && !excludedNotes.has(entry.name)).map((entry) => entry.name).sort().map((name) => ({
56
+ buttonCount: (readFileSync(join(demoVaultPath, name), "utf-8").match(CODE_BUTTON_FENCE_REG_EXP) ?? []).length,
57
+ name
58
+ })).filter((note) => note.buttonCount > 0);
59
+ }
60
+ function registerDemoVaultButtonSuite(options = {}) {
61
+ const rootFolder = options.rootFolder ?? getRootFolder() ?? process.cwd();
62
+ const demoVaultPath = join(rootFolder, "demo-vault");
63
+ const excludedNotes = new Set(options.excludedNotes ?? DEFAULT_EXCLUDED_NOTES);
64
+ const settleTimeoutInMilliseconds = options.settleTimeoutInMilliseconds ?? DEFAULT_SETTLE_TIMEOUT_IN_MILLISECONDS;
65
+ const buttonResultTimeoutInMilliseconds = options.buttonResultTimeoutInMilliseconds ?? DEFAULT_BUTTON_RESULT_TIMEOUT_IN_MILLISECONDS;
66
+ const notes = listNotesWithButtons(demoVaultPath, excludedNotes);
67
+ describe("demo-vault buttons", () => {
68
+ beforeAll(async () => {
69
+ const isCodeScriptToolkitLoaded = await checkCodeScriptToolkitLoaded(settleTimeoutInMilliseconds);
70
+ expect(isCodeScriptToolkitLoaded, "CodeScript Toolkit did not load, so no button could render").toBe(true);
71
+ });
72
+ it("has at least one note with a code button", () => {
73
+ expect(notes.length).toBeGreaterThan(0);
74
+ });
75
+ for (const note of notes) {
76
+ it(`runs every code button in ${note.name}`, async () => {
77
+ const captions = await openNoteAndListButtonCaptions(note, settleTimeoutInMilliseconds);
78
+ expect(captions.length, `${note.name} declares ${String(note.buttonCount)} button(s) but only ${String(captions.length)} rendered`).toBeGreaterThanOrEqual(note.buttonCount);
79
+ const failures = [];
80
+ for (const caption of captions) {
81
+ const result = await clickButton(note.name, caption, {
82
+ buttonResultTimeoutInMilliseconds,
83
+ settleTimeoutInMilliseconds
84
+ });
85
+ if (result.status !== "ok") {
86
+ failures.push(result);
87
+ }
88
+ }
89
+ expect(failures, formatFailures(note.name, failures)).toEqual([]);
90
+ });
91
+ }
92
+ });
93
+ }
94
+ async function checkCodeScriptToolkitLoaded(timeoutInMilliseconds) {
95
+ return evalInObsidian({
96
+ async callback({ app, codeScriptToolkitPluginId, intervalInMilliseconds, lib: { waitUntil }, timeoutInMilliseconds: timeout }) {
97
+ if (typeof app.plugins.isEnabled === "function" && !app.plugins.isEnabled()) {
98
+ await app.plugins.setEnable(true);
99
+ }
100
+ if (!app.plugins.getPlugin(codeScriptToolkitPluginId)) {
101
+ await app.plugins.enablePlugin(codeScriptToolkitPluginId);
102
+ }
103
+ try {
104
+ await waitUntil({
105
+ intervalInMilliseconds,
106
+ message: "CodeScript Toolkit never loaded",
107
+ predicate: () => app.plugins.getPlugin(codeScriptToolkitPluginId) !== null,
108
+ timeoutInMilliseconds: timeout
109
+ });
110
+ } catch {
111
+ return false;
112
+ }
113
+ return true;
114
+ },
115
+ input: {
116
+ codeScriptToolkitPluginId: CODE_SCRIPT_TOOLKIT_PLUGIN_ID,
117
+ intervalInMilliseconds: POLL_INTERVAL_IN_MILLISECONDS,
118
+ timeoutInMilliseconds
119
+ },
120
+ vaultPath: getTemporaryVault().path
121
+ });
122
+ }
123
+ async function clickButton(notePath, buttonCaption, timeouts) {
124
+ return evalInObsidian({
125
+ async callback({
126
+ app,
127
+ buttonResultTimeoutInMilliseconds,
128
+ caption,
129
+ intervalInMilliseconds,
130
+ lib: { waitUntil },
131
+ obsidianModule,
132
+ outputExcerptLength,
133
+ settleTimeoutInMilliseconds
134
+ }) {
135
+ function view() {
136
+ return app.workspace.getActiveViewOfType(obsidianModule.MarkdownView);
137
+ }
138
+ function previewEl() {
139
+ return view()?.containerEl.querySelector(":scope .markdown-preview-view") ?? null;
140
+ }
141
+ function buttons() {
142
+ return [...view()?.containerEl.querySelectorAll(":scope .block-language-code-button button.mod-cta") ?? []];
143
+ }
144
+ function findButton() {
145
+ return buttons().find((candidate) => candidate.textContent === caption);
146
+ }
147
+ try {
148
+ await waitUntil({
149
+ intervalInMilliseconds,
150
+ message: `code button "${caption}" never rendered`,
151
+ predicate: () => {
152
+ const scroller = previewEl();
153
+ if (scroller) {
154
+ scroller.scrollTop = scroller.scrollHeight;
155
+ }
156
+ return findButton() !== void 0;
157
+ },
158
+ timeoutInMilliseconds: settleTimeoutInMilliseconds
159
+ });
160
+ } catch {
161
+ return { caption, output: "", status: "timeout" };
162
+ }
163
+ const button = findButton();
164
+ if (!button) {
165
+ return { caption, output: "", status: "timeout" };
166
+ }
167
+ const block = button.closest(".block-language-code-button") ?? button.parentElement;
168
+ button.scrollIntoView();
169
+ button.click();
170
+ let status;
171
+ try {
172
+ await waitUntil({
173
+ intervalInMilliseconds,
174
+ message: `button "${caption}" never reported a result`,
175
+ predicate: () => /Executed (?:successfully|with error)/.test(block?.textContent ?? ""),
176
+ timeoutInMilliseconds: buttonResultTimeoutInMilliseconds
177
+ });
178
+ const text = block?.textContent ?? "";
179
+ if (text.includes("Executed with error")) {
180
+ status = "error";
181
+ } else if (text.includes("Executed successfully")) {
182
+ status = "ok";
183
+ } else {
184
+ status = "unknown";
185
+ }
186
+ } catch {
187
+ status = "timeout";
188
+ }
189
+ return { caption, output: (block?.textContent ?? "").slice(0, outputExcerptLength), status };
190
+ },
191
+ input: {
192
+ buttonResultTimeoutInMilliseconds: timeouts.buttonResultTimeoutInMilliseconds,
193
+ caption: buttonCaption,
194
+ intervalInMilliseconds: POLL_INTERVAL_IN_MILLISECONDS,
195
+ notePath,
196
+ outputExcerptLength: OUTPUT_EXCERPT_LENGTH,
197
+ settleTimeoutInMilliseconds: timeouts.settleTimeoutInMilliseconds
198
+ },
199
+ vaultPath: getTemporaryVault().path
200
+ });
201
+ }
202
+ async function openNoteAndListButtonCaptions(note, settleTimeoutInMilliseconds) {
203
+ return evalInObsidian({
204
+ async callback({ app, expectedButtonCount, intervalInMilliseconds, lib: { waitUntil }, notePath, obsidianModule, timeoutInMilliseconds }) {
205
+ function view() {
206
+ return app.workspace.getActiveViewOfType(obsidianModule.MarkdownView);
207
+ }
208
+ function previewEl() {
209
+ return view()?.containerEl.querySelector(":scope .markdown-preview-view") ?? null;
210
+ }
211
+ function captions() {
212
+ const rendered = [...view()?.containerEl.querySelectorAll(":scope .block-language-code-button button.mod-cta") ?? []].map((button) => button.textContent).filter((caption) => caption !== "");
213
+ return [...new Set(rendered)];
214
+ }
215
+ await app.workspace.openLinkText(notePath.replace(/\.md$/, ""), "", false);
216
+ await app.workspace.getLeaf(false).setViewState({ state: { file: notePath, mode: "preview" }, type: "markdown" });
217
+ try {
218
+ await waitUntil({
219
+ intervalInMilliseconds,
220
+ message: `"${notePath}" never mounted all ${String(expectedButtonCount)} of its buttons`,
221
+ predicate: () => {
222
+ const scroller = previewEl();
223
+ if (scroller) {
224
+ scroller.scrollTop = scroller.scrollHeight;
225
+ }
226
+ return captions().length >= expectedButtonCount;
227
+ },
228
+ timeoutInMilliseconds
229
+ });
230
+ } catch {
231
+ }
232
+ return captions();
233
+ },
234
+ input: {
235
+ expectedButtonCount: note.buttonCount,
236
+ intervalInMilliseconds: POLL_INTERVAL_IN_MILLISECONDS,
237
+ notePath: note.name,
238
+ timeoutInMilliseconds: settleTimeoutInMilliseconds
239
+ },
240
+ vaultPath: getTemporaryVault().path
241
+ });
242
+ }
243
+ export {
244
+ CODE_SCRIPT_TOOLKIT_PLUGIN_ID,
245
+ formatFailures,
246
+ listNotesWithButtons,
247
+ registerDemoVaultButtonSuite
248
+ };
249
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vc3JjL3NjcmlwdC11dGlscy9kZW1vLXZhdWx0LWJ1dHRvbnMudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQGZpbGVcbiAqXG4gKiBBIHZpdGVzdCBzdWl0ZSB0aGF0IG9wZW5zIGV2ZXJ5IG5vdGUgb2YgYSBwbHVnaW4ncyBpbi1yZXBvIGBkZW1vLXZhdWx0L2AsIGNsaWNrcyBlYWNoIGBjb2RlLWJ1dHRvbmBcbiAqIGluIGl0IGFnYWluc3QgYSByZWFsIE9ic2lkaWFuLCBhbmQgZmFpbHMgb24gYW55IGJ1dHRvbiB0aGF0IGVycm9ycyBvciBuZXZlciByZXBvcnRzIGEgcmVzdWx0LlxuICpcbiAqIEEgZGVtbyB2YXVsdCdzIGJ1dHRvbnMgYXJlIHRoZSBvbmUgcGFydCBvZiBpdCBubyBvdGhlciBnYXRlIGNhbiBjaGVjay4gYGxpbnQ6bWRgIHJlYWRzIHRoZSBtYXJrZG93bixcbiAqIHRoZSBjb3ZlcmFnZSBzdWl0ZSAoe0BsaW5rIC4vZGVtby12YXVsdC1jb3ZlcmFnZS50cyB8IGByZWdpc3RlckRlbW9WYXVsdENvdmVyYWdlU3VpdGVgfSkgY2hlY2tzIHRoZVxuICogYXV0aG9yaW5nIGNvbnZlbnRpb25zLCBhbmQgbmVpdGhlciBleGVjdXRlcyBhbnl0aGluZyBcdTIwMTQgYSBidXR0b24gd2hvc2UgYHJlcXVpcmUoJy9kZW1vU2V0dXAudHMnKWAgcGF0aFxuICogaXMgd3JvbmcsIG9yIHRoYXQgY2FsbHMgYW4gQVBJIHRoYXQgaGFzIHNpbmNlIGNoYW5nZWQgc2hhcGUsIGZhaWxzIGF0IGNsaWNrIHRpbWUgYW5kIG5vd2hlcmUgZWxzZS5cbiAqXG4gKiBUaGUgY29tcGFuaW9uIGhhbGYgXHUyMDE0IHNlZWRpbmcgdGhlIHRlbXAgdmF1bHQgd2l0aCB0aGUgZGVtby12YXVsdCB0cmVlLCBpdHMgYC5vYnNpZGlhbmAgY29uZmlnLCBhbmQgdGhlXG4gKiBDb2RlU2NyaXB0IFRvb2xraXQgYmluYXJ5IHRoYXQgdHVybnMgYGBgYCBgYGBjb2RlLWJ1dHRvbiBgYGBgIGZlbmNlcyBpbnRvIGJ1dHRvbnMgXHUyMDE0IGlzXG4gKiBgYnVpbGREZW1vVmF1bHRQb3B1bGF0ZWAgaW4gYG9ic2lkaWFuLWludGVncmF0aW9uLXRlc3RpbmdgOyBoYW5kIGl0cyByZXN1bHQgdG8gdGhlIHByb2plY3Qnc1xuICogYGdsb2JhbFNldHVwYCBhbmQgbmFtZSBDb2RlU2NyaXB0IFRvb2xraXQgaW4gYGVuYWJsZUNvbW11bml0eVBsdWdpbnNgLlxuICovXG5cbmltcG9ydCB7XG4gIHJlYWRkaXJTeW5jLFxuICByZWFkRmlsZVN5bmNcbn0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCBwcm9jZXNzIGZyb20gJ25vZGU6cHJvY2Vzcyc7XG5pbXBvcnQgeyBldmFsSW5PYnNpZGlhbiB9IGZyb20gJ29ic2lkaWFuLWludGVncmF0aW9uLXRlc3RpbmcnO1xuaW1wb3J0IHsgZ2V0VGVtcG9yYXJ5VmF1bHQgfSBmcm9tICdvYnNpZGlhbi1pbnRlZ3JhdGlvbi10ZXN0aW5nL3ZpdGVzdC1nbG9iYWwtc2V0dXAtcGx1Z2luJztcbmltcG9ydCB7XG4gIGJlZm9yZUFsbCxcbiAgZGVzY3JpYmUsXG4gIGV4cGVjdCxcbiAgaXRcbn0gZnJvbSAndml0ZXN0JztcblxuaW1wb3J0IHsgZ2V0Um9vdEZvbGRlciB9IGZyb20gJy4vcm9vdC50cyc7XG5cbi8qKlxuICogQ29kZVNjcmlwdCBUb29sa2l0J3MgbWFuaWZlc3QgaWQuIEl0IGlzIHN0aWxsIHRoZSBwbHVnaW4ncyBvcmlnaW5hbCBgZml4LXJlcXVpcmUtbW9kdWxlc2AgaWQsIHdoaWNoXG4gKiBubyBsb25nZXIgbWF0Y2hlcyBpdHMgbmFtZSBcdTIwMTQgdGhlIGlkIGlzIHB1Ymxpc2hlZCwgc28gaXQgY2Fubm90IGJlIGNoYW5nZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBDT0RFX1NDUklQVF9UT09MS0lUX1BMVUdJTl9JRCA9ICdmaXgtcmVxdWlyZS1tb2R1bGVzJztcblxuLyoqXG4gKiBUaGUgb3V0Y29tZSBvZiBjbGlja2luZyBvbmUgYnV0dG9uLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIERlbW9WYXVsdEJ1dHRvblJlc3VsdCB7XG4gIC8qKlxuICAgKiBUaGUgYnV0dG9uJ3MgY2FwdGlvbiwgYXMgcmVuZGVyZWQuXG4gICAqL1xuICByZWFkb25seSBjYXB0aW9uOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSByZXN1bHQgbGluZSBDb2RlU2NyaXB0IFRvb2xraXQgcHJpbnRlZCB1bmRlciB0aGUgYnV0dG9uLCB0cnVuY2F0ZWQuXG4gICAqL1xuICByZWFkb25seSBvdXRwdXQ6IHN0cmluZztcblxuICAvKipcbiAgICogV2hldGhlciB0aGUgYnV0dG9uIHJlcG9ydGVkIHN1Y2Nlc3MsIHJlcG9ydGVkIGFuIGVycm9yLCBvciBuZXZlciByZXBvcnRlZCBhdCBhbGwuXG4gICAqL1xuICByZWFkb25seSBzdGF0dXM6ICdlcnJvcicgfCAnb2snIHwgJ3RpbWVvdXQnIHwgJ3Vua25vd24nO1xufVxuXG4vKipcbiAqIE9uZSBkZW1vLXZhdWx0IG5vdGUgYW5kIGhvdyBtYW55IGJ1dHRvbnMgaXRzIHNvdXJjZSBkZWNsYXJlcy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEZW1vVmF1bHROb3RlIHtcbiAgLyoqXG4gICAqIEhvdyBtYW55IGBjb2RlLWJ1dHRvbmAgZmVuY2VzIHRoZSBub3RlJ3Mgc291cmNlIGRlY2xhcmVzLlxuICAgKi9cbiAgcmVhZG9ubHkgYnV0dG9uQ291bnQ6IG51bWJlcjtcblxuICAvKipcbiAgICogVGhlIG5vdGUncyBmaWxlIG5hbWUsIHJlbGF0aXZlIHRvIHRoZSBkZW1vIHZhdWx0IHJvb3QuXG4gICAqL1xuICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG59XG5cbi8qKlxuICogVGhlIG9wdGlvbnMgZm9yIHtAbGluayByZWdpc3RlckRlbW9WYXVsdEJ1dHRvblN1aXRlfS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZWdpc3RlckRlbW9WYXVsdEJ1dHRvblN1aXRlT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBIb3cgbG9uZywgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGZvciBhIGNsaWNrZWQgYnV0dG9uIHRvIHJlcG9ydCBhIHJlc3VsdC5cbiAgICpcbiAgICogQGRlZmF1bHQgYDE1MDAwYFxuICAgKi9cbiAgcmVhZG9ubHkgYnV0dG9uUmVzdWx0VGltZW91dEluTWlsbGlzZWNvbmRzPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBOb3RlIGZpbGUgbmFtZXMgKHJlbGF0aXZlIHRvIHRoZSBkZW1vIHZhdWx0IHJvb3QpIHRvIHNraXAuXG4gICAqXG4gICAqIEBkZWZhdWx0IGBbJ1JFQURNRS5tZCddYFxuICAgKi9cbiAgcmVhZG9ubHkgZXhjbHVkZWROb3Rlcz86IHJlYWRvbmx5IHN0cmluZ1tdO1xuXG4gIC8qKlxuICAgKiBUaGUgcmVwb3NpdG9yeSByb290IGhvbGRpbmcgYGRlbW8tdmF1bHQvYC5cbiAgICpcbiAgICogQGRlZmF1bHQgdGhlIHJlc29sdmVkIHJlcG8gcm9vdCwgZmFsbGluZyBiYWNrIHRvIGBwcm9jZXNzLmN3ZCgpYC5cbiAgICovXG4gIHJlYWRvbmx5IHJvb3RGb2xkZXI/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEhvdyBsb25nLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGEgbm90ZSdzIHByZXZpZXcgYW5kIGl0cyBidXR0b25zIHRvIG1vdW50LlxuICAgKlxuICAgKiBAZGVmYXVsdCBgMjAwMDBgXG4gICAqL1xuICByZWFkb25seSBzZXR0bGVUaW1lb3V0SW5NaWxsaXNlY29uZHM/OiBudW1iZXI7XG59XG5cbi8qKlxuICogVGhlIHdhaXRpbmcgYnVkZ2V0cyBhIHNpbmdsZSBidXR0b24gY2xpY2sgaXMgYWxsb3dlZC5cbiAqL1xuaW50ZXJmYWNlIENsaWNrQnV0dG9uVGltZW91dHMge1xuICAvKipcbiAgICogSG93IGxvbmcgdG8gd2FpdCBmb3IgdGhlIGNsaWNrZWQgYnV0dG9uIHRvIHJlcG9ydCBhIHJlc3VsdC5cbiAgICovXG4gIHJlYWRvbmx5IGJ1dHRvblJlc3VsdFRpbWVvdXRJbk1pbGxpc2Vjb25kczogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBIb3cgbG9uZyB0byB3YWl0IGZvciB0aGUgYnV0dG9uIGl0c2VsZiB0byBtb3VudC5cbiAgICovXG4gIHJlYWRvbmx5IHNldHRsZVRpbWVvdXRJbk1pbGxpc2Vjb25kczogbnVtYmVyO1xufVxuXG5jb25zdCBERUZBVUxUX0JVVFRPTl9SRVNVTFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAxNV8wMDA7XG5jb25zdCBERUZBVUxUX1NFVFRMRV9USU1FT1VUX0lOX01JTExJU0VDT05EUyA9IDIwXzAwMDtcbmNvbnN0IFBPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTID0gMTAwO1xuY29uc3QgT1VUUFVUX0VYQ0VSUFRfTEVOR1RIID0gNDAwO1xuXG4vLyBgUkVBRE1FLm1kYCBpcyB0aGUgcmVwby1mYWNpbmcgcGFnZSBHaXRIdWIgcmVuZGVycywgbm90IGEgd2Fsa3Rocm91Z2guIGAwMCBTdGFydC5tZGAgaXMgZGVsaWJlcmF0ZWx5XG4vLyBOT1QgZXhjbHVkZWQ6IHNpbmNlIFQ0NzggdGhlIGxhbmRpbmcgbm90ZXMgY2FycnkgYnV0dG9ucyBvZiB0aGVpciBvd24uXG5jb25zdCBERUZBVUxUX0VYQ0xVREVEX05PVEVTID0gWydSRUFETUUubWQnXTtcblxuY29uc3QgQ09ERV9CVVRUT05fRkVOQ0VfUkVHX0VYUCA9IC9eXFxzKmBgYGNvZGUtYnV0dG9uL2dtO1xuXG4vLyBUaGUgcmVuZGVyZWQtYnV0dG9uIHNlbGVjdG9yIGlzIGA6c2NvcGUgLmJsb2NrLWxhbmd1YWdlLWNvZGUtYnV0dG9uIGJ1dHRvbi5tb2QtY3RhYC4gSXQgaXMgd3JpdHRlblxuLy8gT3V0IGF0IGVhY2ggdXNlIHNpdGUgcmF0aGVyIHRoYW4gaGVsZCBpbiBhIGNvbnN0YW50IGhlcmU6IGV2ZXJ5IGNsb3N1cmUgYmVsb3cgaXMgc2VyaWFsaXplZCB3aXRoXG4vLyBgdG9TdHJpbmcoKWAgYW5kIGV2YWx1YXRlZCBpbnNpZGUgT2JzaWRpYW4sIHdoZXJlIG5vdGhpbmcgZnJvbSB0aGlzIG1vZHVsZSdzIHNjb3BlIGV4aXN0cy5cblxuLyoqXG4gKiBCdWlsZHMgdGhlIGFzc2VydGlvbiBtZXNzYWdlIGZvciBhIG5vdGUncyBmYWlsaW5nIGJ1dHRvbnMuXG4gKlxuICogVGhlIGNhcHR1cmVkIG91dHB1dCBpcyBpbmNsdWRlZCBiZWNhdXNlIGEgYnV0dG9uJ3MgZmFpbHVyZSBpcyBhIHJ1bnRpbWUgb25lIFx1MjAxNCB0aGUgc3RhY2sgQ29kZVNjcmlwdFxuICogVG9vbGtpdCBwcmludHMgdW5kZXIgaXQgaXMgdGhlIG9ubHkgZGlhZ25vc2lzIHRoZXJlIGlzLlxuICpcbiAqIEBwYXJhbSBub3RlTmFtZSAtIFRoZSBub3RlIHRoZSBidXR0b25zIGJlbG9uZyB0by5cbiAqIEBwYXJhbSBmYWlsdXJlcyAtIFRoZSBmYWlsaW5nIGJ1dHRvbnMuXG4gKiBAcmV0dXJucyBUaGUgbWVzc2FnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEZhaWx1cmVzKG5vdGVOYW1lOiBzdHJpbmcsIGZhaWx1cmVzOiByZWFkb25seSBEZW1vVmF1bHRCdXR0b25SZXN1bHRbXSk6IHN0cmluZyB7XG4gIGlmIChmYWlsdXJlcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gJyc7XG4gIH1cblxuICBjb25zdCBkZXRhaWxzID0gZmFpbHVyZXNcbiAgICAubWFwKChmYWlsdXJlKSA9PiBgICAtIFwiJHtmYWlsdXJlLmNhcHRpb259XCIgWyR7ZmFpbHVyZS5zdGF0dXN9XTogJHtmYWlsdXJlLm91dHB1dC5yZXBsYWNlQWxsKC9cXHMrL2csICcgJykudHJpbSgpfWApXG4gICAgLmpvaW4oJ1xcbicpO1xuICByZXR1cm4gYCR7U3RyaW5nKGZhaWx1cmVzLmxlbmd0aCl9IGJ1dHRvbihzKSBpbiAke25vdGVOYW1lfSBkaWQgbm90IHJ1biBjbGVhbmx5OlxcbiR7ZGV0YWlsc31gO1xufVxuXG4vKipcbiAqIExpc3RzIHRoZSBkZW1vIHZhdWx0J3MgdG9wLWxldmVsIG5vdGVzIHRoYXQgZGVjbGFyZSBhdCBsZWFzdCBvbmUgYGNvZGUtYnV0dG9uYC5cbiAqXG4gKiBPbmx5IHRoZSB2YXVsdCByb290IGlzIHdhbGtlZDogYE1hdGVyaWFscy9gIGhvbGRzIGZpeHR1cmVzIHRoZSB3YWxrdGhyb3VnaHMgYWN0IG9uLCBub3Qgd2Fsa3Rocm91Z2hzLlxuICpcbiAqIEBwYXJhbSBkZW1vVmF1bHRQYXRoIC0gVGhlIGRlbW8gdmF1bHQgcm9vdC5cbiAqIEBwYXJhbSBleGNsdWRlZE5vdGVzIC0gTm90ZSBmaWxlIG5hbWVzIHRvIHNraXAuXG4gKiBAcmV0dXJucyBUaGUgbm90ZXMsIHNvcnRlZCBieSBuYW1lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbGlzdE5vdGVzV2l0aEJ1dHRvbnMoZGVtb1ZhdWx0UGF0aDogc3RyaW5nLCBleGNsdWRlZE5vdGVzOiBSZWFkb25seVNldDxzdHJpbmc+KTogRGVtb1ZhdWx0Tm90ZVtdIHtcbiAgcmV0dXJuIHJlYWRkaXJTeW5jKGRlbW9WYXVsdFBhdGgsIHsgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KVxuICAgIC5maWx0ZXIoKGVudHJ5KSA9PiBlbnRyeS5pc0ZpbGUoKSAmJiBlbnRyeS5uYW1lLmVuZHNXaXRoKCcubWQnKSAmJiAhZXhjbHVkZWROb3Rlcy5oYXMoZW50cnkubmFtZSkpXG4gICAgLm1hcCgoZW50cnkpID0+IGVudHJ5Lm5hbWUpXG4gICAgLnNvcnQoKVxuICAgIC5tYXAoKG5hbWUpID0+ICh7XG4gICAgICBidXR0b25Db3VudDogKHJlYWRGaWxlU3luYyhqb2luKGRlbW9WYXVsdFBhdGgsIG5hbWUpLCAndXRmLTgnKS5tYXRjaChDT0RFX0JVVFRPTl9GRU5DRV9SRUdfRVhQKSA/PyBbXSkubGVuZ3RoLFxuICAgICAgbmFtZVxuICAgIH0pKVxuICAgIC5maWx0ZXIoKG5vdGUpID0+IG5vdGUuYnV0dG9uQ291bnQgPiAwKTtcbn1cblxuLyogdjggaWdub3JlIHN0YXJ0IC0tIEV2ZXJ5IGFzc2VydGlvbiBiZWxvdyBydW5zIGFnYWluc3QgYSByZWFsIE9ic2lkaWFuIG92ZXIgQ0RQLCBkcml2ZW4gYnkgY2xvc3VyZXMgdGhpcyBwcm9jZXNzIHNlcmlhbGl6ZXMgYW5kIGFub3RoZXIgZXhlY3V0ZXMuIFRoZSB0ZXN0YWJsZSBwYXJ0cyBhcmUgbGlzdE5vdGVzV2l0aEJ1dHRvbnMgYW5kIGZvcm1hdEZhaWx1cmVzLCB3aGljaCBoYXZlIHRoZWlyIG93biB1bml0IHRlc3RzLiAqL1xuLyoqXG4gKiBSZWdpc3RlcnMgYSB2aXRlc3Qgc3VpdGUgdGhhdCBjbGlja3MgZXZlcnkgYGNvZGUtYnV0dG9uYCBpbiB0aGUgcmVwbydzIGRlbW8gdmF1bHQuXG4gKlxuICogT25lIGBpdGAgcGVyIG5vdGUsIGFuZCBvbmUgYGV2YWxJbk9ic2lkaWFuYCBwZXIgYnV0dG9uOiBhIHNpbmdsZSBjbG9zdXJlIHJ1bnMgYXMgb25lIENEUFxuICogYFJ1bnRpbWUuZXZhbHVhdGVgLCB3aGljaCB0aGUgaGFybmVzcyBjYXBzIGF0IDMwIHMsIHNvIGEgbm90ZSB3aXRoIGEgZG96ZW4gYnV0dG9ucyB3b3VsZCBvdGhlcndpc2VcbiAqIHRpbWUgb3V0IGFzIGEgd2hvbGUgcmF0aGVyIHRoYW4gcmVwb3J0aW5nIHdoaWNoIGJ1dHRvbiBmYWlsZWQuXG4gKlxuICogQHBhcmFtIG9wdGlvbnMgLSBUaGUge0BsaW5rIFJlZ2lzdGVyRGVtb1ZhdWx0QnV0dG9uU3VpdGVPcHRpb25zfS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZ2lzdGVyRGVtb1ZhdWx0QnV0dG9uU3VpdGUob3B0aW9uczogUmVnaXN0ZXJEZW1vVmF1bHRCdXR0b25TdWl0ZU9wdGlvbnMgPSB7fSk6IHZvaWQge1xuICBjb25zdCByb290Rm9sZGVyID0gb3B0aW9ucy5yb290Rm9sZGVyID8/IGdldFJvb3RGb2xkZXIoKSA/PyBwcm9jZXNzLmN3ZCgpO1xuICBjb25zdCBkZW1vVmF1bHRQYXRoID0gam9pbihyb290Rm9sZGVyLCAnZGVtby12YXVsdCcpO1xuICBjb25zdCBleGNsdWRlZE5vdGVzID0gbmV3IFNldChvcHRpb25zLmV4Y2x1ZGVkTm90ZXMgPz8gREVGQVVMVF9FWENMVURFRF9OT1RFUyk7XG4gIGNvbnN0IHNldHRsZVRpbWVvdXRJbk1pbGxpc2Vjb25kcyA9IG9wdGlvbnMuc2V0dGxlVGltZW91dEluTWlsbGlzZWNvbmRzID8/IERFRkFVTFRfU0VUVExFX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTO1xuICBjb25zdCBidXR0b25SZXN1bHRUaW1lb3V0SW5NaWxsaXNlY29uZHMgPSBvcHRpb25zLmJ1dHRvblJlc3VsdFRpbWVvdXRJbk1pbGxpc2Vjb25kcyA/PyBERUZBVUxUX0JVVFRPTl9SRVNVTFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFM7XG5cbiAgY29uc3Qgbm90ZXMgPSBsaXN0Tm90ZXNXaXRoQnV0dG9ucyhkZW1vVmF1bHRQYXRoLCBleGNsdWRlZE5vdGVzKTtcblxuICBkZXNjcmliZSgnZGVtby12YXVsdCBidXR0b25zJywgKCkgPT4ge1xuICAgIGJlZm9yZUFsbChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBpc0NvZGVTY3JpcHRUb29sa2l0TG9hZGVkID0gYXdhaXQgY2hlY2tDb2RlU2NyaXB0VG9vbGtpdExvYWRlZChzZXR0bGVUaW1lb3V0SW5NaWxsaXNlY29uZHMpO1xuICAgICAgLy8gV2l0aG91dCBpdCBldmVyeSBgY29kZS1idXR0b25gIGZlbmNlIHN0YXlzIGEgcGxhaW4gY29kZSBibG9jaywgc28gZXZlcnkgbm90ZSBiZWxvdyB3b3VsZCByZXBvcnRcbiAgICAgIC8vIFplcm8gYnV0dG9ucyBhbmQgcGFzcyB2YWN1b3VzbHkuIEZhaWwgaGVyZSBpbnN0ZWFkLCBvbmNlLCB3aXRoIHRoZSByZWFzb24uXG4gICAgICBleHBlY3QoaXNDb2RlU2NyaXB0VG9vbGtpdExvYWRlZCwgJ0NvZGVTY3JpcHQgVG9vbGtpdCBkaWQgbm90IGxvYWQsIHNvIG5vIGJ1dHRvbiBjb3VsZCByZW5kZXInKS50b0JlKHRydWUpO1xuICAgIH0pO1xuXG4gICAgLy8gQSB2YXVsdCB3aG9zZSBub3RlcyBkZWNsYXJlIG5vIGJ1dHRvbnMgaXMgYSByZWFsIGZhaWx1cmUgb2YgRzk4J3MgaW50ZXJhY3RpdmUgaGFsZiwgbm90IGEgcmVhc29uXG4gICAgLy8gVG8gcmVnaXN0ZXIgbm90aGluZyBhbmQgcmVwb3J0IGdyZWVuLlxuICAgIGl0KCdoYXMgYXQgbGVhc3Qgb25lIG5vdGUgd2l0aCBhIGNvZGUgYnV0dG9uJywgKCkgPT4ge1xuICAgICAgZXhwZWN0KG5vdGVzLmxlbmd0aCkudG9CZUdyZWF0ZXJUaGFuKDApO1xuICAgIH0pO1xuXG4gICAgZm9yIChjb25zdCBub3RlIG9mIG5vdGVzKSB7XG4gICAgICBpdChgcnVucyBldmVyeSBjb2RlIGJ1dHRvbiBpbiAke25vdGUubmFtZX1gLCBhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnN0IGNhcHRpb25zID0gYXdhaXQgb3Blbk5vdGVBbmRMaXN0QnV0dG9uQ2FwdGlvbnMobm90ZSwgc2V0dGxlVGltZW91dEluTWlsbGlzZWNvbmRzKTtcbiAgICAgICAgLy8gUmVhZGluZyB2aWV3IG1vdW50cyBhIG5vdGUncyBsZWFkaW5nIHNlY3Rpb25zIG1vcmUgdGhhbiBvbmNlIHdoaWxlIGl0IHNldHRsZXMsIHNvIHRoZSBET00gY2FuXG4gICAgICAgIC8vIEhvbGQgc2V2ZXJhbCBlbGVtZW50cyBwZXIgZmVuY2UuIFRoZSBjYXB0aW9ucyBhcmUgZGVkdXBsaWNhdGVkLCBhbmQgdGhlIGFzc2VydGlvbiBpcyB0aGF0IGF0XG4gICAgICAgIC8vIExlYXN0IGFzIG1hbnkgRElTVElOQ1QgYnV0dG9ucyByZW5kZXJlZCBhcyB0aGUgc291cmNlIGRlY2xhcmVzIFx1MjAxNCBhIGZlbmNlIHRoYXQgc2lsZW50bHkgc3RheWVkXG4gICAgICAgIC8vIEEgcGxhaW4gY29kZSBibG9jayBpcyB0aGUgZmFpbHVyZSB0aGlzIGNhdGNoZXMuXG4gICAgICAgIGV4cGVjdChjYXB0aW9ucy5sZW5ndGgsIGAke25vdGUubmFtZX0gZGVjbGFyZXMgJHtTdHJpbmcobm90ZS5idXR0b25Db3VudCl9IGJ1dHRvbihzKSBidXQgb25seSAke1N0cmluZyhjYXB0aW9ucy5sZW5ndGgpfSByZW5kZXJlZGApXG4gICAgICAgICAgLnRvQmVHcmVhdGVyVGhhbk9yRXF1YWwobm90ZS5idXR0b25Db3VudCk7XG5cbiAgICAgICAgY29uc3QgZmFpbHVyZXM6IERlbW9WYXVsdEJ1dHRvblJlc3VsdFtdID0gW107XG4gICAgICAgIGZvciAoY29uc3QgY2FwdGlvbiBvZiBjYXB0aW9ucykge1xuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNsaWNrQnV0dG9uKG5vdGUubmFtZSwgY2FwdGlvbiwge1xuICAgICAgICAgICAgYnV0dG9uUmVzdWx0VGltZW91dEluTWlsbGlzZWNvbmRzLFxuICAgICAgICAgICAgc2V0dGxlVGltZW91dEluTWlsbGlzZWNvbmRzXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKHJlc3VsdC5zdGF0dXMgIT09ICdvaycpIHtcbiAgICAgICAgICAgIGZhaWx1cmVzLnB1c2gocmVzdWx0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBleHBlY3QoZmFpbHVyZXMsIGZvcm1hdEZhaWx1cmVzKG5vdGUubmFtZSwgZmFpbHVyZXMpKS50b0VxdWFsKFtdKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qIHY4IGlnbm9yZSBzdG9wICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBEcml2ZXMgYSByZWFsIE9ic2lkaWFuIG92ZXIgQ0RQOiB0aGUgY2xvc3VyZSBpcyBzZXJpYWxpemVkIGhlcmUgYW5kIGV4ZWN1dGVkIGluIGFub3RoZXIgcHJvY2Vzcywgc28gaXQgY2Fubm90IGJlIGV4ZXJjaXNlZCBieSBhIHVuaXQgdGVzdC4gKi9cbi8qKlxuICogVHVybnMgcmVzdHJpY3RlZCBtb2RlIG9mZiBhbmQgZW5hYmxlcyBDb2RlU2NyaXB0IFRvb2xraXQsIHdob3NlIGJpbmFyeSB0aGUgZ2xvYmFsIHNldHVwIHNlZWRlZC5cbiAqXG4gKiBAcGFyYW0gdGltZW91dEluTWlsbGlzZWNvbmRzIC0gSG93IGxvbmcgdG8gd2FpdCBmb3IgaXQgdG8gbG9hZC5cbiAqIEByZXR1cm5zIFdoZXRoZXIgQ29kZVNjcmlwdCBUb29sa2l0IGlzIGxvYWRlZC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY2hlY2tDb2RlU2NyaXB0VG9vbGtpdExvYWRlZCh0aW1lb3V0SW5NaWxsaXNlY29uZHM6IG51bWJlcik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICByZXR1cm4gZXZhbEluT2JzaWRpYW4oe1xuICAgIGFzeW5jIGNhbGxiYWNrKHsgYXBwLCBjb2RlU2NyaXB0VG9vbGtpdFBsdWdpbklkLCBpbnRlcnZhbEluTWlsbGlzZWNvbmRzLCBsaWI6IHsgd2FpdFVudGlsIH0sIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogdGltZW91dCB9KTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgICBpZiAodHlwZW9mIGFwcC5wbHVnaW5zLmlzRW5hYmxlZCA9PT0gJ2Z1bmN0aW9uJyAmJiAhYXBwLnBsdWdpbnMuaXNFbmFibGVkKCkpIHtcbiAgICAgICAgYXdhaXQgYXBwLnBsdWdpbnMuc2V0RW5hYmxlKHRydWUpO1xuICAgICAgfVxuICAgICAgaWYgKCFhcHAucGx1Z2lucy5nZXRQbHVnaW4oY29kZVNjcmlwdFRvb2xraXRQbHVnaW5JZCkpIHtcbiAgICAgICAgYXdhaXQgYXBwLnBsdWdpbnMuZW5hYmxlUGx1Z2luKGNvZGVTY3JpcHRUb29sa2l0UGx1Z2luSWQpO1xuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgd2FpdFVudGlsKHtcbiAgICAgICAgICBpbnRlcnZhbEluTWlsbGlzZWNvbmRzLFxuICAgICAgICAgIG1lc3NhZ2U6ICdDb2RlU2NyaXB0IFRvb2xraXQgbmV2ZXIgbG9hZGVkJyxcbiAgICAgICAgICBwcmVkaWNhdGU6ICgpOiBib29sZWFuID0+IGFwcC5wbHVnaW5zLmdldFBsdWdpbihjb2RlU2NyaXB0VG9vbGtpdFBsdWdpbklkKSAhPT0gbnVsbCxcbiAgICAgICAgICB0aW1lb3V0SW5NaWxsaXNlY29uZHM6IHRpbWVvdXRcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSxcbiAgICBpbnB1dDoge1xuICAgICAgY29kZVNjcmlwdFRvb2xraXRQbHVnaW5JZDogQ09ERV9TQ1JJUFRfVE9PTEtJVF9QTFVHSU5fSUQsXG4gICAgICBpbnRlcnZhbEluTWlsbGlzZWNvbmRzOiBQT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyxcbiAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kc1xuICAgIH0sXG4gICAgdmF1bHRQYXRoOiBnZXRUZW1wb3JhcnlWYXVsdCgpLnBhdGhcbiAgfSk7XG59XG5cbi8qIHY4IGlnbm9yZSBzdG9wICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBEcml2ZXMgYSByZWFsIE9ic2lkaWFuIG92ZXIgQ0RQOiB0aGUgY2xvc3VyZSBpcyBzZXJpYWxpemVkIGhlcmUgYW5kIGV4ZWN1dGVkIGluIGFub3RoZXIgcHJvY2Vzcywgc28gaXQgY2Fubm90IGJlIGV4ZXJjaXNlZCBieSBhIHVuaXQgdGVzdC4gKi9cbi8qKlxuICogQ2xpY2tzIG9uZSBidXR0b24gYW5kIHJlYWRzIHRoZSByZXN1bHQgbGluZSBDb2RlU2NyaXB0IFRvb2xraXQgd3JpdGVzIHVuZGVyIGl0LlxuICpcbiAqIEEgYnV0dG9uIHRoYXQgcmUtcmVuZGVycyBpdHMgb3duIG5vdGUgKGEgc2V0dGluZ3MgY2hhbmdlLCBhIHJlbG9hZCkgZGV0YWNoZXMgdGhlIGJsb2NrIGl0IGxpdmVzIGluLFxuICogc28gdGhlIGJsb2NrIGVsZW1lbnQgaXMgY2FwdHVyZWQgQkVGT1JFIHRoZSBjbGljayBhbmQgcmVhZCBhZnRlcndhcmRzIFx1MjAxNCBDb2RlU2NyaXB0IFRvb2xraXQgd3JpdGVzIHRoZVxuICogcmVzdWx0IGludG8gdGhhdCBzYW1lIGVsZW1lbnQgZXZlbiBvbmNlIGl0IGlzIGRldGFjaGVkLlxuICpcbiAqIEBwYXJhbSBub3RlUGF0aCAtIFRoZSBub3RlIGhvbGRpbmcgdGhlIGJ1dHRvbi5cbiAqIEBwYXJhbSBidXR0b25DYXB0aW9uIC0gVGhlIGJ1dHRvbidzIHJlbmRlcmVkIGNhcHRpb24sIHdoaWNoIGlzIGhvdyBpdCBpcyBhZGRyZXNzZWQuXG4gKiBAcGFyYW0gdGltZW91dHMgLSBUaGUgcmVuZGVyIGFuZCByZXN1bHQgYnVkZ2V0cy5cbiAqIEByZXR1cm5zIFRoZSB7QGxpbmsgRGVtb1ZhdWx0QnV0dG9uUmVzdWx0fS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY2xpY2tCdXR0b24oXG4gIG5vdGVQYXRoOiBzdHJpbmcsXG4gIGJ1dHRvbkNhcHRpb246IHN0cmluZyxcbiAgdGltZW91dHM6IENsaWNrQnV0dG9uVGltZW91dHNcbik6IFByb21pc2U8RGVtb1ZhdWx0QnV0dG9uUmVzdWx0PiB7XG4gIHJldHVybiBldmFsSW5PYnNpZGlhbih7XG4gICAgYXN5bmMgY2FsbGJhY2soe1xuICAgICAgYXBwLFxuICAgICAgYnV0dG9uUmVzdWx0VGltZW91dEluTWlsbGlzZWNvbmRzLFxuICAgICAgY2FwdGlvbixcbiAgICAgIGludGVydmFsSW5NaWxsaXNlY29uZHMsXG4gICAgICBsaWI6IHsgd2FpdFVudGlsIH0sXG4gICAgICBvYnNpZGlhbk1vZHVsZSxcbiAgICAgIG91dHB1dEV4Y2VycHRMZW5ndGgsXG4gICAgICBzZXR0bGVUaW1lb3V0SW5NaWxsaXNlY29uZHNcbiAgICB9KTogUHJvbWlzZTxEZW1vVmF1bHRCdXR0b25SZXN1bHQ+IHtcbiAgICAgIGZ1bmN0aW9uIHZpZXcoKTogSW5zdGFuY2VUeXBlPHR5cGVvZiBvYnNpZGlhbk1vZHVsZS5NYXJrZG93blZpZXc+IHwgbnVsbCB7XG4gICAgICAgIHJldHVybiBhcHAud29ya3NwYWNlLmdldEFjdGl2ZVZpZXdPZlR5cGUob2JzaWRpYW5Nb2R1bGUuTWFya2Rvd25WaWV3KTtcbiAgICAgIH1cbiAgICAgIGZ1bmN0aW9uIHByZXZpZXdFbCgpOiBIVE1MRWxlbWVudCB8IG51bGwge1xuICAgICAgICByZXR1cm4gdmlldygpPy5jb250YWluZXJFbC5xdWVyeVNlbGVjdG9yPEhUTUxFbGVtZW50PignOnNjb3BlIC5tYXJrZG93bi1wcmV2aWV3LXZpZXcnKSA/PyBudWxsO1xuICAgICAgfVxuICAgICAgZnVuY3Rpb24gYnV0dG9ucygpOiBIVE1MQnV0dG9uRWxlbWVudFtdIHtcbiAgICAgICAgcmV0dXJuIFsuLi52aWV3KCk/LmNvbnRhaW5lckVsLnF1ZXJ5U2VsZWN0b3JBbGw8SFRNTEJ1dHRvbkVsZW1lbnQ+KCc6c2NvcGUgLmJsb2NrLWxhbmd1YWdlLWNvZGUtYnV0dG9uIGJ1dHRvbi5tb2QtY3RhJykgPz8gW11dO1xuICAgICAgfVxuXG4gICAgICBmdW5jdGlvbiBmaW5kQnV0dG9uKCk6IEhUTUxCdXR0b25FbGVtZW50IHwgdW5kZWZpbmVkIHtcbiAgICAgICAgcmV0dXJuIGJ1dHRvbnMoKS5maW5kKChjYW5kaWRhdGUpID0+IGNhbmRpZGF0ZS50ZXh0Q29udGVudCA9PT0gY2FwdGlvbik7XG4gICAgICB9XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHdhaXRVbnRpbCh7XG4gICAgICAgICAgaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyxcbiAgICAgICAgICBtZXNzYWdlOiBgY29kZSBidXR0b24gXCIke2NhcHRpb259XCIgbmV2ZXIgcmVuZGVyZWRgLFxuICAgICAgICAgIHByZWRpY2F0ZTogKCk6IGJvb2xlYW4gPT4ge1xuICAgICAgICAgICAgY29uc3Qgc2Nyb2xsZXIgPSBwcmV2aWV3RWwoKTtcbiAgICAgICAgICAgIGlmIChzY3JvbGxlcikge1xuICAgICAgICAgICAgICBzY3JvbGxlci5zY3JvbGxUb3AgPSBzY3JvbGxlci5zY3JvbGxIZWlnaHQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gZmluZEJ1dHRvbigpICE9PSB1bmRlZmluZWQ7XG4gICAgICAgICAgfSxcbiAgICAgICAgICB0aW1lb3V0SW5NaWxsaXNlY29uZHM6IHNldHRsZVRpbWVvdXRJbk1pbGxpc2Vjb25kc1xuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4geyBjYXB0aW9uLCBvdXRwdXQ6ICcnLCBzdGF0dXM6ICd0aW1lb3V0JyB9O1xuICAgICAgfVxuXG4gICAgICBjb25zdCBidXR0b24gPSBmaW5kQnV0dG9uKCk7XG4gICAgICBpZiAoIWJ1dHRvbikge1xuICAgICAgICByZXR1cm4geyBjYXB0aW9uLCBvdXRwdXQ6ICcnLCBzdGF0dXM6ICd0aW1lb3V0JyB9O1xuICAgICAgfVxuXG4gICAgICBjb25zdCBibG9jayA9IGJ1dHRvbi5jbG9zZXN0PEhUTUxFbGVtZW50PignLmJsb2NrLWxhbmd1YWdlLWNvZGUtYnV0dG9uJykgPz8gYnV0dG9uLnBhcmVudEVsZW1lbnQ7XG4gICAgICBidXR0b24uc2Nyb2xsSW50b1ZpZXcoKTtcbiAgICAgIGJ1dHRvbi5jbGljaygpO1xuXG4gICAgICBsZXQgc3RhdHVzOiBEZW1vVmF1bHRCdXR0b25SZXN1bHRbJ3N0YXR1cyddO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgd2FpdFVudGlsKHtcbiAgICAgICAgICBpbnRlcnZhbEluTWlsbGlzZWNvbmRzLFxuICAgICAgICAgIG1lc3NhZ2U6IGBidXR0b24gXCIke2NhcHRpb259XCIgbmV2ZXIgcmVwb3J0ZWQgYSByZXN1bHRgLFxuICAgICAgICAgIHByZWRpY2F0ZTogKCk6IGJvb2xlYW4gPT4gL0V4ZWN1dGVkICg/OnN1Y2Nlc3NmdWxseXx3aXRoIGVycm9yKS8udGVzdChibG9jaz8udGV4dENvbnRlbnQgPz8gJycpLFxuICAgICAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogYnV0dG9uUmVzdWx0VGltZW91dEluTWlsbGlzZWNvbmRzXG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCB0ZXh0ID0gYmxvY2s/LnRleHRDb250ZW50ID8/ICcnO1xuICAgICAgICBpZiAodGV4dC5pbmNsdWRlcygnRXhlY3V0ZWQgd2l0aCBlcnJvcicpKSB7XG4gICAgICAgICAgc3RhdHVzID0gJ2Vycm9yJztcbiAgICAgICAgfSBlbHNlIGlmICh0ZXh0LmluY2x1ZGVzKCdFeGVjdXRlZCBzdWNjZXNzZnVsbHknKSkge1xuICAgICAgICAgIHN0YXR1cyA9ICdvayc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RhdHVzID0gJ3Vua25vd24nO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgc3RhdHVzID0gJ3RpbWVvdXQnO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4geyBjYXB0aW9uLCBvdXRwdXQ6IChibG9jaz8udGV4dENvbnRlbnQgPz8gJycpLnNsaWNlKDAsIG91dHB1dEV4Y2VycHRMZW5ndGgpLCBzdGF0dXMgfTtcbiAgICB9LFxuICAgIGlucHV0OiB7XG4gICAgICBidXR0b25SZXN1bHRUaW1lb3V0SW5NaWxsaXNlY29uZHM6IHRpbWVvdXRzLmJ1dHRvblJlc3VsdFRpbWVvdXRJbk1pbGxpc2Vjb25kcyxcbiAgICAgIGNhcHRpb246IGJ1dHRvbkNhcHRpb24sXG4gICAgICBpbnRlcnZhbEluTWlsbGlzZWNvbmRzOiBQT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyxcbiAgICAgIG5vdGVQYXRoLFxuICAgICAgb3V0cHV0RXhjZXJwdExlbmd0aDogT1VUUFVUX0VYQ0VSUFRfTEVOR1RILFxuICAgICAgc2V0dGxlVGltZW91dEluTWlsbGlzZWNvbmRzOiB0aW1lb3V0cy5zZXR0bGVUaW1lb3V0SW5NaWxsaXNlY29uZHNcbiAgICB9LFxuICAgIHZhdWx0UGF0aDogZ2V0VGVtcG9yYXJ5VmF1bHQoKS5wYXRoXG4gIH0pO1xufVxuXG4vKipcbiAqIE9wZW5zIGEgbm90ZSBpbiByZWFkaW5nIHZpZXcgYW5kIGxpc3RzIHRoZSBjYXB0aW9ucyBvZiB0aGUgYnV0dG9ucyB0aGF0IG1vdW50ZWQsIGRlZHVwbGljYXRlZCBhbmQgaW5cbiAqIGRvY3VtZW50IG9yZGVyLlxuICpcbiAqIFR3byBxdWlya3Mgb2YgcmVhZGluZyB2aWV3IHNoYXBlIHRoaXMuIEl0IHJlbmRlcnMgbGF6aWx5IGFuZCB1bm1vdW50cyBzZWN0aW9ucyBmYXIgb2ZmLXNjcmVlbiwgc28gdGhlXG4gKiBwcmV2aWV3IGlzIHNjcm9sbGVkIHRvIHRoZSBib3R0b20gd2hpbGUgd2FpdGluZyBvciBhIG5vdGUncyBsYXN0IGJ1dHRvbnMgbmV2ZXIgbW91bnQgYXQgYWxsLiBBbmRcbiAqIHdoaWxlIGl0IHNldHRsZXMgaXQgY2FuIGhvbGQgU0VWRVJBTCBlbGVtZW50cyBwZXIgZmVuY2UgXHUyMDE0IHRoZSBzYW1lIGJ1dHRvbiB3YXMgb2JzZXJ2ZWQgcmVuZGVyZWQgdHdpY2VcbiAqIFx1MjAxNCBzbyB0aGUgY2FwdGlvbnMgYXJlIGRlZHVwbGljYXRlZDsgY2xpY2tpbmcgYnkgY2FwdGlvbiB0aGVuIGFkZHJlc3NlcyBlYWNoIGJ1dHRvbiBvbmNlIGhvd2V2ZXIgbWFueVxuICogY29waWVzIG9mIGl0IHRoZSBET00gaXMgaG9sZGluZy5cbiAqXG4gKiBAcGFyYW0gbm90ZSAtIFRoZSBub3RlIHRvIG9wZW4uXG4gKiBAcGFyYW0gc2V0dGxlVGltZW91dEluTWlsbGlzZWNvbmRzIC0gSG93IGxvbmcgdG8gd2FpdCBmb3IgdGhlIGJ1dHRvbnMgdG8gbW91bnQuXG4gKiBAcmV0dXJucyBUaGUgZGlzdGluY3QgYnV0dG9uIGNhcHRpb25zLlxuICovXG5hc3luYyBmdW5jdGlvbiBvcGVuTm90ZUFuZExpc3RCdXR0b25DYXB0aW9ucyhub3RlOiBEZW1vVmF1bHROb3RlLCBzZXR0bGVUaW1lb3V0SW5NaWxsaXNlY29uZHM6IG51bWJlcik6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgcmV0dXJuIGV2YWxJbk9ic2lkaWFuKHtcbiAgICBhc3luYyBjYWxsYmFjayh7IGFwcCwgZXhwZWN0ZWRCdXR0b25Db3VudCwgaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcywgbGliOiB7IHdhaXRVbnRpbCB9LCBub3RlUGF0aCwgb2JzaWRpYW5Nb2R1bGUsIHRpbWVvdXRJbk1pbGxpc2Vjb25kcyB9KTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgICAgZnVuY3Rpb24gdmlldygpOiBJbnN0YW5jZVR5cGU8dHlwZW9mIG9ic2lkaWFuTW9kdWxlLk1hcmtkb3duVmlldz4gfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIGFwcC53b3Jrc3BhY2UuZ2V0QWN0aXZlVmlld09mVHlwZShvYnNpZGlhbk1vZHVsZS5NYXJrZG93blZpZXcpO1xuICAgICAgfVxuICAgICAgZnVuY3Rpb24gcHJldmlld0VsKCk6IEhUTUxFbGVtZW50IHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB2aWV3KCk/LmNvbnRhaW5lckVsLnF1ZXJ5U2VsZWN0b3I8SFRNTEVsZW1lbnQ+KCc6c2NvcGUgLm1hcmtkb3duLXByZXZpZXctdmlldycpID8/IG51bGw7XG4gICAgICB9XG4gICAgICBmdW5jdGlvbiBjYXB0aW9ucygpOiBzdHJpbmdbXSB7XG4gICAgICAgIGNvbnN0IHJlbmRlcmVkID0gWy4uLnZpZXcoKT8uY29udGFpbmVyRWwucXVlcnlTZWxlY3RvckFsbDxIVE1MQnV0dG9uRWxlbWVudD4oJzpzY29wZSAuYmxvY2stbGFuZ3VhZ2UtY29kZS1idXR0b24gYnV0dG9uLm1vZC1jdGEnKSA/PyBbXV1cbiAgICAgICAgICAubWFwKChidXR0b24pID0+IGJ1dHRvbi50ZXh0Q29udGVudClcbiAgICAgICAgICAuZmlsdGVyKChjYXB0aW9uKSA9PiBjYXB0aW9uICE9PSAnJyk7XG4gICAgICAgIHJldHVybiBbLi4ubmV3IFNldChyZW5kZXJlZCldO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCBhcHAud29ya3NwYWNlLm9wZW5MaW5rVGV4dChub3RlUGF0aC5yZXBsYWNlKC9cXC5tZCQvLCAnJyksICcnLCBmYWxzZSk7XG4gICAgICBhd2FpdCBhcHAud29ya3NwYWNlLmdldExlYWYoZmFsc2UpLnNldFZpZXdTdGF0ZSh7IHN0YXRlOiB7IGZpbGU6IG5vdGVQYXRoLCBtb2RlOiAncHJldmlldycgfSwgdHlwZTogJ21hcmtkb3duJyB9KTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgd2FpdFVudGlsKHtcbiAgICAgICAgICBpbnRlcnZhbEluTWlsbGlzZWNvbmRzLFxuICAgICAgICAgIG1lc3NhZ2U6IGBcIiR7bm90ZVBhdGh9XCIgbmV2ZXIgbW91bnRlZCBhbGwgJHtTdHJpbmcoZXhwZWN0ZWRCdXR0b25Db3VudCl9IG9mIGl0cyBidXR0b25zYCxcbiAgICAgICAgICBwcmVkaWNhdGU6ICgpOiBib29sZWFuID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHNjcm9sbGVyID0gcHJldmlld0VsKCk7XG4gICAgICAgICAgICBpZiAoc2Nyb2xsZXIpIHtcbiAgICAgICAgICAgICAgc2Nyb2xsZXIuc2Nyb2xsVG9wID0gc2Nyb2xsZXIuc2Nyb2xsSGVpZ2h0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGNhcHRpb25zKCkubGVuZ3RoID49IGV4cGVjdGVkQnV0dG9uQ291bnQ7XG4gICAgICAgICAgfSxcbiAgICAgICAgICB0aW1lb3V0SW5NaWxsaXNlY29uZHNcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gVGhlIGNhcHRpb25zIGFyZSByZXR1cm5lZCBlaXRoZXIgd2F5OyB0aGUgY2FsbGVyIGFzc2VydHMgb24gdGhlbSBhbmQgcmVwb3J0cyB0aGUgc2hvcnRmYWxsLlxuICAgICAgfVxuXG4gICAgICByZXR1cm4gY2FwdGlvbnMoKTtcbiAgICB9LFxuICAgIGlucHV0OiB7XG4gICAgICBleHBlY3RlZEJ1dHRvbkNvdW50OiBub3RlLmJ1dHRvbkNvdW50LFxuICAgICAgaW50ZXJ2YWxJbk1pbGxpc2Vjb25kczogUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMsXG4gICAgICBub3RlUGF0aDogbm90ZS5uYW1lLFxuICAgICAgdGltZW91dEluTWlsbGlzZWNvbmRzOiBzZXR0bGVUaW1lb3V0SW5NaWxsaXNlY29uZHNcbiAgICB9LFxuICAgIHZhdWx0UGF0aDogZ2V0VGVtcG9yYXJ5VmF1bHQoKS5wYXRoXG4gIH0pO1xufVxuXG4vKiB2OCBpZ25vcmUgc3RvcCAqL1xuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpQkE7QUFBQSxFQUNFO0FBQUEsRUFDQTtBQUFBLE9BQ0s7QUFDUCxTQUFTLFlBQVk7QUFDckIsT0FBTyxhQUFhO0FBQ3BCLFNBQVMsc0JBQXNCO0FBQy9CLFNBQVMseUJBQXlCO0FBQ2xDO0FBQUEsRUFDRTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLE9BQ0s7QUFFUCxTQUFTLHFCQUFxQjtBQU12QixNQUFNLGdDQUFnQztBQXFGN0MsTUFBTSxnREFBZ0Q7QUFDdEQsTUFBTSx5Q0FBeUM7QUFDL0MsTUFBTSxnQ0FBZ0M7QUFDdEMsTUFBTSx3QkFBd0I7QUFJOUIsTUFBTSx5QkFBeUIsQ0FBQyxXQUFXO0FBRTNDLE1BQU0sNEJBQTRCO0FBZ0IzQixTQUFTLGVBQWUsVUFBa0IsVUFBb0Q7QUFDbkcsTUFBSSxTQUFTLFdBQVcsR0FBRztBQUN6QixXQUFPO0FBQUEsRUFDVDtBQUVBLFFBQU0sVUFBVSxTQUNiLElBQUksQ0FBQyxZQUFZLFFBQVEsUUFBUSxPQUFPLE1BQU0sUUFBUSxNQUFNLE1BQU0sUUFBUSxPQUFPLFdBQVcsUUFBUSxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFDakgsS0FBSyxJQUFJO0FBQ1osU0FBTyxHQUFHLE9BQU8sU0FBUyxNQUFNLENBQUMsaUJBQWlCLFFBQVE7QUFBQSxFQUEwQixPQUFPO0FBQzdGO0FBV08sU0FBUyxxQkFBcUIsZUFBdUIsZUFBcUQ7QUFDL0csU0FBTyxZQUFZLGVBQWUsRUFBRSxlQUFlLEtBQUssQ0FBQyxFQUN0RCxPQUFPLENBQUMsVUFBVSxNQUFNLE9BQU8sS0FBSyxNQUFNLEtBQUssU0FBUyxLQUFLLEtB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
@@ -21,6 +21,12 @@
21
21
  * broken for real readers. `authoring` only tunes them (which note is the start, which notes sit outside
22
22
  * the learning path); it cannot switch them off.
23
23
  *
24
+ * Only `rootFolder` is required, so a plugin with no settings and no public API to reflect registers the
25
+ * suite with that alone and still gets every authoring check — the checks have nothing to do with what the
26
+ * plugin exposes. Each reflection spec (`configInterfaces`, `interfaces`, `nonTrivialGuard`) adds the checks
27
+ * it describes when supplied. The vault is guarded against being empty either way: with no notes to read,
28
+ * every authoring check would pass by having nothing to look at.
29
+ *
24
30
  * The one exception is a note whose SUBJECT is the wikilink — an embed spelling the reader is being taught
25
31
  * to type, a frontmatter property value that exists to contrast with a Markdown link, a fixture that must
26
32
  * keep the wikilink a command is supposed to leave alone, a link the reader clicks while it still resolves
@@ -315,9 +321,10 @@ export interface RegisterDemoVaultCoverageSuiteParams {
315
321
  */
316
322
  readonly authoring?: DemoVaultAuthoringSpec;
317
323
  /**
318
- * The config interfaces whose options must each be demonstrated by their bare name.
324
+ * The config interfaces whose options must each be demonstrated by their bare name, or `undefined` when the
325
+ * plugin has no settings.
319
326
  */
320
- readonly configInterfaces: DemoVaultConfigInterfaceCoverageSpec[];
327
+ readonly configInterfaces?: DemoVaultConfigInterfaceCoverageSpec[];
321
328
  /**
322
329
  * The feature-doc linking check, or `undefined` when the plugin ships no `docs/` folder.
323
330
  */
@@ -328,13 +335,15 @@ export interface RegisterDemoVaultCoverageSuiteParams {
328
335
  */
329
336
  readonly functionModules?: DemoVaultFunctionsCoverageSpec[];
330
337
  /**
331
- * The interfaces whose members must each be demonstrated (and referenced without drift).
338
+ * The interfaces whose members must each be demonstrated (and referenced without drift), or `undefined` when
339
+ * the plugin exposes no public API surface to reflect.
332
340
  */
333
- readonly interfaces: DemoVaultInterfaceCoverageSpec[];
341
+ readonly interfaces?: DemoVaultInterfaceCoverageSpec[];
334
342
  /**
335
- * The guard that keeps the reflected surface non-trivial.
343
+ * The guard that keeps the reflected surface non-trivial, or `undefined` when there is no reflected surface
344
+ * to guard. The vault is checked for being non-empty either way.
336
345
  */
337
- readonly nonTrivialGuard: DemoVaultNonTrivialGuardSpec;
346
+ readonly nonTrivialGuard?: DemoVaultNonTrivialGuardSpec;
338
347
  /**
339
348
  * The absolute path of the plugin repo root. Callers typically pass `getRootFolder() ?? process.cwd()`.
340
349
  */