obsidian-e2e 0.0.0-next.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/README.md +432 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +2 -0
- package/dist/matchers.d.mts +26 -0
- package/dist/matchers.mjs +94 -0
- package/dist/matchers.mjs.map +1 -0
- package/dist/sandbox-BhesE1S4.mjs +565 -0
- package/dist/sandbox-BhesE1S4.mjs.map +1 -0
- package/dist/types-5UxOZM7r.d.mts +152 -0
- package/dist/vitest.d.mts +46 -0
- package/dist/vitest.mjs +182 -0
- package/dist/vitest.mjs.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
//#region src/core/types.d.ts
|
|
2
|
+
type ObsidianArg = boolean | number | string | null | undefined;
|
|
3
|
+
interface ExecOptions {
|
|
4
|
+
allowNonZeroExit?: boolean;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
interface ExecResult {
|
|
10
|
+
argv: string[];
|
|
11
|
+
command: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
stderr: string;
|
|
14
|
+
stdout: string;
|
|
15
|
+
}
|
|
16
|
+
interface ExecuteRequest extends ExecOptions {
|
|
17
|
+
argv: string[];
|
|
18
|
+
bin: string;
|
|
19
|
+
}
|
|
20
|
+
type CommandTransport = (request: ExecuteRequest) => Promise<ExecResult>;
|
|
21
|
+
interface WaitForOptions {
|
|
22
|
+
intervalMs?: number;
|
|
23
|
+
message?: string;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
interface CommandListOptions {
|
|
27
|
+
filter?: string;
|
|
28
|
+
}
|
|
29
|
+
interface OpenFileOptions {
|
|
30
|
+
file?: string;
|
|
31
|
+
newTab?: boolean;
|
|
32
|
+
path?: string;
|
|
33
|
+
}
|
|
34
|
+
interface OpenTabOptions {
|
|
35
|
+
file?: string;
|
|
36
|
+
group?: string;
|
|
37
|
+
view?: string;
|
|
38
|
+
}
|
|
39
|
+
interface PluginToggleOptions {
|
|
40
|
+
filter?: "community" | "core";
|
|
41
|
+
}
|
|
42
|
+
interface RestartAppOptions {
|
|
43
|
+
readyOptions?: WaitForOptions;
|
|
44
|
+
waitUntilReady?: boolean;
|
|
45
|
+
}
|
|
46
|
+
interface TabsOptions {
|
|
47
|
+
ids?: boolean;
|
|
48
|
+
}
|
|
49
|
+
interface WorkspaceOptions {
|
|
50
|
+
ids?: boolean;
|
|
51
|
+
}
|
|
52
|
+
type JsonFileUpdater<T> = (draft: T) => Promise<T | void> | T | void;
|
|
53
|
+
interface JsonFile<T = unknown> {
|
|
54
|
+
patch(updater: JsonFileUpdater<T>): Promise<T>;
|
|
55
|
+
read(): Promise<T>;
|
|
56
|
+
write(value: T): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
interface PluginHandle {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
data<T = unknown>(): JsonFile<T>;
|
|
61
|
+
dataPath(): Promise<string>;
|
|
62
|
+
disable(options?: PluginToggleOptions): Promise<void>;
|
|
63
|
+
enable(options?: PluginToggleOptions): Promise<void>;
|
|
64
|
+
isEnabled(): Promise<boolean>;
|
|
65
|
+
reload(): Promise<void>;
|
|
66
|
+
restoreData(): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
interface ObsidianAppHandle {
|
|
69
|
+
reload(options?: ExecOptions): Promise<void>;
|
|
70
|
+
restart(options?: RestartAppOptions & ExecOptions): Promise<void>;
|
|
71
|
+
version(options?: ExecOptions): Promise<string>;
|
|
72
|
+
waitUntilReady(options?: WaitForOptions): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
interface ObsidianCommandHandle {
|
|
75
|
+
readonly id: string;
|
|
76
|
+
exists(options?: CommandListOptions): Promise<boolean>;
|
|
77
|
+
run(options?: ExecOptions): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
interface ObsidianDevHandle {
|
|
80
|
+
dom(options: DevDomQueryOptions, execOptions?: ExecOptions): Promise<DevDomResult>;
|
|
81
|
+
eval<T = unknown>(code: string, options?: ExecOptions): Promise<T>;
|
|
82
|
+
screenshot(path: string, options?: ExecOptions): Promise<string>;
|
|
83
|
+
}
|
|
84
|
+
type DevDomResult = number | string | string[];
|
|
85
|
+
interface DevDomQueryOptions {
|
|
86
|
+
all?: boolean;
|
|
87
|
+
attr?: string;
|
|
88
|
+
css?: string;
|
|
89
|
+
inner?: boolean;
|
|
90
|
+
selector: string;
|
|
91
|
+
text?: boolean;
|
|
92
|
+
total?: boolean;
|
|
93
|
+
}
|
|
94
|
+
interface WorkspaceNode {
|
|
95
|
+
children: WorkspaceNode[];
|
|
96
|
+
id?: string;
|
|
97
|
+
label: string;
|
|
98
|
+
title?: string;
|
|
99
|
+
viewType?: string;
|
|
100
|
+
}
|
|
101
|
+
interface WorkspaceTab {
|
|
102
|
+
id?: string;
|
|
103
|
+
title: string;
|
|
104
|
+
viewType: string;
|
|
105
|
+
}
|
|
106
|
+
interface ObsidianClient {
|
|
107
|
+
readonly app: ObsidianAppHandle;
|
|
108
|
+
readonly bin: string;
|
|
109
|
+
readonly dev: ObsidianDevHandle;
|
|
110
|
+
readonly vaultName: string;
|
|
111
|
+
command(id: string): ObsidianCommandHandle;
|
|
112
|
+
commands(options?: CommandListOptions, execOptions?: ExecOptions): Promise<string[]>;
|
|
113
|
+
exec(command: string, args?: Record<string, ObsidianArg>, options?: ExecOptions): Promise<ExecResult>;
|
|
114
|
+
execJson<T = unknown>(command: string, args?: Record<string, ObsidianArg>, options?: ExecOptions): Promise<T>;
|
|
115
|
+
execText(command: string, args?: Record<string, ObsidianArg>, options?: ExecOptions): Promise<string>;
|
|
116
|
+
open(options: OpenFileOptions, execOptions?: ExecOptions): Promise<void>;
|
|
117
|
+
openTab(options?: OpenTabOptions, execOptions?: ExecOptions): Promise<void>;
|
|
118
|
+
plugin(id: string): PluginHandle;
|
|
119
|
+
tabs(options?: TabsOptions, execOptions?: ExecOptions): Promise<WorkspaceTab[]>;
|
|
120
|
+
vaultPath(): Promise<string>;
|
|
121
|
+
verify(): Promise<void>;
|
|
122
|
+
waitFor<T>(fn: () => Promise<T | false | null | undefined> | T | false | null | undefined, options?: WaitForOptions): Promise<T>;
|
|
123
|
+
workspace(options?: WorkspaceOptions, execOptions?: ExecOptions): Promise<WorkspaceNode[]>;
|
|
124
|
+
}
|
|
125
|
+
interface CreateObsidianClientOptions {
|
|
126
|
+
bin?: string;
|
|
127
|
+
intervalMs?: number;
|
|
128
|
+
timeoutMs?: number;
|
|
129
|
+
transport?: CommandTransport;
|
|
130
|
+
vault: string;
|
|
131
|
+
}
|
|
132
|
+
interface DeleteOptions {
|
|
133
|
+
permanent?: boolean;
|
|
134
|
+
}
|
|
135
|
+
interface VaultApi {
|
|
136
|
+
delete(path: string, options?: DeleteOptions): Promise<void>;
|
|
137
|
+
exists(path: string): Promise<boolean>;
|
|
138
|
+
json<T = unknown>(path: string): JsonFile<T>;
|
|
139
|
+
mkdir(path: string): Promise<void>;
|
|
140
|
+
read(path: string): Promise<string>;
|
|
141
|
+
waitForExists(path: string, options?: WaitForOptions): Promise<void>;
|
|
142
|
+
waitForMissing(path: string, options?: WaitForOptions): Promise<void>;
|
|
143
|
+
write(path: string, content: string): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
interface SandboxApi extends VaultApi {
|
|
146
|
+
readonly root: string;
|
|
147
|
+
cleanup(): Promise<void>;
|
|
148
|
+
path(...segments: string[]): string;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
export { WaitForOptions as C, WorkspaceTab as E, VaultApi as S, WorkspaceOptions as T, PluginHandle as _, DevDomResult as a, SandboxApi as b, JsonFile as c, ObsidianArg as d, ObsidianClient as f, OpenTabOptions as g, OpenFileOptions as h, DevDomQueryOptions as i, JsonFileUpdater as l, ObsidianDevHandle as m, CommandTransport as n, ExecOptions as o, ObsidianCommandHandle as p, CreateObsidianClientOptions as r, ExecResult as s, CommandListOptions as t, ObsidianAppHandle as u, PluginToggleOptions as v, WorkspaceNode as w, TabsOptions as x, RestartAppOptions as y };
|
|
152
|
+
//# sourceMappingURL=types-5UxOZM7r.d.mts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { S as VaultApi, _ as PluginHandle, b as SandboxApi, f as ObsidianClient, r as CreateObsidianClientOptions, v as PluginToggleOptions } from "./types-5UxOZM7r.mjs";
|
|
2
|
+
import { TestAPI } from "vite-plus/test";
|
|
3
|
+
|
|
4
|
+
//#region src/fixtures/types.d.ts
|
|
5
|
+
interface FailureArtifactOptions {
|
|
6
|
+
activeFile?: boolean;
|
|
7
|
+
dom?: boolean;
|
|
8
|
+
editorText?: boolean;
|
|
9
|
+
screenshot?: boolean;
|
|
10
|
+
tabs?: boolean;
|
|
11
|
+
workspace?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface CreateObsidianTestOptions extends CreateObsidianClientOptions {
|
|
14
|
+
artifactsDir?: string;
|
|
15
|
+
captureOnFailure?: boolean | FailureArtifactOptions;
|
|
16
|
+
sandboxRoot?: string;
|
|
17
|
+
}
|
|
18
|
+
type VaultSeedEntry = string | {
|
|
19
|
+
json: unknown;
|
|
20
|
+
};
|
|
21
|
+
type VaultSeed = Record<string, VaultSeedEntry>;
|
|
22
|
+
interface CreatePluginTestOptions extends CreateObsidianTestOptions {
|
|
23
|
+
pluginFilter?: PluginToggleOptions["filter"];
|
|
24
|
+
pluginId: string;
|
|
25
|
+
seedPluginData?: unknown;
|
|
26
|
+
seedVault?: VaultSeed;
|
|
27
|
+
}
|
|
28
|
+
interface ObsidianFixtures {
|
|
29
|
+
obsidian: ObsidianClient;
|
|
30
|
+
sandbox: SandboxApi;
|
|
31
|
+
vault: VaultApi;
|
|
32
|
+
}
|
|
33
|
+
interface PluginFixtures extends ObsidianFixtures {
|
|
34
|
+
plugin: PluginHandle;
|
|
35
|
+
}
|
|
36
|
+
type ObsidianTest = TestAPI<ObsidianFixtures>;
|
|
37
|
+
type PluginTest = TestAPI<PluginFixtures>;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/fixtures/create-obsidian-test.d.ts
|
|
40
|
+
declare function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/fixtures/create-plugin-test.d.ts
|
|
43
|
+
declare function createPluginTest(options: CreatePluginTestOptions): PluginTest;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { type CreateObsidianTestOptions, type CreatePluginTestOptions, type ObsidianFixtures, type ObsidianTest, type PluginFixtures, type PluginTest, type VaultSeed, type VaultSeedEntry, createObsidianTest, createPluginTest };
|
|
46
|
+
//# sourceMappingURL=vitest.d.mts.map
|
package/dist/vitest.mjs
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { i as getClientInternals, n as createVaultApi, r as createObsidianClient, t as createSandboxApi } from "./sandbox-BhesE1S4.mjs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { test } from "vite-plus/test";
|
|
5
|
+
//#region src/fixtures/failure-artifacts.ts
|
|
6
|
+
const DEFAULT_ARTIFACTS_DIR = ".obsidian-e2e-artifacts";
|
|
7
|
+
function getFailureArtifactConfig(options) {
|
|
8
|
+
if (!options.captureOnFailure) return {
|
|
9
|
+
artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),
|
|
10
|
+
capture: {
|
|
11
|
+
activeFile: true,
|
|
12
|
+
dom: true,
|
|
13
|
+
editorText: true,
|
|
14
|
+
screenshot: true,
|
|
15
|
+
tabs: true,
|
|
16
|
+
workspace: true
|
|
17
|
+
},
|
|
18
|
+
enabled: false
|
|
19
|
+
};
|
|
20
|
+
const overrides = options.captureOnFailure === true ? {} : options.captureOnFailure;
|
|
21
|
+
return {
|
|
22
|
+
artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),
|
|
23
|
+
capture: {
|
|
24
|
+
activeFile: overrides.activeFile ?? true,
|
|
25
|
+
dom: overrides.dom ?? true,
|
|
26
|
+
editorText: overrides.editorText ?? true,
|
|
27
|
+
screenshot: overrides.screenshot ?? true,
|
|
28
|
+
tabs: overrides.tabs ?? true,
|
|
29
|
+
workspace: overrides.workspace ?? true
|
|
30
|
+
},
|
|
31
|
+
enabled: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function getFailureArtifactDirectory(artifactsDir, task) {
|
|
35
|
+
const suffix = task.id.split("_").at(-1) ?? "test";
|
|
36
|
+
return path.join(artifactsDir, `${sanitizeForPath(task.name)}-${suffix}`);
|
|
37
|
+
}
|
|
38
|
+
function registerFailureArtifacts(context, obsidian, options) {
|
|
39
|
+
const config = getFailureArtifactConfig(options);
|
|
40
|
+
if (!config.enabled) return;
|
|
41
|
+
context.onTestFailed(async () => {
|
|
42
|
+
const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);
|
|
43
|
+
await mkdir(artifactDirectory, { recursive: true });
|
|
44
|
+
await Promise.all([
|
|
45
|
+
captureJsonArtifact(artifactDirectory, "active-file.json", config.capture.activeFile, async () => ({ activeFile: await obsidian.dev.eval("app.workspace.getActiveFile()?.path ?? null") })),
|
|
46
|
+
captureTextArtifact(artifactDirectory, "dom.txt", config.capture.dom, async () => String(await obsidian.dev.dom({
|
|
47
|
+
inner: true,
|
|
48
|
+
selector: ".workspace"
|
|
49
|
+
}))),
|
|
50
|
+
captureJsonArtifact(artifactDirectory, "editor.json", config.capture.editorText, async () => ({ text: await obsidian.dev.eval("app.workspace.activeLeaf?.view?.editor?.getValue?.() ?? null") })),
|
|
51
|
+
captureScreenshotArtifact(artifactDirectory, config.capture.screenshot, obsidian),
|
|
52
|
+
captureJsonArtifact(artifactDirectory, "tabs.json", config.capture.tabs, () => obsidian.tabs()),
|
|
53
|
+
captureJsonArtifact(artifactDirectory, "workspace.json", config.capture.workspace, () => obsidian.workspace())
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function registerPluginFailureArtifacts(context, plugin, options) {
|
|
58
|
+
const config = getFailureArtifactConfig(options);
|
|
59
|
+
if (!config.enabled) return;
|
|
60
|
+
context.onTestFailed(async () => {
|
|
61
|
+
const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);
|
|
62
|
+
await mkdir(artifactDirectory, { recursive: true });
|
|
63
|
+
await captureJsonArtifact(artifactDirectory, `${plugin.id}-data.json`, true, () => plugin.data().read());
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async function captureJsonArtifact(artifactDirectory, filename, enabled, readValue) {
|
|
67
|
+
if (!enabled) return;
|
|
68
|
+
try {
|
|
69
|
+
const value = await readValue();
|
|
70
|
+
await writeFile(path.join(artifactDirectory, filename), `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
71
|
+
} catch (error) {
|
|
72
|
+
await writeFile(path.join(artifactDirectory, `${filename}.error.txt`), formatArtifactError(error), "utf8");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function captureScreenshotArtifact(artifactDirectory, enabled, obsidian) {
|
|
76
|
+
if (!enabled) return;
|
|
77
|
+
const screenshotPath = path.join(artifactDirectory, "screenshot.png");
|
|
78
|
+
try {
|
|
79
|
+
await obsidian.dev.screenshot(screenshotPath);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
await writeFile(path.join(artifactDirectory, "screenshot.error.txt"), formatArtifactError(error), "utf8");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function captureTextArtifact(artifactDirectory, filename, enabled, readValue) {
|
|
85
|
+
if (!enabled) return;
|
|
86
|
+
try {
|
|
87
|
+
await writeFile(path.join(artifactDirectory, filename), await readValue(), "utf8");
|
|
88
|
+
} catch (error) {
|
|
89
|
+
await writeFile(path.join(artifactDirectory, `${filename}.error.txt`), formatArtifactError(error), "utf8");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function formatArtifactError(error) {
|
|
93
|
+
return error instanceof Error ? `${error.name}: ${error.message}\n` : `${String(error)}\n`;
|
|
94
|
+
}
|
|
95
|
+
function sanitizeForPath(value) {
|
|
96
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "test";
|
|
97
|
+
}
|
|
98
|
+
function createBaseFixtures(options, fixtureOptions = {}) {
|
|
99
|
+
const createVault = fixtureOptions.createVault ?? ((obsidian) => createVaultApi({ obsidian }));
|
|
100
|
+
return {
|
|
101
|
+
obsidian: async ({ onTestFailed, task }, use) => {
|
|
102
|
+
const obsidian = createObsidianClient(options);
|
|
103
|
+
await obsidian.verify();
|
|
104
|
+
registerFailureArtifacts({
|
|
105
|
+
onTestFailed,
|
|
106
|
+
task
|
|
107
|
+
}, obsidian, options);
|
|
108
|
+
try {
|
|
109
|
+
await use(obsidian);
|
|
110
|
+
} finally {
|
|
111
|
+
await getClientInternals(obsidian).restoreAll();
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
sandbox: async ({ obsidian }, use) => {
|
|
115
|
+
const sandbox = await createSandboxApi({
|
|
116
|
+
obsidian,
|
|
117
|
+
sandboxRoot: options.sandboxRoot ?? "__obsidian_e2e__",
|
|
118
|
+
testName: "test"
|
|
119
|
+
});
|
|
120
|
+
try {
|
|
121
|
+
await use(sandbox);
|
|
122
|
+
} finally {
|
|
123
|
+
await sandbox.cleanup();
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
vault: async ({ obsidian }, use) => {
|
|
127
|
+
await use(await createVault(obsidian));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/fixtures/create-obsidian-test.ts
|
|
133
|
+
function createObsidianTest(options) {
|
|
134
|
+
return test.extend(createBaseFixtures(options));
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/fixtures/create-plugin-test.ts
|
|
138
|
+
function createPluginTest(options) {
|
|
139
|
+
return test.extend({
|
|
140
|
+
...createBaseFixtures(options, { async createVault(obsidian) {
|
|
141
|
+
if (options.seedVault) await applyVaultSeed(obsidian, options.seedVault);
|
|
142
|
+
return createVaultApi({ obsidian });
|
|
143
|
+
} }),
|
|
144
|
+
plugin: async ({ obsidian, onTestFailed, task }, use) => {
|
|
145
|
+
const plugin = obsidian.plugin(options.pluginId);
|
|
146
|
+
const wasEnabled = await plugin.isEnabled();
|
|
147
|
+
if (!wasEnabled) await plugin.enable({ filter: options.pluginFilter });
|
|
148
|
+
if (options.seedPluginData !== void 0) await plugin.data().write(options.seedPluginData);
|
|
149
|
+
registerPluginFailureArtifacts({
|
|
150
|
+
onTestFailed,
|
|
151
|
+
task
|
|
152
|
+
}, plugin, options);
|
|
153
|
+
try {
|
|
154
|
+
await use(plugin);
|
|
155
|
+
} finally {
|
|
156
|
+
if (!wasEnabled) await plugin.disable({ filter: options.pluginFilter });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function applyVaultSeed(obsidian, seedVault) {
|
|
162
|
+
const vaultRoot = await obsidian.vaultPath();
|
|
163
|
+
for (const [targetPath, value] of Object.entries(seedVault)) {
|
|
164
|
+
const resolvedPath = path.resolve(vaultRoot, ...targetPath.split("/").filter(Boolean));
|
|
165
|
+
const normalizedVaultRoot = path.resolve(vaultRoot);
|
|
166
|
+
if (resolvedPath !== normalizedVaultRoot && !resolvedPath.startsWith(`${normalizedVaultRoot}${path.sep}`)) throw new Error(`Seed path escapes the vault root: ${targetPath}`);
|
|
167
|
+
await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);
|
|
168
|
+
await mkdir(path.dirname(resolvedPath), { recursive: true });
|
|
169
|
+
await writeSeedValue(resolvedPath, value);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function writeSeedValue(resolvedPath, value) {
|
|
173
|
+
if (typeof value === "string") {
|
|
174
|
+
await writeFile(resolvedPath, value, "utf8");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
await writeFile(resolvedPath, `${JSON.stringify(value.json, null, 2)}\n`, "utf8");
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { createObsidianTest, createPluginTest };
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=vitest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.mjs","names":["base","base"],"sources":["../src/fixtures/failure-artifacts.ts","../src/fixtures/base-fixtures.ts","../src/fixtures/create-obsidian-test.ts","../src/fixtures/create-plugin-test.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { TestContext } from \"vite-plus/test\";\n\nimport type { ObsidianClient, PluginHandle } from \"../core/types\";\nimport type { CreateObsidianTestOptions, FailureArtifactOptions } from \"./types\";\n\nconst DEFAULT_ARTIFACTS_DIR = \".obsidian-e2e-artifacts\";\n\ninterface FailureArtifactConfig {\n artifactsDir: string;\n capture: Required<FailureArtifactOptions>;\n enabled: boolean;\n}\n\nexport function getFailureArtifactConfig(\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): FailureArtifactConfig {\n if (!options.captureOnFailure) {\n return {\n artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),\n capture: {\n activeFile: true,\n dom: true,\n editorText: true,\n screenshot: true,\n tabs: true,\n workspace: true,\n },\n enabled: false,\n };\n }\n\n const overrides = options.captureOnFailure === true ? {} : options.captureOnFailure;\n\n return {\n artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),\n capture: {\n activeFile: overrides.activeFile ?? true,\n dom: overrides.dom ?? true,\n editorText: overrides.editorText ?? true,\n screenshot: overrides.screenshot ?? true,\n tabs: overrides.tabs ?? true,\n workspace: overrides.workspace ?? true,\n },\n enabled: true,\n };\n}\n\nexport function getFailureArtifactDirectory(\n artifactsDir: string,\n task: Pick<TestContext[\"task\"], \"id\" | \"name\">,\n): string {\n const suffix = task.id.split(\"_\").at(-1) ?? \"test\";\n return path.join(artifactsDir, `${sanitizeForPath(task.name)}-${suffix}`);\n}\n\nexport function registerFailureArtifacts(\n context: Pick<TestContext, \"onTestFailed\" | \"task\">,\n obsidian: ObsidianClient,\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): void {\n const config = getFailureArtifactConfig(options);\n\n if (!config.enabled) {\n return;\n }\n\n context.onTestFailed(async () => {\n const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);\n await mkdir(artifactDirectory, { recursive: true });\n\n await Promise.all([\n captureJsonArtifact(\n artifactDirectory,\n \"active-file.json\",\n config.capture.activeFile,\n async () => ({\n activeFile: await obsidian.dev.eval<string | null>(\n \"app.workspace.getActiveFile()?.path ?? null\",\n ),\n }),\n ),\n captureTextArtifact(artifactDirectory, \"dom.txt\", config.capture.dom, async () =>\n String(\n await obsidian.dev.dom({\n inner: true,\n selector: \".workspace\",\n }),\n ),\n ),\n captureJsonArtifact(\n artifactDirectory,\n \"editor.json\",\n config.capture.editorText,\n async () => ({\n text: await obsidian.dev.eval<string | null>(\n \"app.workspace.activeLeaf?.view?.editor?.getValue?.() ?? null\",\n ),\n }),\n ),\n captureScreenshotArtifact(artifactDirectory, config.capture.screenshot, obsidian),\n captureJsonArtifact(artifactDirectory, \"tabs.json\", config.capture.tabs, () =>\n obsidian.tabs(),\n ),\n captureJsonArtifact(artifactDirectory, \"workspace.json\", config.capture.workspace, () =>\n obsidian.workspace(),\n ),\n ]);\n });\n}\n\nexport function registerPluginFailureArtifacts(\n context: Pick<TestContext, \"onTestFailed\" | \"task\">,\n plugin: PluginHandle,\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): void {\n const config = getFailureArtifactConfig(options);\n\n if (!config.enabled) {\n return;\n }\n\n context.onTestFailed(async () => {\n const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);\n await mkdir(artifactDirectory, { recursive: true });\n await captureJsonArtifact(artifactDirectory, `${plugin.id}-data.json`, true, () =>\n plugin.data().read(),\n );\n });\n}\n\nasync function captureJsonArtifact(\n artifactDirectory: string,\n filename: string,\n enabled: boolean,\n readValue: () => Promise<unknown>,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n try {\n const value = await readValue();\n await writeFile(\n path.join(artifactDirectory, filename),\n `${JSON.stringify(value, null, 2)}\\n`,\n \"utf8\",\n );\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, `${filename}.error.txt`),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nasync function captureScreenshotArtifact(\n artifactDirectory: string,\n enabled: boolean,\n obsidian: ObsidianClient,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n const screenshotPath = path.join(artifactDirectory, \"screenshot.png\");\n\n try {\n await obsidian.dev.screenshot(screenshotPath);\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, \"screenshot.error.txt\"),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nasync function captureTextArtifact(\n artifactDirectory: string,\n filename: string,\n enabled: boolean,\n readValue: () => Promise<string>,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n try {\n await writeFile(path.join(artifactDirectory, filename), await readValue(), \"utf8\");\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, `${filename}.error.txt`),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nfunction formatArtifactError(error: unknown): string {\n return error instanceof Error ? `${error.name}: ${error.message}\\n` : `${String(error)}\\n`;\n}\n\nfunction sanitizeForPath(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"test\"\n );\n}\n","import type { TestContext } from \"vite-plus/test\";\n\nimport { createObsidianClient } from \"../core/client\";\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient, VaultApi } from \"../core/types\";\nimport { createSandboxApi } from \"../vault/sandbox\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { registerFailureArtifacts } from \"./failure-artifacts\";\nimport type { CreateObsidianTestOptions } from \"./types\";\n\nexport const DEFAULT_SANDBOX_ROOT = \"__obsidian_e2e__\";\n\ninterface BaseFixtureOptions {\n createVault?: (obsidian: ObsidianClient) => Promise<VaultApi> | VaultApi;\n}\n\nexport function createBaseFixtures(\n options: CreateObsidianTestOptions,\n fixtureOptions: BaseFixtureOptions = {},\n) {\n const createVault =\n fixtureOptions.createVault ?? ((obsidian: ObsidianClient) => createVaultApi({ obsidian }));\n\n return {\n // oxlint-disable-next-line no-empty-pattern\n obsidian: async (\n { onTestFailed, task }: Pick<TestContext, \"onTestFailed\" | \"task\">,\n use: (obsidian: ObsidianClient) => Promise<void>,\n ) => {\n const obsidian = createObsidianClient(options);\n\n await obsidian.verify();\n registerFailureArtifacts({ onTestFailed, task }, obsidian, options);\n\n try {\n await use(obsidian);\n } finally {\n await getClientInternals(obsidian).restoreAll();\n }\n },\n sandbox: async (\n { obsidian }: { obsidian: ObsidianClient },\n use: (sandbox: Awaited<ReturnType<typeof createSandboxApi>>) => Promise<void>,\n ) => {\n const sandbox = await createSandboxApi({\n obsidian,\n sandboxRoot: options.sandboxRoot ?? DEFAULT_SANDBOX_ROOT,\n testName: \"test\",\n });\n\n try {\n await use(sandbox);\n } finally {\n await sandbox.cleanup();\n }\n },\n vault: async (\n { obsidian }: { obsidian: ObsidianClient },\n use: (vault: VaultApi) => Promise<void>,\n ) => {\n await use(await createVault(obsidian));\n },\n };\n}\n","import { test as base } from \"vite-plus/test\";\n\nimport { createBaseFixtures } from \"./base-fixtures\";\nimport type { CreateObsidianTestOptions, ObsidianFixtures, ObsidianTest } from \"./types\";\n\nexport function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest {\n return base.extend<ObsidianFixtures>(createBaseFixtures(options));\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { test as base } from \"vite-plus/test\";\nimport type { TestContext } from \"vite-plus/test\";\n\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient } from \"../core/types\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { createBaseFixtures } from \"./base-fixtures\";\nimport { registerPluginFailureArtifacts } from \"./failure-artifacts\";\nimport type {\n CreatePluginTestOptions,\n PluginFixtures,\n PluginTest,\n VaultSeed,\n VaultSeedEntry,\n} from \"./types\";\n\nexport function createPluginTest(options: CreatePluginTestOptions): PluginTest {\n return base.extend<PluginFixtures>({\n ...createBaseFixtures(options, {\n async createVault(obsidian) {\n if (options.seedVault) {\n await applyVaultSeed(obsidian, options.seedVault);\n }\n\n return createVaultApi({ obsidian });\n },\n }),\n plugin: async (\n {\n obsidian,\n onTestFailed,\n task,\n }: Pick<PluginFixtures & TestContext, \"obsidian\" | \"onTestFailed\" | \"task\">,\n use,\n ) => {\n const plugin = obsidian.plugin(options.pluginId);\n const wasEnabled = await plugin.isEnabled();\n\n if (!wasEnabled) {\n await plugin.enable({ filter: options.pluginFilter });\n }\n\n if (options.seedPluginData !== undefined) {\n await plugin.data().write(options.seedPluginData);\n }\n\n registerPluginFailureArtifacts({ onTestFailed, task }, plugin, options);\n\n try {\n await use(plugin);\n } finally {\n if (!wasEnabled) {\n await plugin.disable({ filter: options.pluginFilter });\n }\n }\n },\n });\n}\n\nasync function applyVaultSeed(obsidian: ObsidianClient, seedVault: VaultSeed): Promise<void> {\n const vaultRoot = await obsidian.vaultPath();\n\n for (const [targetPath, value] of Object.entries(seedVault)) {\n const resolvedPath = path.resolve(vaultRoot, ...targetPath.split(\"/\").filter(Boolean));\n const normalizedVaultRoot = path.resolve(vaultRoot);\n\n if (\n resolvedPath !== normalizedVaultRoot &&\n !resolvedPath.startsWith(`${normalizedVaultRoot}${path.sep}`)\n ) {\n throw new Error(`Seed path escapes the vault root: ${targetPath}`);\n }\n\n await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);\n await mkdir(path.dirname(resolvedPath), { recursive: true });\n await writeSeedValue(resolvedPath, value);\n }\n}\n\nasync function writeSeedValue(resolvedPath: string, value: VaultSeedEntry): Promise<void> {\n if (typeof value === \"string\") {\n await writeFile(resolvedPath, value, \"utf8\");\n return;\n }\n\n await writeFile(resolvedPath, `${JSON.stringify(value.json, null, 2)}\\n`, \"utf8\");\n}\n"],"mappings":";;;;;AAQA,MAAM,wBAAwB;AAQ9B,SAAgB,yBACd,SACuB;AACvB,KAAI,CAAC,QAAQ,iBACX,QAAO;EACL,cAAc,KAAK,QAAQ,QAAQ,gBAAgB,sBAAsB;EACzE,SAAS;GACP,YAAY;GACZ,KAAK;GACL,YAAY;GACZ,YAAY;GACZ,MAAM;GACN,WAAW;GACZ;EACD,SAAS;EACV;CAGH,MAAM,YAAY,QAAQ,qBAAqB,OAAO,EAAE,GAAG,QAAQ;AAEnE,QAAO;EACL,cAAc,KAAK,QAAQ,QAAQ,gBAAgB,sBAAsB;EACzE,SAAS;GACP,YAAY,UAAU,cAAc;GACpC,KAAK,UAAU,OAAO;GACtB,YAAY,UAAU,cAAc;GACpC,YAAY,UAAU,cAAc;GACpC,MAAM,UAAU,QAAQ;GACxB,WAAW,UAAU,aAAa;GACnC;EACD,SAAS;EACV;;AAGH,SAAgB,4BACd,cACA,MACQ;CACR,MAAM,SAAS,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,GAAG,IAAI;AAC5C,QAAO,KAAK,KAAK,cAAc,GAAG,gBAAgB,KAAK,KAAK,CAAC,GAAG,SAAS;;AAG3E,SAAgB,yBACd,SACA,UACA,SACM;CACN,MAAM,SAAS,yBAAyB,QAAQ;AAEhD,KAAI,CAAC,OAAO,QACV;AAGF,SAAQ,aAAa,YAAY;EAC/B,MAAM,oBAAoB,4BAA4B,OAAO,cAAc,QAAQ,KAAK;AACxF,QAAM,MAAM,mBAAmB,EAAE,WAAW,MAAM,CAAC;AAEnD,QAAM,QAAQ,IAAI;GAChB,oBACE,mBACA,oBACA,OAAO,QAAQ,YACf,aAAa,EACX,YAAY,MAAM,SAAS,IAAI,KAC7B,8CACD,EACF,EACF;GACD,oBAAoB,mBAAmB,WAAW,OAAO,QAAQ,KAAK,YACpE,OACE,MAAM,SAAS,IAAI,IAAI;IACrB,OAAO;IACP,UAAU;IACX,CAAC,CACH,CACF;GACD,oBACE,mBACA,eACA,OAAO,QAAQ,YACf,aAAa,EACX,MAAM,MAAM,SAAS,IAAI,KACvB,+DACD,EACF,EACF;GACD,0BAA0B,mBAAmB,OAAO,QAAQ,YAAY,SAAS;GACjF,oBAAoB,mBAAmB,aAAa,OAAO,QAAQ,YACjE,SAAS,MAAM,CAChB;GACD,oBAAoB,mBAAmB,kBAAkB,OAAO,QAAQ,iBACtE,SAAS,WAAW,CACrB;GACF,CAAC;GACF;;AAGJ,SAAgB,+BACd,SACA,QACA,SACM;CACN,MAAM,SAAS,yBAAyB,QAAQ;AAEhD,KAAI,CAAC,OAAO,QACV;AAGF,SAAQ,aAAa,YAAY;EAC/B,MAAM,oBAAoB,4BAA4B,OAAO,cAAc,QAAQ,KAAK;AACxF,QAAM,MAAM,mBAAmB,EAAE,WAAW,MAAM,CAAC;AACnD,QAAM,oBAAoB,mBAAmB,GAAG,OAAO,GAAG,aAAa,YACrE,OAAO,MAAM,CAAC,MAAM,CACrB;GACD;;AAGJ,eAAe,oBACb,mBACA,UACA,SACA,WACe;AACf,KAAI,CAAC,QACH;AAGF,KAAI;EACF,MAAM,QAAQ,MAAM,WAAW;AAC/B,QAAM,UACJ,KAAK,KAAK,mBAAmB,SAAS,EACtC,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAClC,OACD;UACM,OAAO;AACd,QAAM,UACJ,KAAK,KAAK,mBAAmB,GAAG,SAAS,YAAY,EACrD,oBAAoB,MAAM,EAC1B,OACD;;;AAIL,eAAe,0BACb,mBACA,SACA,UACe;AACf,KAAI,CAAC,QACH;CAGF,MAAM,iBAAiB,KAAK,KAAK,mBAAmB,iBAAiB;AAErE,KAAI;AACF,QAAM,SAAS,IAAI,WAAW,eAAe;UACtC,OAAO;AACd,QAAM,UACJ,KAAK,KAAK,mBAAmB,uBAAuB,EACpD,oBAAoB,MAAM,EAC1B,OACD;;;AAIL,eAAe,oBACb,mBACA,UACA,SACA,WACe;AACf,KAAI,CAAC,QACH;AAGF,KAAI;AACF,QAAM,UAAU,KAAK,KAAK,mBAAmB,SAAS,EAAE,MAAM,WAAW,EAAE,OAAO;UAC3E,OAAO;AACd,QAAM,UACJ,KAAK,KAAK,mBAAmB,GAAG,SAAS,YAAY,EACrD,oBAAoB,MAAM,EAC1B,OACD;;;AAIL,SAAS,oBAAoB,OAAwB;AACnD,QAAO,iBAAiB,QAAQ,GAAG,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO,MAAM,CAAC;;AAGzF,SAAS,gBAAgB,OAAuB;AAC9C,QACE,MACG,MAAM,CACN,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,MAAM,GAAG,GAAG,IAAI;;ACrMvB,SAAgB,mBACd,SACA,iBAAqC,EAAE,EACvC;CACA,MAAM,cACJ,eAAe,iBAAiB,aAA6B,eAAe,EAAE,UAAU,CAAC;AAE3F,QAAO;EAEL,UAAU,OACR,EAAE,cAAc,QAChB,QACG;GACH,MAAM,WAAW,qBAAqB,QAAQ;AAE9C,SAAM,SAAS,QAAQ;AACvB,4BAAyB;IAAE;IAAc;IAAM,EAAE,UAAU,QAAQ;AAEnE,OAAI;AACF,UAAM,IAAI,SAAS;aACX;AACR,UAAM,mBAAmB,SAAS,CAAC,YAAY;;;EAGnD,SAAS,OACP,EAAE,YACF,QACG;GACH,MAAM,UAAU,MAAM,iBAAiB;IACrC;IACA,aAAa,QAAQ,eAAA;IACrB,UAAU;IACX,CAAC;AAEF,OAAI;AACF,UAAM,IAAI,QAAQ;aACV;AACR,UAAM,QAAQ,SAAS;;;EAG3B,OAAO,OACL,EAAE,YACF,QACG;AACH,SAAM,IAAI,MAAM,YAAY,SAAS,CAAC;;EAEzC;;;;ACzDH,SAAgB,mBAAmB,SAAkD;AACnF,QAAOA,KAAK,OAAyB,mBAAmB,QAAQ,CAAC;;;;ACanE,SAAgB,iBAAiB,SAA8C;AAC7E,QAAOC,KAAK,OAAuB;EACjC,GAAG,mBAAmB,SAAS,EAC7B,MAAM,YAAY,UAAU;AAC1B,OAAI,QAAQ,UACV,OAAM,eAAe,UAAU,QAAQ,UAAU;AAGnD,UAAO,eAAe,EAAE,UAAU,CAAC;KAEtC,CAAC;EACF,QAAQ,OACN,EACE,UACA,cACA,QAEF,QACG;GACH,MAAM,SAAS,SAAS,OAAO,QAAQ,SAAS;GAChD,MAAM,aAAa,MAAM,OAAO,WAAW;AAE3C,OAAI,CAAC,WACH,OAAM,OAAO,OAAO,EAAE,QAAQ,QAAQ,cAAc,CAAC;AAGvD,OAAI,QAAQ,mBAAmB,KAAA,EAC7B,OAAM,OAAO,MAAM,CAAC,MAAM,QAAQ,eAAe;AAGnD,kCAA+B;IAAE;IAAc;IAAM,EAAE,QAAQ,QAAQ;AAEvE,OAAI;AACF,UAAM,IAAI,OAAO;aACT;AACR,QAAI,CAAC,WACH,OAAM,OAAO,QAAQ,EAAE,QAAQ,QAAQ,cAAc,CAAC;;;EAI7D,CAAC;;AAGJ,eAAe,eAAe,UAA0B,WAAqC;CAC3F,MAAM,YAAY,MAAM,SAAS,WAAW;AAE5C,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,UAAU,EAAE;EAC3D,MAAM,eAAe,KAAK,QAAQ,WAAW,GAAG,WAAW,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC;EACtF,MAAM,sBAAsB,KAAK,QAAQ,UAAU;AAEnD,MACE,iBAAiB,uBACjB,CAAC,aAAa,WAAW,GAAG,sBAAsB,KAAK,MAAM,CAE7D,OAAM,IAAI,MAAM,qCAAqC,aAAa;AAGpE,QAAM,mBAAmB,SAAS,CAAC,iBAAiB,aAAa;AACjE,QAAM,MAAM,KAAK,QAAQ,aAAa,EAAE,EAAE,WAAW,MAAM,CAAC;AAC5D,QAAM,eAAe,cAAc,MAAM;;;AAI7C,eAAe,eAAe,cAAsB,OAAsC;AACxF,KAAI,OAAO,UAAU,UAAU;AAC7B,QAAM,UAAU,cAAc,OAAO,OAAO;AAC5C;;AAGF,OAAM,UAAU,cAAc,GAAG,KAAK,UAAU,MAAM,MAAM,MAAM,EAAE,CAAC,KAAK,OAAO"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obsidian-e2e",
|
|
3
|
+
"version": "0.0.0-next.0",
|
|
4
|
+
"description": "Vitest-first end-to-end test utilities for Obsidian plugins.",
|
|
5
|
+
"homepage": "https://github.com/chhoumann/obsidian-e2e#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/chhoumann/obsidian-e2e/issues"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "Christian B. B. Houmann",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/chhoumann/obsidian-e2e.git"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"main": "./dist/index.mjs",
|
|
21
|
+
"module": "./dist/index.mjs",
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.mts",
|
|
26
|
+
"default": "./dist/index.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./vitest": {
|
|
29
|
+
"types": "./dist/vitest.d.mts",
|
|
30
|
+
"default": "./dist/vitest.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./matchers": {
|
|
33
|
+
"types": "./dist/matchers.d.mts",
|
|
34
|
+
"default": "./dist/matchers.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"vite-plus": "^0.1.11"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
43
|
+
},
|
|
44
|
+
"packageManager": "pnpm@10.32.1"
|
|
45
|
+
}
|