obsidian-dev-utils 101.6.0 → 101.7.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/CHANGELOG.md +5 -0
- package/dist/integration-test-plugin/main.js +4410 -3705
- package/dist/lib/cjs/__merged.cjs +26 -1
- package/dist/lib/cjs/__merged.d.cts +4 -1
- package/dist/lib/cjs/generated-during-build.cjs +1 -1
- package/dist/lib/cjs/obsidian/components/component-ex.cjs +14 -1
- package/dist/lib/cjs/obsidian/components/component-ex.d.cts +11 -0
- package/dist/lib/cjs/obsidian/components/index.cjs +4 -1
- package/dist/lib/cjs/obsidian/components/index.d.cts +1 -0
- package/dist/lib/cjs/obsidian/components/plugin-gate-component.cjs +520 -0
- package/dist/lib/cjs/obsidian/components/plugin-gate-component.d.cts +299 -0
- package/dist/lib/cjs/obsidian/components/plugin-suggestion-component.cjs +17 -38
- package/dist/lib/cjs/obsidian/components/plugin-suggestion-component.d.cts +25 -18
- package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.cjs +7 -2
- package/dist/lib/cjs/obsidian/css-class.cjs +3 -1
- package/dist/lib/cjs/obsidian/css-class.d.cts +9 -0
- package/dist/lib/cjs/obsidian/html-element.cjs +27 -3
- package/dist/lib/cjs/obsidian/html-element.d.cts +36 -0
- package/dist/lib/cjs/obsidian/i18n/locales/en.cjs +15 -1
- package/dist/lib/cjs/obsidian/i18n/locales/en.d.cts +14 -0
- package/dist/lib/cjs/obsidian/i18n/locales/translations-map.d.cts +14 -0
- package/dist/lib/cjs/obsidian/plugin/index.cjs +7 -1
- package/dist/lib/cjs/obsidian/plugin/index.d.cts +2 -0
- package/dist/lib/cjs/obsidian/plugin/plugin-api.cjs +2 -2
- package/dist/lib/cjs/obsidian/plugin/plugin-api.d.cts +34 -15
- package/dist/lib/cjs/obsidian/plugin/plugin-install-state.cjs +208 -0
- package/dist/lib/cjs/obsidian/plugin/plugin-install-state.d.cts +113 -0
- package/dist/lib/cjs/obsidian/plugin/plugin-lifecycle-events.cjs +161 -0
- package/dist/lib/cjs/obsidian/plugin/plugin-lifecycle-events.d.cts +119 -0
- package/dist/lib/cjs/obsidian/plugin/plugin.cjs +241 -21
- package/dist/lib/cjs/obsidian/plugin/plugin.d.cts +133 -4
- package/dist/lib/esm/__merged.d.mts +4 -1
- package/dist/lib/esm/__merged.mjs +30 -2
- package/dist/lib/esm/generated-during-build.mjs +1 -1
- package/dist/lib/esm/obsidian/components/component-ex.d.mts +11 -0
- package/dist/lib/esm/obsidian/components/component-ex.mjs +14 -1
- package/dist/lib/esm/obsidian/components/index.d.mts +1 -0
- package/dist/lib/esm/obsidian/components/index.mjs +3 -1
- package/dist/lib/esm/obsidian/components/plugin-gate-component.d.mts +299 -0
- package/dist/lib/esm/obsidian/components/plugin-gate-component.mjs +428 -0
- package/dist/lib/esm/obsidian/components/plugin-suggestion-component.d.mts +25 -18
- package/dist/lib/esm/obsidian/components/plugin-suggestion-component.mjs +21 -41
- package/dist/lib/esm/obsidian/components/rename-delete-handler-component.mjs +7 -2
- package/dist/lib/esm/obsidian/css-class.d.mts +9 -0
- package/dist/lib/esm/obsidian/css-class.mjs +3 -1
- package/dist/lib/esm/obsidian/html-element.d.mts +36 -0
- package/dist/lib/esm/obsidian/html-element.mjs +24 -2
- package/dist/lib/esm/obsidian/i18n/locales/en.d.mts +14 -0
- package/dist/lib/esm/obsidian/i18n/locales/en.mjs +15 -1
- package/dist/lib/esm/obsidian/i18n/locales/translations-map.d.mts +14 -0
- package/dist/lib/esm/obsidian/plugin/index.d.mts +2 -0
- package/dist/lib/esm/obsidian/plugin/index.mjs +5 -1
- package/dist/lib/esm/obsidian/plugin/plugin-api.d.mts +34 -15
- package/dist/lib/esm/obsidian/plugin/plugin-api.mjs +2 -2
- package/dist/lib/esm/obsidian/plugin/plugin-install-state.d.mts +113 -0
- package/dist/lib/esm/obsidian/plugin/plugin-install-state.mjs +106 -0
- package/dist/lib/esm/obsidian/plugin/plugin-lifecycle-events.d.mts +119 -0
- package/dist/lib/esm/obsidian/plugin/plugin-lifecycle-events.mjs +54 -0
- package/dist/lib/esm/obsidian/plugin/plugin.d.mts +133 -4
- package/dist/lib/esm/obsidian/plugin/plugin.mjs +245 -21
- package/obsidian/components/plugin-gate-component/package.json +6 -0
- package/obsidian/plugin/plugin-install-state/package.json +6 -0
- package/obsidian/plugin/plugin-lifecycle-events/package.json +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/// <reference path="../../library.d.cts" />
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
*
|
|
5
|
+
* How another plugin is present in this vault, and the one-click path to making it present.
|
|
6
|
+
*
|
|
7
|
+
* Shared by the two components that ask a user to bring another plugin in: `PluginSuggestionComponent`,
|
|
8
|
+
* which OFFERS one and keeps working without it, and `PluginGateComponent`, which REQUIRES one and
|
|
9
|
+
* does nothing until it is there. The offer and the requirement differ in what they do about the answer,
|
|
10
|
+
* not in how they read the state or how they install — so that part lives here rather than in both.
|
|
11
|
+
*/
|
|
12
|
+
import type { App } from 'obsidian' with { 'resolution-mode': 'import' };
|
|
13
|
+
import type { PluginNoticeComponent } from '../components/plugin-notice-component.cjs';
|
|
14
|
+
/**
|
|
15
|
+
* How another plugin is currently present in the vault.
|
|
16
|
+
*/
|
|
17
|
+
export declare enum InstalledPluginState {
|
|
18
|
+
/**
|
|
19
|
+
* Installed and enabled — there is nothing to do.
|
|
20
|
+
*/
|
|
21
|
+
Enabled = "enabled",
|
|
22
|
+
/**
|
|
23
|
+
* Installed but disabled. Only an enable is needed, so no download happens.
|
|
24
|
+
*/
|
|
25
|
+
InstalledButDisabled = "installedButDisabled",
|
|
26
|
+
/**
|
|
27
|
+
* Not installed at all.
|
|
28
|
+
*/
|
|
29
|
+
NotInstalled = "notInstalled"
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parameters for {@link getInstalledPluginState}.
|
|
33
|
+
*/
|
|
34
|
+
export interface GetInstalledPluginStateParams {
|
|
35
|
+
/**
|
|
36
|
+
* The Obsidian app instance.
|
|
37
|
+
*/
|
|
38
|
+
readonly app: App;
|
|
39
|
+
/**
|
|
40
|
+
* The `manifest.id` of the plugin to look for.
|
|
41
|
+
*/
|
|
42
|
+
readonly pluginId: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parameters for {@link getInstalledPluginVersion}.
|
|
46
|
+
*/
|
|
47
|
+
export interface GetInstalledPluginVersionParams {
|
|
48
|
+
/**
|
|
49
|
+
* The Obsidian app instance.
|
|
50
|
+
*/
|
|
51
|
+
readonly app: App;
|
|
52
|
+
/**
|
|
53
|
+
* The `manifest.id` of the plugin to look for.
|
|
54
|
+
*/
|
|
55
|
+
readonly pluginId: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parameters for {@link installAndEnablePlugin}.
|
|
59
|
+
*/
|
|
60
|
+
export interface InstallAndEnablePluginParams {
|
|
61
|
+
/**
|
|
62
|
+
* The Obsidian app instance.
|
|
63
|
+
*/
|
|
64
|
+
readonly app: App;
|
|
65
|
+
/**
|
|
66
|
+
* The `manifest.id` of the plugin to install and enable.
|
|
67
|
+
*/
|
|
68
|
+
readonly pluginId: string;
|
|
69
|
+
/**
|
|
70
|
+
* The display name of the plugin, shown to the user.
|
|
71
|
+
*/
|
|
72
|
+
readonly pluginName: string;
|
|
73
|
+
/**
|
|
74
|
+
* The notice component of the plugin doing the asking, used to report the outcome.
|
|
75
|
+
*/
|
|
76
|
+
readonly pluginNoticeComponent: PluginNoticeComponent;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resolves how another plugin is currently present in the vault.
|
|
80
|
+
*
|
|
81
|
+
* @param params - The {@link GetInstalledPluginStateParams}.
|
|
82
|
+
* @returns The {@link InstalledPluginState}.
|
|
83
|
+
*/
|
|
84
|
+
export declare function getInstalledPluginState(params: GetInstalledPluginStateParams): InstalledPluginState;
|
|
85
|
+
/**
|
|
86
|
+
* Reads the version of another plugin as this vault has it installed, or `null` when it is not running.
|
|
87
|
+
*
|
|
88
|
+
* The version comes out of `app.plugins.manifests`, which is populated for every installed plugin at
|
|
89
|
+
* startup — so, unlike anything a plugin REGISTERS, it answers the same way no matter which plugin loaded
|
|
90
|
+
* first. That load-order independence is the whole reason a conflict is detected by version rather than by
|
|
91
|
+
* asking a registry.
|
|
92
|
+
*
|
|
93
|
+
* `null` for a plugin that is not installed OR is installed but disabled: a disabled plugin registers
|
|
94
|
+
* nothing, so for every question this answers the two cases are the same. An installed, enabled plugin
|
|
95
|
+
* whose manifest somehow carries no version reads as an empty string rather than `null`, which keeps
|
|
96
|
+
* "running, version unknown" distinguishable from "not running" — a caller comparing versions should fail
|
|
97
|
+
* closed on it rather than treat it as absent.
|
|
98
|
+
*
|
|
99
|
+
* @param params - The {@link GetInstalledPluginVersionParams}.
|
|
100
|
+
* @returns The installed version, or `null` when the plugin is not enabled.
|
|
101
|
+
*/
|
|
102
|
+
export declare function getInstalledPluginVersion(params: GetInstalledPluginVersionParams): null | string;
|
|
103
|
+
/**
|
|
104
|
+
* Installs (when needed) and enables another plugin, reporting the outcome as a notice.
|
|
105
|
+
*
|
|
106
|
+
* A no-op when the plugin is already enabled, so a caller reacting to a click need not check first.
|
|
107
|
+
*
|
|
108
|
+
* @param params - The {@link InstallAndEnablePluginParams}.
|
|
109
|
+
* @returns A {@link Promise} that resolves once the plugin is enabled.
|
|
110
|
+
* @throws Whatever the install or enable threw, after reporting it as a notice — the caller decides
|
|
111
|
+
* whether a failure is worth more than the notice the user has already seen.
|
|
112
|
+
*/
|
|
113
|
+
export declare function installAndEnablePlugin(params: InstallAndEnablePluginParams): Promise<void>;
|
|
@@ -0,0 +1,161 @@
|
|
|
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 () {
|
|
7
|
+
function ensureBrowserProcess() {
|
|
8
|
+
const browserProcess = {
|
|
9
|
+
browser: true,
|
|
10
|
+
cwd() {
|
|
11
|
+
return '/';
|
|
12
|
+
},
|
|
13
|
+
env: {},
|
|
14
|
+
platform: 'android'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line obsidianmd/no-global-this, unicorn/no-unnecessary-global-this -- Must stay `globalThis`-qualified: in the emitted banner a bare `process` is a free identifier, so reading it where the host has none throws a ReferenceError instead of yielding `undefined`. That is the very case this function exists to handle.
|
|
18
|
+
const existingProcess = globalThis.process;
|
|
19
|
+
|
|
20
|
+
if (!existingProcess) {
|
|
21
|
+
// eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
|
|
22
|
+
globalThis.process = browserProcess;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (existingProcess.versions?.node) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const existingProcessRecord = existingProcess;
|
|
31
|
+
|
|
32
|
+
for (const [key, value] of Object.entries(browserProcess)) {
|
|
33
|
+
existingProcessRecord[key] ??= value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
existingProcessRecord['browser'] = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function keepName($unknown) {
|
|
40
|
+
return $unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
(function initCjs() {
|
|
44
|
+
// eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
|
|
45
|
+
const globalThisRecord = globalThis;
|
|
46
|
+
globalThisRecord['__name'] ??= keepName;
|
|
47
|
+
const originalRequire = require;
|
|
48
|
+
if (originalRequire && !originalRequire.__isPatched) {
|
|
49
|
+
// eslint-disable-next-line no-global-assign, no-implicit-globals -- We need to patch the `require()` function.
|
|
50
|
+
require = Object.assign(
|
|
51
|
+
(id) => requirePatched(id),
|
|
52
|
+
originalRequire,
|
|
53
|
+
{
|
|
54
|
+
__isPatched: true
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
globalThisRecord['__extractDefault'] ??= extractDefault;
|
|
60
|
+
|
|
61
|
+
ensureBrowserProcess();
|
|
62
|
+
|
|
63
|
+
function extractDefault(module) {
|
|
64
|
+
return module && module.__esModule && 'default' in module ? module.default : module;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const OBSIDIAN_BUILT_IN_MODULE_NAMES = new Set([
|
|
68
|
+
'@codemirror/autocomplete',
|
|
69
|
+
'@codemirror/collab',
|
|
70
|
+
'@codemirror/commands',
|
|
71
|
+
'@codemirror/language',
|
|
72
|
+
'@codemirror/lint',
|
|
73
|
+
'@codemirror/search',
|
|
74
|
+
'@codemirror/state',
|
|
75
|
+
'@codemirror/text',
|
|
76
|
+
'@codemirror/view',
|
|
77
|
+
'@lezer/common',
|
|
78
|
+
'@lezer/highlight',
|
|
79
|
+
'@lezer/lr',
|
|
80
|
+
'obsidian']
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES = new Set([
|
|
84
|
+
'@codemirror/closebrackets',
|
|
85
|
+
'@codemirror/comment',
|
|
86
|
+
'@codemirror/fold',
|
|
87
|
+
'@codemirror/gutter',
|
|
88
|
+
'@codemirror/highlight',
|
|
89
|
+
'@codemirror/history',
|
|
90
|
+
'@codemirror/matchbrackets',
|
|
91
|
+
'@codemirror/panel',
|
|
92
|
+
'@codemirror/rangeset',
|
|
93
|
+
'@codemirror/rectangular-selection',
|
|
94
|
+
'@codemirror/stream-parser',
|
|
95
|
+
'@codemirror/tooltip']
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
function requirePatched(id) {
|
|
99
|
+
if (OBSIDIAN_BUILT_IN_MODULE_NAMES.has(id) || DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES.has(id)) {
|
|
100
|
+
return originalRequire?.(id);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated, obsidianmd/no-global-this -- Need access to app. Actively use globalThis.
|
|
104
|
+
if (globalThis.app.isMobile) {
|
|
105
|
+
if (id === 'process' || id === 'node:process') {
|
|
106
|
+
// eslint-disable-next-line no-console -- Valid usage.
|
|
107
|
+
console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Fake process object is returned instead.`);
|
|
108
|
+
|
|
109
|
+
return process;
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
const module = originalRequire?.(id);
|
|
113
|
+
if (module) {
|
|
114
|
+
return extractDefault(module);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// eslint-disable-next-line no-console -- Valid usage.
|
|
119
|
+
console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Empty object is returned instead.`);
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
})();
|
|
123
|
+
})();
|
|
124
|
+
|
|
125
|
+
"use strict";
|
|
126
|
+
var __defProp = Object.defineProperty;
|
|
127
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
128
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
129
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
130
|
+
var __export = (target, all) => {
|
|
131
|
+
for (var name in all)
|
|
132
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
133
|
+
};
|
|
134
|
+
var __copyProps = (to, from, except, desc) => {
|
|
135
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
136
|
+
for (let key of __getOwnPropNames(from))
|
|
137
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
138
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
139
|
+
}
|
|
140
|
+
return to;
|
|
141
|
+
};
|
|
142
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
143
|
+
var plugin_lifecycle_events_exports = {};
|
|
144
|
+
__export(plugin_lifecycle_events_exports, {
|
|
145
|
+
PLUGIN_LOADED_EVENT_NAME: () => PLUGIN_LOADED_EVENT_NAME,
|
|
146
|
+
PLUGIN_UNLOADED_EVENT_NAME: () => PLUGIN_UNLOADED_EVENT_NAME,
|
|
147
|
+
triggerPluginLifecycleEvent: () => triggerPluginLifecycleEvent
|
|
148
|
+
});
|
|
149
|
+
module.exports = __toCommonJS(plugin_lifecycle_events_exports);
|
|
150
|
+
const PLUGIN_LOADED_EVENT_NAME = "obsidian-dev-utils:plugin-loaded";
|
|
151
|
+
const PLUGIN_UNLOADED_EVENT_NAME = "obsidian-dev-utils:plugin-unloaded";
|
|
152
|
+
function triggerPluginLifecycleEvent(params) {
|
|
153
|
+
params.app.workspace.trigger(params.name, params.payload);
|
|
154
|
+
}
|
|
155
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
156
|
+
0 && (module.exports = {
|
|
157
|
+
PLUGIN_LOADED_EVENT_NAME,
|
|
158
|
+
PLUGIN_UNLOADED_EVENT_NAME,
|
|
159
|
+
triggerPluginLifecycleEvent
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vLi4vc3JjL29ic2lkaWFuL3BsdWdpbi9wbHVnaW4tbGlmZWN5Y2xlLWV2ZW50cy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBAZmlsZVxuICpcbiAqIFRoZSBsaWZlY3ljbGUgYnJvYWRjYXN0IGV2ZXJ5IHtAbGluayBQbHVnaW5CYXNlfSBtYWtlczogYG9ic2lkaWFuLWRldi11dGlsczpwbHVnaW4tbG9hZGVkYCB3aGVuIGl0IGhhc1xuICogZmluaXNoZWQgbG9hZGluZyBhbmQgYG9ic2lkaWFuLWRldi11dGlsczpwbHVnaW4tdW5sb2FkZWRgIHdoZW4gaXQgZ29lcyBhd2F5LlxuICpcbiAqIE9ic2lkaWFuIGdpdmVzIGEgcGx1Z2luIG5vIHdheSB0byBsZWFybiB0aGF0IEFOT1RIRVIgcGx1Z2luIHdhcyBlbmFibGVkIG9yIGRpc2FibGVkIFx1MjAxNCB0aGVyZSBpcyBubyBzdWNoXG4gKiBldmVudCBvbiBgYXBwLnBsdWdpbnNgLCBhbmQgYSBwbHVnaW4ncyBvd24gYEV2ZW50c2Agc291cmNlIGNhbm5vdCBoZWxwIGEgbGlzdGVuZXIgdGhhdCBkb2VzIG5vdCB5ZXQgaG9sZFxuICogdGhlIGluc3RhbmNlLiBTbyB0aGUgYnJvYWRjYXN0IGdvZXMgdGhyb3VnaCBgYXBwLndvcmtzcGFjZWAsIHdoaWNoIGlzIG9uZSBvYmplY3QgZXZlcnkgcGx1Z2luIGluIHRoZVxuICogdmF1bHQgY2FuIHJlYWNoLiBEZWxpYmVyYXRlbHkgTk9UIHRoZSBgZ2xvYmFsVGhpcy5fX29ic2lkaWFuRGV2VXRpbHNgIGJhZyB0aGUgcmVzdCBvZiB0aGUgbGlicmFyeSBzaGFyZXNcbiAqIGl0cyBzdGF0ZSB0aHJvdWdoOiBhIGxpc3RlbmVyIHRoZXJlIG5lZWRzIGl0cyBvd24gY29weSBvZiB0aGlzIGxpYnJhcnksIGFuZCB0aGVzZSBldmVudHMgYXJlIG1lYW50IHRvIGJlXG4gKiBjb25zdW1hYmxlIGJ5IGFueSBwbHVnaW4gYXQgYWxsLlxuICpcbiAqIFRoZSBuYW1lcyBhcmUgcGFzdCB0ZW5zZSBiZWNhdXNlIGEgYnJvYWRjYXN0IHN0YXRlcyBzb21ldGhpbmcgdGhhdCBoYXMgYWxyZWFkeSBoYXBwZW5lZC4gYGxvYWRlZGAgaW5cbiAqIHBhcnRpY3VsYXIgY2FycmllcyBhIGd1YXJhbnRlZTogaXQgaXMgdHJpZ2dlcmVkIG9ubHkgYWZ0ZXIgZXZlcnkgQVBJIHRoZSBwbHVnaW4gZGVjbGFyZXMgaGFzIGJlZW5cbiAqIHB1Ymxpc2hlZCwgc28gYSBsaXN0ZW5lciBtYXkgY2FsbCB0aGVtIGltbWVkaWF0ZWx5LlxuICpcbiAqIEJvdGggdGhlIGV2ZW50IG5hbWVzIGFuZCB7QGxpbmsgUGx1Z2luTGlmZWN5Y2xlRXZlbnRQYXlsb2FkfSBhcmUgYSBDUk9TUy1WRVJTSU9OIENPTlRSQUNULiBDb3BpZXMgb2ZcbiAqIHRoaXMgbGlicmFyeSBhdCBkaWZmZXJlbnQgdmVyc2lvbnMgcHVibGlzaCBhbmQgY29uc3VtZSB0aGVtIHNpZGUgYnkgc2lkZSBpbiBvbmUgdmF1bHQsIHNvIG5laXRoZXIgbWF5XG4gKiBjaGFuZ2UgaW5jb21wYXRpYmx5OiBwbGFpbiBkYXRhIG9ubHksIGFuZCBuZXcgcGF5bG9hZCBmaWVsZHMgb25seSBldmVyIGFkZGVkLlxuICovXG5cbmltcG9ydCB0eXBlIHsgQXBwIH0gZnJvbSAnb2JzaWRpYW4nO1xuXG4vKipcbiAqIFRoZSBuYW1lIG9mIGVpdGhlciBsaWZlY3ljbGUgZXZlbnQuXG4gKi9cbmV4cG9ydCB0eXBlIFBsdWdpbkxpZmVjeWNsZUV2ZW50TmFtZSA9IHR5cGVvZiBQTFVHSU5fTE9BREVEX0VWRU5UX05BTUUgfCB0eXBlb2YgUExVR0lOX1VOTE9BREVEX0VWRU5UX05BTUU7XG5cbi8qKlxuICogVGhlIHBheWxvYWQgb2Yge0BsaW5rIFBMVUdJTl9MT0FERURfRVZFTlRfTkFNRX0gYW5kIHtAbGluayBQTFVHSU5fVU5MT0FERURfRVZFTlRfTkFNRX0uXG4gKlxuICogUGxhaW4gZGF0YSBieSBkZXNpZ24gXHUyMDE0IG5vIGNsYXNzIGluc3RhbmNlcyBhbmQgbm8gdHlwZXMgb3duZWQgYnkgdGhpcyBsaWJyYXJ5IFx1MjAxNCBiZWNhdXNlIGl0IGNyb3NzZXNcbiAqIGJldHdlZW4gaW5kZXBlbmRlbnRseSBidW5kbGVkIGNvcGllcyBvZiBpdCwgYW5kIGJldHdlZW4gcGx1Z2lucyB0aGF0IGRvIG5vdCB1c2UgaXQgYXQgYWxsLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFBsdWdpbkxpZmVjeWNsZUV2ZW50UGF5bG9hZCB7XG4gIC8qKlxuICAgKiBUaGUgY29udHJhY3QgdmVyc2lvbnMgdGhlIHBsdWdpbiBwdWJsaXNoZWQsIGVtcHR5IHdoZW4gaXQgcHVibGlzaGVzIG5vIEFQSS5cbiAgICpcbiAgICogQW4gYXJyYXkgcmF0aGVyIHRoYW4gYSBzaW5nbGUgdmVyc2lvbiBiZWNhdXNlIGEgcHJvdmlkZXIgbWF5IHB1Ymxpc2ggc2V2ZXJhbCBjb250cmFjdCB2ZXJzaW9ucyBzaWRlIGJ5XG4gICAqIHNpZGUsIHNvIGNvbnN1bWVycyBwaW5uZWQgdG8gYW4gb2xkZXIgcmFuZ2Uga2VlcCB3b3JraW5nIGFjcm9zcyBhIGJyZWFraW5nIGNoYW5nZS5cbiAgICpcbiAgICogVGhlIEFQSSBvYmplY3RzIHRoZW1zZWx2ZXMgYXJlIGRlbGliZXJhdGVseSBOT1QgaGVyZS4gQSBoYW5kbGUgZGVsaXZlcmVkIGJ5IGEgb25lLXNob3QgZXZlbnQgaXMgYVxuICAgKiBwcm9iZTogaXQgYW5zd2VycyBcIm5vd1wiIGFuZCBuZXZlciBzYXlzIHdoZW4gXCJub3dcIiBjaGFuZ2VkLCBhbmQgb25lIHRoYXQgb3V0bGl2ZXMgdGhlIHByb3ZpZGVyIGlzXG4gICAqIGV4YWN0bHkgdGhlIHN0YWxlIGhhbmRsZSB0aGUgcGx1Z2luLWFwaSByZWdpc3RyeSdzIHJldm9jYWJsZSBoYW5kbGVzIGV4aXN0IHRvIHByZXZlbnQuIFJlYWNoIHRoZSBBUElcbiAgICogdGhyb3VnaCBgd2F0Y2hQbHVnaW5BcGlgIGluc3RlYWQsIHdob3NlIHJlZmVyZW5jZSBzdGF5cyBjb3JyZWN0IGFjcm9zcyB1bmxvYWQgYW5kIHJlLWVuYWJsZS5cbiAgICovXG4gIHJlYWRvbmx5IGFwaVZlcnNpb25zOiByZWFkb25seSBzdHJpbmdbXTtcblxuICAvKipcbiAgICogVGhlIGlkcyBvZiB0aGUgcGx1Z2lucyB0aGlzIG9uZSBkZWNsYXJlcyBhcyBtYW5kYXRvcnkgZGVwZW5kZW5jaWVzLCBlbXB0eSB3aGVuIGl0IGRlY2xhcmVzIG5vbmUuXG4gICAqXG4gICAqIFByZXNlbnQgc28gdGhlIHJlbGF0aW9uc2hpcCBjYW4gYmUgcmVhZCBmcm9tIHRoZSBPVEhFUiBlbmQ6IGEgcHJvdmlkZXIgaGFzIG5vIHdheSB0byBhc2sgd2hvIGRlcGVuZHNcbiAgICogb24gaXQgXHUyMDE0IHRoZSByZWdpc3RyeSBvbmx5IGFuc3dlcnMgY29uc3VtZXItdG8tcHJvdmlkZXIgXHUyMDE0IGFuZCBcIndoaWNoIGluc3RhbGxlZCBwbHVnaW5zIG5lZWQgdGhpcyBvbmVcIlxuICAgKiBpcyB3aGF0IGxldHMgYSBwcm92aWRlcidzIHNldHRpbmdzIHRhYiB0ZWxsIHRoZSB1c2VyIHdoeSBpdCBpcyBpbiB0aGVpciB2YXVsdCBhdCBhbGwuXG4gICAqL1xuICByZWFkb25seSBkZXBlbmRlbmN5UGx1Z2luSWRzOiByZWFkb25seSBzdHJpbmdbXTtcblxuICAvKipcbiAgICogVGhlIHBsdWdpbidzIGBtYW5pZmVzdC5pZGAuXG4gICAqL1xuICByZWFkb25seSBwbHVnaW5JZDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgcGx1Z2luJ3MgYG1hbmlmZXN0Lm5hbWVgLCBmb3IgZGlzcGxheS5cbiAgICovXG4gIHJlYWRvbmx5IHBsdWdpbk5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHBsdWdpbidzIGBtYW5pZmVzdC52ZXJzaW9uYC5cbiAgICovXG4gIHJlYWRvbmx5IHBsdWdpblZlcnNpb246IHN0cmluZztcbn1cblxuLyoqXG4gKiBQYXJhbWV0ZXJzIGZvciB7QGxpbmsgdHJpZ2dlclBsdWdpbkxpZmVjeWNsZUV2ZW50fS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUcmlnZ2VyUGx1Z2luTGlmZWN5Y2xlRXZlbnRQYXJhbXMge1xuICAvKipcbiAgICogVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZSB3aG9zZSB3b3Jrc3BhY2UgY2FycmllcyB0aGUgYnJvYWRjYXN0LlxuICAgKi9cbiAgcmVhZG9ubHkgYXBwOiBBcHA7XG5cbiAgLyoqXG4gICAqIFRoZSBldmVudCB0byB0cmlnZ2VyLlxuICAgKi9cbiAgcmVhZG9ubHkgbmFtZTogUGx1Z2luTGlmZWN5Y2xlRXZlbnROYW1lO1xuXG4gIC8qKlxuICAgKiBUaGUgcGF5bG9hZCBkZXNjcmliaW5nIHRoZSBwbHVnaW4uXG4gICAqL1xuICByZWFkb25seSBwYXlsb2FkOiBQbHVnaW5MaWZlY3ljbGVFdmVudFBheWxvYWQ7XG59XG5cbi8qKlxuICogVHJpZ2dlcmVkIG9uY2UgYSBwbHVnaW4gaGFzIGZpbmlzaGVkIGxvYWRpbmcgQU5EIHB1Ymxpc2hlZCBldmVyeSBBUEkgaXQgZGVjbGFyZXMsIHNvIGEgbGlzdGVuZXIgbWF5IGNhbGxcbiAqIHRob3NlIEFQSXMgaW1tZWRpYXRlbHkuXG4gKlxuICogTmFtZXNwYWNlZCBieSB0aGUgcGFja2FnZSBuYW1lIHJhdGhlciB0aGFuIGJ5IGFuIGFiYnJldmlhdGlvbiBvZiBpdDogdGhlIGV2ZW50IGlzIGdsb2JhbCB0byB0aGUgdmF1bHRcbiAqIGFuZCBhaW1lZCBhdCBwbHVnaW4gYXV0aG9ycyB3aG8gaGF2ZSBuZXZlciBoZWFyZCBvZiB0aGlzIGxpYnJhcnksIHNvIHRoZSBwcmVmaXggaGFzIHRvIGlkZW50aWZ5IGl0c2VsZi5cbiAqL1xuZXhwb3J0IGNvbnN0IFBMVUdJTl9MT0FERURfRVZFTlRfTkFNRSA9ICdvYnNpZGlhbi1kZXYtdXRpbHM6cGx1Z2luLWxvYWRlZCc7XG5cbi8qKlxuICogVHJpZ2dlcmVkIGFzIGEgcGx1Z2luIHVubG9hZHMgXHUyMDE0IHdoZXRoZXIgdGhlIHVzZXIgZGlzYWJsZWQgaXQsIHVuaW5zdGFsbGVkIGl0LCBvciBPYnNpZGlhbiBpcyBzaHV0dGluZ1xuICogZG93bi4gSXRzIEFQSXMgYXJlIHJldm9rZWQgYnkgdGhlIHRpbWUgYSBsaXN0ZW5lciBydW5zLlxuICovXG5leHBvcnQgY29uc3QgUExVR0lOX1VOTE9BREVEX0VWRU5UX05BTUUgPSAnb2JzaWRpYW4tZGV2LXV0aWxzOnBsdWdpbi11bmxvYWRlZCc7XG5cbmRlY2xhcmUgbW9kdWxlICdvYnNpZGlhbicge1xuICBpbnRlcmZhY2UgV29ya3NwYWNlIHtcbiAgICAvKipcbiAgICAgKiBTdWJzY3JpYmVzIHRvIGEgcGx1Z2luIGZpbmlzaGluZyBpdHMgbG9hZCwgb3IgdG8gb25lIHVubG9hZGluZy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBuYW1lIC0gU2hvdWxkIGJlIHtAbGluayBQTFVHSU5fTE9BREVEX0VWRU5UX05BTUV9IG9yIHtAbGluayBQTFVHSU5fVU5MT0FERURfRVZFTlRfTkFNRX0uXG4gICAgICogQHBhcmFtIGNhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIHJlY2VpdmluZyB0aGUgcGx1Z2luJ3MgcGF5bG9hZC5cbiAgICAgKiBAcGFyYW0gY29udGV4dCAtIFRoZSBjb250ZXh0IHBhc3NlZCBhcyBgdGhpc2AgdG8gdGhlIGBjYWxsYmFja2AgZnVuY3Rpb24uXG4gICAgICogQHJldHVybnMgVGhlIGV2ZW50IHJlZmVyZW5jZS5cbiAgICAgKi9cbiAgICBvbihuYW1lOiBQbHVnaW5MaWZlY3ljbGVFdmVudE5hbWUsIGNhbGxiYWNrOiAocGF5bG9hZDogUGx1Z2luTGlmZWN5Y2xlRXZlbnRQYXlsb2FkKSA9PiB1bmtub3duLCBjb250ZXh0PzogdW5rbm93bik6IEV2ZW50UmVmO1xuICB9XG59XG5cbi8qKlxuICogVHJpZ2dlcnMgb25lIG9mIHRoZSB0d28gbGlmZWN5Y2xlIGV2ZW50cyBvbiB0aGUgYXBwJ3Mgd29ya3NwYWNlLlxuICpcbiAqIEEgdGhpbiB0eXBlZCB3cmFwcGVyIG92ZXIgYFdvcmtzcGFjZS50cmlnZ2VyYCwgd2hpY2ggYWNjZXB0cyBhbnkgZXZlbnQgbmFtZSBhbmQgYW55IGFyZ3VtZW50cywgc28gdGhhdFxuICogdGhlIG9uZSBwbGFjZSBhIHBheWxvYWQgaXMgY29uc3RydWN0ZWQgaXMgY2hlY2tlZCBhZ2FpbnN0IHtAbGluayBQbHVnaW5MaWZlY3ljbGVFdmVudFBheWxvYWR9LlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgLSBUaGUge0BsaW5rIFRyaWdnZXJQbHVnaW5MaWZlY3ljbGVFdmVudFBhcmFtc30uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cmlnZ2VyUGx1Z2luTGlmZWN5Y2xlRXZlbnQocGFyYW1zOiBUcmlnZ2VyUGx1Z2luTGlmZWN5Y2xlRXZlbnRQYXJhbXMpOiB2b2lkIHtcbiAgcGFyYW1zLmFwcC53b3Jrc3BhY2UudHJpZ2dlcihwYXJhbXMubmFtZSwgcGFyYW1zLnBheWxvYWQpO1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBcUdPLE1BQU0sMkJBQTJCO0FBTWpDLE1BQU0sNkJBQTZCO0FBd0JuQyxTQUFTLDRCQUE0QixRQUFpRDtBQUMzRixTQUFPLElBQUksVUFBVSxRQUFRLE9BQU8sTUFBTSxPQUFPLE9BQU87QUFDMUQ7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/// <reference path="../../library.d.cts" />
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
*
|
|
5
|
+
* The lifecycle broadcast every {@link PluginBase} makes: `obsidian-dev-utils:plugin-loaded` when it has
|
|
6
|
+
* finished loading and `obsidian-dev-utils:plugin-unloaded` when it goes away.
|
|
7
|
+
*
|
|
8
|
+
* Obsidian gives a plugin no way to learn that ANOTHER plugin was enabled or disabled — there is no such
|
|
9
|
+
* event on `app.plugins`, and a plugin's own `Events` source cannot help a listener that does not yet hold
|
|
10
|
+
* the instance. So the broadcast goes through `app.workspace`, which is one object every plugin in the
|
|
11
|
+
* vault can reach. Deliberately NOT the `globalThis.__obsidianDevUtils` bag the rest of the library shares
|
|
12
|
+
* its state through: a listener there needs its own copy of this library, and these events are meant to be
|
|
13
|
+
* consumable by any plugin at all.
|
|
14
|
+
*
|
|
15
|
+
* The names are past tense because a broadcast states something that has already happened. `loaded` in
|
|
16
|
+
* particular carries a guarantee: it is triggered only after every API the plugin declares has been
|
|
17
|
+
* published, so a listener may call them immediately.
|
|
18
|
+
*
|
|
19
|
+
* Both the event names and {@link PluginLifecycleEventPayload} are a CROSS-VERSION CONTRACT. Copies of
|
|
20
|
+
* this library at different versions publish and consume them side by side in one vault, so neither may
|
|
21
|
+
* change incompatibly: plain data only, and new payload fields only ever added.
|
|
22
|
+
*/
|
|
23
|
+
import type { App } from 'obsidian' with { 'resolution-mode': 'import' };
|
|
24
|
+
/**
|
|
25
|
+
* The name of either lifecycle event.
|
|
26
|
+
*/
|
|
27
|
+
export type PluginLifecycleEventName = typeof PLUGIN_LOADED_EVENT_NAME | typeof PLUGIN_UNLOADED_EVENT_NAME;
|
|
28
|
+
/**
|
|
29
|
+
* The payload of {@link PLUGIN_LOADED_EVENT_NAME} and {@link PLUGIN_UNLOADED_EVENT_NAME}.
|
|
30
|
+
*
|
|
31
|
+
* Plain data by design — no class instances and no types owned by this library — because it crosses
|
|
32
|
+
* between independently bundled copies of it, and between plugins that do not use it at all.
|
|
33
|
+
*/
|
|
34
|
+
export interface PluginLifecycleEventPayload {
|
|
35
|
+
/**
|
|
36
|
+
* The contract versions the plugin published, empty when it publishes no API.
|
|
37
|
+
*
|
|
38
|
+
* An array rather than a single version because a provider may publish several contract versions side by
|
|
39
|
+
* side, so consumers pinned to an older range keep working across a breaking change.
|
|
40
|
+
*
|
|
41
|
+
* The API objects themselves are deliberately NOT here. A handle delivered by a one-shot event is a
|
|
42
|
+
* probe: it answers "now" and never says when "now" changed, and one that outlives the provider is
|
|
43
|
+
* exactly the stale handle the plugin-api registry's revocable handles exist to prevent. Reach the API
|
|
44
|
+
* through `watchPluginApi` instead, whose reference stays correct across unload and re-enable.
|
|
45
|
+
*/
|
|
46
|
+
readonly apiVersions: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* The ids of the plugins this one declares as mandatory dependencies, empty when it declares none.
|
|
49
|
+
*
|
|
50
|
+
* Present so the relationship can be read from the OTHER end: a provider has no way to ask who depends
|
|
51
|
+
* on it — the registry only answers consumer-to-provider — and "which installed plugins need this one"
|
|
52
|
+
* is what lets a provider's settings tab tell the user why it is in their vault at all.
|
|
53
|
+
*/
|
|
54
|
+
readonly dependencyPluginIds: readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* The plugin's `manifest.id`.
|
|
57
|
+
*/
|
|
58
|
+
readonly pluginId: string;
|
|
59
|
+
/**
|
|
60
|
+
* The plugin's `manifest.name`, for display.
|
|
61
|
+
*/
|
|
62
|
+
readonly pluginName: string;
|
|
63
|
+
/**
|
|
64
|
+
* The plugin's `manifest.version`.
|
|
65
|
+
*/
|
|
66
|
+
readonly pluginVersion: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parameters for {@link triggerPluginLifecycleEvent}.
|
|
70
|
+
*/
|
|
71
|
+
export interface TriggerPluginLifecycleEventParams {
|
|
72
|
+
/**
|
|
73
|
+
* The Obsidian app instance whose workspace carries the broadcast.
|
|
74
|
+
*/
|
|
75
|
+
readonly app: App;
|
|
76
|
+
/**
|
|
77
|
+
* The event to trigger.
|
|
78
|
+
*/
|
|
79
|
+
readonly name: PluginLifecycleEventName;
|
|
80
|
+
/**
|
|
81
|
+
* The payload describing the plugin.
|
|
82
|
+
*/
|
|
83
|
+
readonly payload: PluginLifecycleEventPayload;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Triggered once a plugin has finished loading AND published every API it declares, so a listener may call
|
|
87
|
+
* those APIs immediately.
|
|
88
|
+
*
|
|
89
|
+
* Namespaced by the package name rather than by an abbreviation of it: the event is global to the vault
|
|
90
|
+
* and aimed at plugin authors who have never heard of this library, so the prefix has to identify itself.
|
|
91
|
+
*/
|
|
92
|
+
export declare const PLUGIN_LOADED_EVENT_NAME = "obsidian-dev-utils:plugin-loaded";
|
|
93
|
+
/**
|
|
94
|
+
* Triggered as a plugin unloads — whether the user disabled it, uninstalled it, or Obsidian is shutting
|
|
95
|
+
* down. Its APIs are revoked by the time a listener runs.
|
|
96
|
+
*/
|
|
97
|
+
export declare const PLUGIN_UNLOADED_EVENT_NAME = "obsidian-dev-utils:plugin-unloaded";
|
|
98
|
+
declare module 'obsidian' {
|
|
99
|
+
interface Workspace {
|
|
100
|
+
/**
|
|
101
|
+
* Subscribes to a plugin finishing its load, or to one unloading.
|
|
102
|
+
*
|
|
103
|
+
* @param name - Should be {@link PLUGIN_LOADED_EVENT_NAME} or {@link PLUGIN_UNLOADED_EVENT_NAME}.
|
|
104
|
+
* @param callback - The callback receiving the plugin's payload.
|
|
105
|
+
* @param context - The context passed as `this` to the `callback` function.
|
|
106
|
+
* @returns The event reference.
|
|
107
|
+
*/
|
|
108
|
+
on(name: PluginLifecycleEventName, callback: (payload: PluginLifecycleEventPayload) => unknown, context?: unknown): EventRef;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Triggers one of the two lifecycle events on the app's workspace.
|
|
113
|
+
*
|
|
114
|
+
* A thin typed wrapper over `Workspace.trigger`, which accepts any event name and any arguments, so that
|
|
115
|
+
* the one place a payload is constructed is checked against {@link PluginLifecycleEventPayload}.
|
|
116
|
+
*
|
|
117
|
+
* @param params - The {@link TriggerPluginLifecycleEventParams}.
|
|
118
|
+
*/
|
|
119
|
+
export declare function triggerPluginLifecycleEvent(params: TriggerPluginLifecycleEventParams): void;
|