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.
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/dist/lib/cjs/context-id.cjs +83 -0
- package/dist/lib/cjs/context-id.d.cts +57 -0
- package/dist/lib/cjs/exec.cjs +267 -0
- package/dist/lib/cjs/exec.d.cts +108 -0
- package/dist/lib/cjs/function-expression.cjs +41 -0
- package/dist/lib/cjs/function-expression.d.cts +16 -0
- package/dist/lib/cjs/index.cjs +40 -0
- package/dist/lib/cjs/index.d.cts +11 -0
- package/dist/lib/cjs/integration-global-setup.cjs +106 -0
- package/dist/lib/cjs/integration-global-setup.d.cts +32 -0
- package/dist/lib/cjs/json-with-functions.cjs +46 -0
- package/dist/lib/cjs/json-with-functions.d.cts +16 -0
- package/dist/lib/cjs/obsidian-cli.cjs +191 -0
- package/dist/lib/cjs/obsidian-cli.d.cts +75 -0
- package/dist/lib/cjs/obsidian-config.cjs +91 -0
- package/dist/lib/cjs/obsidian-config.d.cts +25 -0
- package/dist/lib/cjs/temp-vault.cjs +96 -0
- package/dist/lib/cjs/temp-vault.d.cts +47 -0
- package/dist/lib/cjs/type-guards.cjs +42 -0
- package/dist/lib/cjs/type-guards.d.cts +30 -0
- package/dist/lib/cjs/vault-registry.cjs +89 -0
- package/dist/lib/cjs/vault-registry.d.cts +18 -0
- package/dist/lib/esm/context-id.d.mts +57 -0
- package/dist/lib/esm/context-id.mjs +59 -0
- package/dist/lib/esm/exec.d.mts +108 -0
- package/dist/lib/esm/exec.mjs +232 -0
- package/dist/lib/esm/function-expression.d.mts +16 -0
- package/dist/lib/esm/function-expression.mjs +17 -0
- package/dist/lib/esm/index.d.mts +11 -0
- package/dist/lib/esm/index.mjs +15 -0
- package/dist/lib/esm/integration-global-setup.d.mts +32 -0
- package/dist/lib/esm/integration-global-setup.mjs +88 -0
- package/dist/lib/esm/json-with-functions.d.mts +16 -0
- package/dist/lib/esm/json-with-functions.mjs +22 -0
- package/dist/lib/esm/obsidian-cli.d.mts +75 -0
- package/dist/lib/esm/obsidian-cli.mjs +161 -0
- package/dist/lib/esm/obsidian-config.d.mts +25 -0
- package/dist/lib/esm/obsidian-config.mjs +58 -0
- package/dist/lib/esm/temp-vault.d.mts +47 -0
- package/dist/lib/esm/temp-vault.mjs +82 -0
- package/dist/lib/esm/type-guards.d.mts +30 -0
- package/dist/lib/esm/type-guards.mjs +17 -0
- package/dist/lib/esm/vault-registry.d.mts +18 -0
- package/dist/lib/esm/vault-registry.mjs +64 -0
- package/dist/obsidian-integration-testing-1.0.0.tgz +0 -0
- package/package.json +131 -0
- package/patches/@vitest+runner+4.1.0.patch +22 -0
- 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
|
+
[](https://www.buymeacoffee.com/mnaoumov)
|
|
4
|
+
[](https://github.com/mnaoumov/obsidian-integration-testing/releases)
|
|
5
|
+
[](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
|
+
}
|