obsidian-integration-testing 1.0.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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -0
  3. package/dist/lib/cjs/context-id.cjs +83 -0
  4. package/dist/lib/cjs/context-id.d.cts +57 -0
  5. package/dist/lib/cjs/exec.cjs +267 -0
  6. package/dist/lib/cjs/exec.d.cts +108 -0
  7. package/dist/lib/cjs/function-expression.cjs +41 -0
  8. package/dist/lib/cjs/function-expression.d.cts +16 -0
  9. package/dist/lib/cjs/index.cjs +40 -0
  10. package/dist/lib/cjs/index.d.cts +11 -0
  11. package/dist/lib/cjs/integration-global-setup.cjs +106 -0
  12. package/dist/lib/cjs/integration-global-setup.d.cts +32 -0
  13. package/dist/lib/cjs/json-with-functions.cjs +46 -0
  14. package/dist/lib/cjs/json-with-functions.d.cts +16 -0
  15. package/dist/lib/cjs/obsidian-cli.cjs +191 -0
  16. package/dist/lib/cjs/obsidian-cli.d.cts +75 -0
  17. package/dist/lib/cjs/obsidian-config.cjs +91 -0
  18. package/dist/lib/cjs/obsidian-config.d.cts +25 -0
  19. package/dist/lib/cjs/temp-vault.cjs +96 -0
  20. package/dist/lib/cjs/temp-vault.d.cts +47 -0
  21. package/dist/lib/cjs/type-guards.cjs +42 -0
  22. package/dist/lib/cjs/type-guards.d.cts +30 -0
  23. package/dist/lib/cjs/vault-registry.cjs +89 -0
  24. package/dist/lib/cjs/vault-registry.d.cts +18 -0
  25. package/dist/lib/esm/context-id.d.mts +57 -0
  26. package/dist/lib/esm/context-id.mjs +59 -0
  27. package/dist/lib/esm/exec.d.mts +108 -0
  28. package/dist/lib/esm/exec.mjs +232 -0
  29. package/dist/lib/esm/function-expression.d.mts +16 -0
  30. package/dist/lib/esm/function-expression.mjs +17 -0
  31. package/dist/lib/esm/index.d.mts +11 -0
  32. package/dist/lib/esm/index.mjs +15 -0
  33. package/dist/lib/esm/integration-global-setup.d.mts +32 -0
  34. package/dist/lib/esm/integration-global-setup.mjs +88 -0
  35. package/dist/lib/esm/json-with-functions.d.mts +16 -0
  36. package/dist/lib/esm/json-with-functions.mjs +22 -0
  37. package/dist/lib/esm/obsidian-cli.d.mts +75 -0
  38. package/dist/lib/esm/obsidian-cli.mjs +161 -0
  39. package/dist/lib/esm/obsidian-config.d.mts +25 -0
  40. package/dist/lib/esm/obsidian-config.mjs +58 -0
  41. package/dist/lib/esm/temp-vault.d.mts +47 -0
  42. package/dist/lib/esm/temp-vault.mjs +82 -0
  43. package/dist/lib/esm/type-guards.d.mts +30 -0
  44. package/dist/lib/esm/type-guards.mjs +17 -0
  45. package/dist/lib/esm/vault-registry.d.mts +18 -0
  46. package/dist/lib/esm/vault-registry.mjs +64 -0
  47. package/dist/obsidian-integration-testing-1.0.0.tgz +0 -0
  48. package/package.json +131 -0
  49. package/patches/@vitest+runner+4.1.0.patch +22 -0
  50. package/scripts/postinstall.mjs +29 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Michael Naumov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,298 @@
1
+ # obsidian-integration-testing
2
+
3
+ [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/mnaoumov)
4
+ [![GitHub release](https://img.shields.io/github/v/release/mnaoumov/obsidian-integration-testing)](https://github.com/mnaoumov/obsidian-integration-testing/releases)
5
+ [![Coverage: 100%](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/mnaoumov/obsidian-integration-testing)
6
+
7
+ A set of helpers that simplify integration testing of [Obsidian](https://obsidian.md/) plugins against a running Obsidian instance via the [Obsidian CLI](https://obsidian.md/help/cli/).
8
+
9
+ ## Prerequisites
10
+
11
+ - [Obsidian](https://obsidian.md/) must be running with the CLI enabled.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install --save-dev obsidian-integration-testing
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Plugin Vitest setup
22
+
23
+ > **This entry point is designed for Obsidian plugin repos only.** It expects your built plugin in `dist/dev` or `dist/build` (whichever has a newer `main.js`), with a `manifest.json` at the root of the chosen folder. The setup creates a temporary vault, copies the build into it, and enables the plugin via the Obsidian CLI.
24
+
25
+ Add it as a Vitest global setup:
26
+
27
+ ```ts
28
+ // vitest.config.ts
29
+ export default defineConfig({
30
+ test: {
31
+ globalSetup: ['obsidian-integration-testing/obsidian-plugin-vitest-setup']
32
+ }
33
+ });
34
+ ```
35
+
36
+ ### Write integration tests
37
+
38
+ Use `evalInObsidian()` to run code inside the Obsidian process. The `vaultPath` is optional — it defaults to `process.cwd()`:
39
+
40
+ ```ts
41
+ import { evalInObsidian } from 'obsidian-integration-testing';
42
+
43
+ // Simple expression
44
+ const sum = await evalInObsidian({
45
+ args: { a: 2, b: 3 },
46
+ fn: ({ a, b }) => a + b
47
+ });
48
+ // sum === 5
49
+ ```
50
+
51
+ ### Access the Obsidian API
52
+
53
+ Every callback receives `app` (the Obsidian `App` instance) and `obsidianModule` (the full `obsidian` module):
54
+
55
+ ```ts
56
+ // Read the vault config directory
57
+ const configDir = await evalInObsidian({
58
+ fn: ({ app }) => app.vault.configDir
59
+ });
60
+
61
+ // Use the obsidian module
62
+ const yaml = await evalInObsidian({
63
+ fn: ({ obsidianModule }) => obsidianModule.stringifyYaml({ key: 'value' })
64
+ });
65
+
66
+ // Access internal APIs
67
+ const title = await evalInObsidian({
68
+ fn: ({ app }) => app.title
69
+ });
70
+ ```
71
+
72
+ ### Pass complex arguments
73
+
74
+ Arguments are JSON-serialized. You can even pass functions — they are serialized via `toString()`:
75
+
76
+ ```ts
77
+ const result = await evalInObsidian({
78
+ args: {
79
+ transform(x: number): number {
80
+ return x * 2;
81
+ },
82
+ value: 5
83
+ },
84
+ fn: ({ transform, value }) => transform(value)
85
+ });
86
+ // result === 10
87
+ ```
88
+
89
+ ### Persist non-serializable values across calls
90
+
91
+ Obsidian objects like `TFile` or `Editor` live in the Obsidian process and can't be returned to the test. Use `ContextId` to create a typed store that persists across calls:
92
+
93
+ ```ts
94
+ import type { TFile } from 'obsidian';
95
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
96
+ import { ContextId, evalInObsidian } from 'obsidian-integration-testing';
97
+
98
+ interface Context {
99
+ file: TFile;
100
+ }
101
+
102
+ const contextId = new ContextId<Context>();
103
+
104
+ beforeEach(async () => {
105
+ await evalInObsidian({
106
+ contextId,
107
+ fn: async ({ app, context }) => {
108
+ context.file = await app.vault.create('test.md', '# Hello');
109
+ }
110
+ });
111
+ });
112
+
113
+ afterEach(async () => {
114
+ await evalInObsidian({
115
+ contextId,
116
+ fn: async ({ app, context: { file } }) => {
117
+ await app.vault.delete(file);
118
+ }
119
+ });
120
+ await contextId.dispose();
121
+ });
122
+
123
+ it('should read the file path', async () => {
124
+ const path = await evalInObsidian({
125
+ contextId,
126
+ fn: ({ context: { file } }) => file.path
127
+ });
128
+ expect(path).toBe('test.md');
129
+ });
130
+ ```
131
+
132
+ ### Create a temporary vault
133
+
134
+ Use `TempVault` to create a disposable vault pre-populated with files:
135
+
136
+ ```ts
137
+ import type { TFile } from 'obsidian';
138
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
139
+ import { ContextId, evalInObsidian, TempVault } from 'obsidian-integration-testing';
140
+
141
+ interface Context {
142
+ file: TFile;
143
+ }
144
+
145
+ const vault = new TempVault({
146
+ files: {
147
+ 'note.md': '# Hello',
148
+ 'folder/nested.md': 'nested content',
149
+ }
150
+ });
151
+
152
+ const contextId = new ContextId<Context>();
153
+
154
+ beforeAll(async () => {
155
+ await vault.register();
156
+
157
+ // Resolve the pre-populated file into a TFile and store it in the context
158
+ await evalInObsidian({
159
+ contextId,
160
+ fn: async ({ app, context }) => {
161
+ const file = app.vault.getFileByPath('note.md');
162
+ if (!file) {
163
+ throw new Error('File not found');
164
+ }
165
+ context.file = file;
166
+ },
167
+ vaultPath: vault.path
168
+ });
169
+ });
170
+
171
+ afterAll(async () => {
172
+ await contextId.dispose(vault.path);
173
+ await vault.dispose();
174
+ });
175
+
176
+ it('should read a pre-populated file', async () => {
177
+ const content = await evalInObsidian({
178
+ fn: ({ app }) => app.vault.adapter.read('note.md'),
179
+ vaultPath: vault.path
180
+ });
181
+ expect(content).toBe('# Hello');
182
+ });
183
+
184
+ it('should access the TFile from context', async () => {
185
+ const path = await evalInObsidian({
186
+ contextId,
187
+ fn: ({ context: { file } }) => file.path,
188
+ vaultPath: vault.path
189
+ });
190
+ expect(path).toBe('note.md');
191
+ });
192
+ ```
193
+
194
+ Parent directories are created automatically. To create an empty folder, use a path ending with `/` and an empty string as content.
195
+
196
+ ### Test your plugin
197
+
198
+ Use `getTempVaultPath()` to get the temporary vault created by the global setup:
199
+
200
+ ```ts
201
+ import { describe, expect, it } from 'vitest';
202
+ import { evalInObsidian } from 'obsidian-integration-testing';
203
+ import { getTempVaultPath } from 'obsidian-integration-testing/obsidian-plugin-vitest-setup';
204
+
205
+ describe('my-plugin', () => {
206
+ const vaultPath = getTempVaultPath();
207
+
208
+ it('should be enabled', async () => {
209
+ const isEnabled = await evalInObsidian({
210
+ args: { pluginId: 'my-plugin' },
211
+ fn: ({ app, pluginId }) => app.plugins.enabledPlugins.has(pluginId),
212
+ vaultPath
213
+ });
214
+ expect(isEnabled).toBe(true);
215
+ });
216
+
217
+ it('should create a file', async () => {
218
+ await evalInObsidian({
219
+ fn: async ({ app }) => {
220
+ await app.vault.create('test.md', '# Hello');
221
+ },
222
+ vaultPath
223
+ });
224
+
225
+ const content = await evalInObsidian({
226
+ fn: ({ app }) => app.vault.adapter.read('test.md'),
227
+ vaultPath
228
+ });
229
+ expect(content).toBe('# Hello');
230
+ });
231
+ });
232
+ ```
233
+
234
+ > [!WARNING]
235
+ >
236
+ > **`evalInObsidian` limitations:**
237
+ >
238
+ > - The function is serialized via `toString()` and executed in a separate process. It must be **self-contained** — closures over local variables will not work.
239
+ > - Pass any needed values via `args`. Arguments must be **JSON-serializable** (strings, numbers, booleans, arrays, plain objects). Functions in `args` are supported — they are serialized via `toString()` with the same self-contained constraint.
240
+ > - The **return value** must also be JSON-serializable. You cannot return functions, class instances, `Map`, `Set`, DOM elements, or other non-serializable values.
241
+ > - Imports (`import`/`require`) are not available inside the function. Use `obsidianModule` to access the `obsidian` API, and `app` to access the Obsidian `App` instance.
242
+
243
+ ### Accessing internal APIs
244
+
245
+ Since `evalInObsidian` runs inside a real Obsidian process, you have access to internal (undocumented) APIs like `app.plugins`, `app.commands`, `app.title`, etc. However, these are not declared in `obsidian.d.ts`, so TypeScript won't compile references to them. Here are the options to make it work, from best to worst:
246
+
247
+ **1. Use `obsidian-typings`** (recommended) — install [`obsidian-typings`](https://www.npmjs.com/package/obsidian-typings) which declares the full internal API. Everything compiles with no extra work:
248
+
249
+ ```ts
250
+ // With obsidian-typings installed — no casts needed
251
+ const title = await evalInObsidian({
252
+ fn: ({ app }) => app.title
253
+ });
254
+ ```
255
+
256
+ **2. Manual module augmentation** — declare only what you need:
257
+
258
+ ```ts
259
+ declare module 'obsidian' {
260
+ interface App {
261
+ title: string;
262
+ }
263
+ }
264
+
265
+ const title = await evalInObsidian({
266
+ fn: ({ app }) => app.title
267
+ });
268
+ ```
269
+
270
+ **3. `as any` / `@ts-expect-error` / `@ts-ignore`** (not recommended) — suppresses all type checking and hides real errors:
271
+
272
+ ```ts
273
+ const title = await evalInObsidian({
274
+ // @ts-expect-error -- accessing internal API
275
+ fn: ({ app }) => app.title
276
+ });
277
+
278
+ // or
279
+ const title2 = await evalInObsidian({
280
+ fn: ({ app }) => (app as any).title
281
+ });
282
+ ```
283
+
284
+ ## Support
285
+
286
+ <!-- markdownlint-disable MD033 -->
287
+
288
+ <a href="https://www.buymeacoffee.com/mnaoumov" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60" width="217"></a>
289
+
290
+ <!-- markdownlint-enable MD033 -->
291
+
292
+ ## My other Obsidian resources
293
+
294
+ [See my other Obsidian resources](https://github.com/mnaoumov/obsidian-resources).
295
+
296
+ ## License
297
+
298
+ © [Michael Naumov](https://github.com/mnaoumov/)
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var context_id_exports = {};
20
+ __export(context_id_exports, {
21
+ ContextId: () => ContextId
22
+ });
23
+ module.exports = __toCommonJS(context_id_exports);
24
+ var import_obsidian_cli = require("./obsidian-cli.cjs");
25
+ class ContextId {
26
+ id;
27
+ /**
28
+ * Creates a new context id.
29
+ */
30
+ constructor() {
31
+ const SLICE_START = 2;
32
+ this.id = `__ctx_${String(Math.random()).slice(SLICE_START)}`;
33
+ }
34
+ /**
35
+ * Removes this context from the Obsidian process.
36
+ *
37
+ * @param vaultPath - The path to the Obsidian vault. Defaults to `process.cwd()`.
38
+ */
39
+ async dispose(vaultPath) {
40
+ await (0, import_obsidian_cli.evalInObsidian)({
41
+ args: { id: this.id },
42
+ /* v8 ignore start -- Serialized via toString() and executed inside the Obsidian process. Covered by integration tests. */
43
+ fn({ id }) {
44
+ const holder = window;
45
+ if (holder.__obsidianContexts__) {
46
+ delete holder.__obsidianContexts__[id];
47
+ }
48
+ },
49
+ /* v8 ignore stop */
50
+ shouldSkipPreflightChecks: true,
51
+ ...vaultPath === void 0 ? {} : { vaultPath }
52
+ });
53
+ }
54
+ /**
55
+ * Disposes this context id.
56
+ *
57
+ * @returns A promise that resolves when the context id is disposed.
58
+ */
59
+ async [Symbol.asyncDispose]() {
60
+ await this.dispose();
61
+ }
62
+ /**
63
+ * Returns the JSON representation of the context id.
64
+ *
65
+ * @returns The JSON representation of the context id.
66
+ */
67
+ toJSON() {
68
+ return this.id;
69
+ }
70
+ /**
71
+ * Returns the string representation of the context id.
72
+ *
73
+ * @returns The string representation of the context id.
74
+ */
75
+ toString() {
76
+ return this.id;
77
+ }
78
+ }
79
+ // Annotate the CommonJS export names for ESM import in node:
80
+ 0 && (module.exports = {
81
+ ContextId
82
+ });
83
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2NvbnRleHQtaWQudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQHBhY2thZ2VEb2N1bWVudGF0aW9uXG4gKlxuICogVHlwZWQgY29udGV4dCBpZGVudGlmaWVycyBmb3IgcGVyc2lzdGluZyBub24tc2VyaWFsaXphYmxlIHZhbHVlc1xuICogYWNyb3NzIHtAbGluayBldmFsSW5PYnNpZGlhbn0gY2FsbHMuXG4gKi9cblxuaW1wb3J0IHsgZXZhbEluT2JzaWRpYW4gfSBmcm9tICcuL29ic2lkaWFuLWNsaS5janMnO1xuXG4vKipcbiAqIFdyYXBzIGEge0BsaW5rIENvbnRleHRPZn0gZXh0cmFjdGlvbiBpbnRvIGFuIG9iamVjdCB3aXRoIGEgYGNvbnRleHRgIHByb3BlcnR5LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbnRleHRBcmdzPFRDb250ZXh0SWQgZXh0ZW5kcyBDb250ZXh0SWQ8dW5rbm93bj4gfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ+IHtcbiAgY29udGV4dDogQ29udGV4dE9mPFRDb250ZXh0SWQ+O1xufVxuXG4vKipcbiAqIEV4dHJhY3RzIHRoZSBjb250ZXh0IHR5cGUgZnJvbSBhIHtAbGluayBDb250ZXh0SWR9LlxuICovXG5leHBvcnQgdHlwZSBDb250ZXh0T2Y8VD4gPSBUIGV4dGVuZHMgQ29udGV4dElkPGluZmVyIENvbnRleHQ+ID8gQ29udGV4dCA6IG5ldmVyO1xuXG4vKipcbiAqIEEgdHlwZWQgY29udGV4dCBpZGVudGlmaWVyIGZvciBwZXJzaXN0aW5nIG5vbi1zZXJpYWxpemFibGUgdmFsdWVzXG4gKiBhY3Jvc3Mge0BsaW5rIGV2YWxJbk9ic2lkaWFufSBjYWxscy5cbiAqXG4gKiBUaGUgY29udGV4dCBpcyBhIHBlcnNpc3RlbnQgb2JqZWN0IHN0b3JlZCBvbiBgd2luZG93YCBpbiB0aGUgT2JzaWRpYW4gcHJvY2Vzcy5cbiAqIEFsbCBjYWxscyBzaGFyaW5nIHRoZSBzYW1lIGBDb250ZXh0SWRgIHNoYXJlIHRoZSBzYW1lIGNvbnRleHQgb2JqZWN0LFxuICogYWxsb3dpbmcgbm9uLXNlcmlhbGl6YWJsZSB2YWx1ZXMgKGUuZy4gYFRGaWxlYCwgYEVkaXRvcmApIHRvIHN1cnZpdmUgYWNyb3NzIGNhbGxzLlxuICpcbiAqIEltcGxlbWVudHMgYEFzeW5jRGlzcG9zYWJsZWAgZm9yIHVzZSB3aXRoIGBhd2FpdCB1c2luZ2AuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb250ZXh0SWQ8X0NvbnRleHQ+IGltcGxlbWVudHMgQXN5bmNEaXNwb3NhYmxlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBpZDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmV3IGNvbnRleHQgaWQuXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3IoKSB7XG4gICAgY29uc3QgU0xJQ0VfU1RBUlQgPSAyO1xuICAgIHRoaXMuaWQgPSBgX19jdHhfJHtTdHJpbmcoTWF0aC5yYW5kb20oKSkuc2xpY2UoU0xJQ0VfU1RBUlQpfWA7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyB0aGlzIGNvbnRleHQgZnJvbSB0aGUgT2JzaWRpYW4gcHJvY2Vzcy5cbiAgICpcbiAgICogQHBhcmFtIHZhdWx0UGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBPYnNpZGlhbiB2YXVsdC4gRGVmYXVsdHMgdG8gYHByb2Nlc3MuY3dkKClgLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGRpc3Bvc2UodmF1bHRQYXRoPzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgZXZhbEluT2JzaWRpYW4oe1xuICAgICAgYXJnczogeyBpZDogdGhpcy5pZCB9LFxuICAgICAgLyogdjggaWdub3JlIHN0YXJ0IC0tIFNlcmlhbGl6ZWQgdmlhIHRvU3RyaW5nKCkgYW5kIGV4ZWN1dGVkIGluc2lkZSB0aGUgT2JzaWRpYW4gcHJvY2Vzcy4gQ292ZXJlZCBieSBpbnRlZ3JhdGlvbiB0ZXN0cy4gKi9cbiAgICAgIGZuKHsgaWQgfSk6IHZvaWQge1xuICAgICAgICBpbnRlcmZhY2UgQ29udGV4dEhvbGRlciB7XG4gICAgICAgICAgX19vYnNpZGlhbkNvbnRleHRzX186IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGhvbGRlciA9IHdpbmRvdyBhcyBQYXJ0aWFsPENvbnRleHRIb2xkZXI+O1xuICAgICAgICBpZiAoaG9sZGVyLl9fb2JzaWRpYW5Db250ZXh0c19fKSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1keW5hbWljLWRlbGV0ZSAtLSBObyBvdGhlciB3YXkuXG4gICAgICAgICAgZGVsZXRlIGhvbGRlci5fX29ic2lkaWFuQ29udGV4dHNfX1tpZF07XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICAvKiB2OCBpZ25vcmUgc3RvcCAqL1xuICAgICAgc2hvdWxkU2tpcFByZWZsaWdodENoZWNrczogdHJ1ZSxcbiAgICAgIC4uLih2YXVsdFBhdGggPT09IHVuZGVmaW5lZCA/IHt9IDogeyB2YXVsdFBhdGggfSlcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNwb3NlcyB0aGlzIGNvbnRleHQgaWQuXG4gICAqXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGNvbnRleHQgaWQgaXMgZGlzcG9zZWQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgW1N5bWJvbC5hc3luY0Rpc3Bvc2VdKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuZGlzcG9zZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIEpTT04gcmVwcmVzZW50YXRpb24gb2YgdGhlIGNvbnRleHQgaWQuXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBKU09OIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjb250ZXh0IGlkLlxuICAgKi9cbiAgcHVibGljIHRvSlNPTigpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmlkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgY29udGV4dCBpZC5cbiAgICpcbiAgICogQHJldHVybnMgVGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgY29udGV4dCBpZC5cbiAgICovXG4gIHB1YmxpYyB0b1N0cmluZygpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmlkO1xuICB9XG59XG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBT0EsMEJBQStCO0FBd0J4QixNQUFNLFVBQStDO0FBQUEsRUFDekM7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQUtWLGNBQWM7QUFDbkIsVUFBTSxjQUFjO0FBQ3BCLFNBQUssS0FBSyxTQUFTLE9BQU8sS0FBSyxPQUFPLENBQUMsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUFBLEVBQzdEO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsTUFBYSxRQUFRLFdBQW1DO0FBQ3RELGNBQU0sb0NBQWU7QUFBQSxNQUNuQixNQUFNLEVBQUUsSUFBSSxLQUFLLEdBQUc7QUFBQTtBQUFBLE1BRXBCLEdBQUcsRUFBRSxHQUFHLEdBQVM7QUFJZixjQUFNLFNBQVM7QUFDZixZQUFJLE9BQU8sc0JBQXNCO0FBRS9CLGlCQUFPLE9BQU8scUJBQXFCLEVBQUU7QUFBQSxRQUN2QztBQUFBLE1BQ0Y7QUFBQTtBQUFBLE1BRUEsMkJBQTJCO0FBQUEsTUFDM0IsR0FBSSxjQUFjLFNBQVksQ0FBQyxJQUFJLEVBQUUsVUFBVTtBQUFBLElBQ2pELENBQUM7QUFBQSxFQUNIO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsT0FBYyxPQUFPLFlBQVksSUFBbUI7QUFDbEQsVUFBTSxLQUFLLFFBQVE7QUFBQSxFQUNyQjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9PLFNBQWlCO0FBQ3RCLFdBQU8sS0FBSztBQUFBLEVBQ2Q7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFPTyxXQUFtQjtBQUN4QixXQUFPLEtBQUs7QUFBQSxFQUNkO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Typed context identifiers for persisting non-serializable values
5
+ * across {@link evalInObsidian} calls.
6
+ */
7
+ /**
8
+ * Wraps a {@link ContextOf} extraction into an object with a `context` property.
9
+ */
10
+ export interface ContextArgs<TContextId extends ContextId<unknown> | undefined = undefined> {
11
+ context: ContextOf<TContextId>;
12
+ }
13
+ /**
14
+ * Extracts the context type from a {@link ContextId}.
15
+ */
16
+ export type ContextOf<T> = T extends ContextId<infer Context> ? Context : never;
17
+ /**
18
+ * A typed context identifier for persisting non-serializable values
19
+ * across {@link evalInObsidian} calls.
20
+ *
21
+ * The context is a persistent object stored on `window` in the Obsidian process.
22
+ * All calls sharing the same `ContextId` share the same context object,
23
+ * allowing non-serializable values (e.g. `TFile`, `Editor`) to survive across calls.
24
+ *
25
+ * Implements `AsyncDisposable` for use with `await using`.
26
+ */
27
+ export declare class ContextId<_Context> implements AsyncDisposable {
28
+ private readonly id;
29
+ /**
30
+ * Creates a new context id.
31
+ */
32
+ constructor();
33
+ /**
34
+ * Removes this context from the Obsidian process.
35
+ *
36
+ * @param vaultPath - The path to the Obsidian vault. Defaults to `process.cwd()`.
37
+ */
38
+ dispose(vaultPath?: string): Promise<void>;
39
+ /**
40
+ * Disposes this context id.
41
+ *
42
+ * @returns A promise that resolves when the context id is disposed.
43
+ */
44
+ [Symbol.asyncDispose](): Promise<void>;
45
+ /**
46
+ * Returns the JSON representation of the context id.
47
+ *
48
+ * @returns The JSON representation of the context id.
49
+ */
50
+ toJSON(): string;
51
+ /**
52
+ * Returns the string representation of the context id.
53
+ *
54
+ * @returns The string representation of the context id.
55
+ */
56
+ toString(): string;
57
+ }