Skip to content

Commit 7b75696

Browse files
committed
add validation to API methods
1 parent 70a8eda commit 7b75696

17 files changed

+497
-74
lines changed

bun.lockb

328 Bytes
Binary file not shown.

exampleVault/Test.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,8 @@ el.addEventListener("click", () => {
227227

228228
```js-engine
229229
le)
230+
```
231+
232+
```js-engine
233+
engine.getPlugin(false)
230234
```

jsEngine/api/API.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import { QueryAPI } from 'jsEngine/api/QueryAPI';
88
import { ReactiveComponent } from 'jsEngine/api/reactive/ReactiveComponent';
99
import type { JsFunc } from 'jsEngine/engine/JsExecution';
1010
import type JsEnginePlugin from 'jsEngine/main';
11+
import type { Validators } from 'jsEngine/utils/Validators';
12+
import { validateAPIArgs } from 'jsEngine/utils/Validators';
1113
import type { App, Plugin } from 'obsidian';
14+
import { z } from 'zod';
1215

1316
export class API {
1417
/**
@@ -20,6 +23,7 @@ export class API {
2023
*/
2124
readonly plugin: JsEnginePlugin;
2225
readonly instanceId: InstanceId;
26+
readonly validators: Validators;
2327
/**
2428
* API to interact with markdown.
2529
*/
@@ -46,6 +50,7 @@ export class API {
4650
this.app = app;
4751
this.plugin = plugin;
4852
this.instanceId = instanceId;
53+
this.validators = plugin.validators;
4954

5055
this.markdown = new MarkdownAPI(this);
5156
this.message = new MessageAPI(this);
@@ -65,6 +70,8 @@ export class API {
6570
* @param path the vault relative path of the file to import
6671
*/
6772
public async importJs(path: string): Promise<unknown> {
73+
validateAPIArgs(z.object({ path: z.string() }), { path });
74+
6875
let fullPath = this.app.vault.adapter.getResourcePath(path);
6976

7077
// we need to remove the query parameters from the path
@@ -83,6 +90,8 @@ export class API {
8390
* @param pluginId the id of the plugin.
8491
*/
8592
public getPlugin(pluginId: string): Plugin | undefined {
93+
validateAPIArgs(z.object({ pluginId: z.string() }), { pluginId });
94+
8695
return this.app.plugins.getPlugin(pluginId) ?? undefined;
8796
}
8897

@@ -94,6 +103,8 @@ export class API {
94103
* @param initialArgs the initial arguments (for the first render) to pass to the function.
95104
*/
96105
public reactive(fn: JsFunc, ...initialArgs: unknown[]): ReactiveComponent {
106+
validateAPIArgs(z.object({ fn: z.function(), initialArgs: z.array(z.unknown()) }), { fn, initialArgs });
107+
97108
return new ReactiveComponent(this, fn, initialArgs);
98109
}
99110
}

jsEngine/api/Internal.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import type { API } from 'jsEngine/api/API';
22
import type { EngineExecutionParams } from 'jsEngine/engine/Engine';
33
import type { JsExecution, JsExecutionContext, JsExecutionGlobals, JsExecutionGlobalsConstructionOptions } from 'jsEngine/engine/JsExecution';
44
import { ResultRenderer } from 'jsEngine/engine/ResultRenderer';
5+
import { validateAPIArgs } from 'jsEngine/utils/Validators';
56
import { Component, TFile } from 'obsidian';
67
import * as Obsidian from 'obsidian';
8+
import { z } from 'zod';
79

810
/**
911
* The internal API provides access to some of js engines internals.
@@ -21,6 +23,8 @@ export class InternalAPI {
2123
* @param params
2224
*/
2325
public async execute(params: EngineExecutionParams): Promise<JsExecution> {
26+
validateAPIArgs(z.object({ params: this.apiInstance.validators.engineExecutionParams }), { params });
27+
2428
return await this.apiInstance.plugin.jsEngine.execute(params);
2529
}
2630

@@ -32,6 +36,11 @@ export class InternalAPI {
3236
* @param component
3337
*/
3438
public createRenderer(container: HTMLElement, sourcePath: string, component: Component): ResultRenderer {
39+
validateAPIArgs(
40+
z.object({ container: this.apiInstance.validators.htmlElement, sourcePath: z.string(), component: this.apiInstance.validators.component }),
41+
{ container, sourcePath, component },
42+
);
43+
3544
return new ResultRenderer(this.apiInstance.plugin, container, sourcePath, component);
3645
}
3746

@@ -42,6 +51,8 @@ export class InternalAPI {
4251
* @param params
4352
*/
4453
public async executeFile(path: string, params: Omit<EngineExecutionParams, 'code'>): Promise<JsExecution> {
54+
validateAPIArgs(z.object({ path: z.string(), params: this.apiInstance.validators.engineExecutionParamsNoCode }), { path, params });
55+
4556
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
4657
if (!file || !(file instanceof TFile)) {
4758
throw new Error(`File ${path} not found.`);
@@ -60,6 +71,11 @@ export class InternalAPI {
6071
* @param params
6172
*/
6273
public async executeFileSimple(path: string, params?: Omit<EngineExecutionParams, 'code' | 'component'>): Promise<JsExecution> {
74+
validateAPIArgs(z.object({ path: z.string(), params: this.apiInstance.validators.engineExecutionParamsNoCodeAndComponent.optional() }), {
75+
path,
76+
params,
77+
});
78+
6379
const component = new Component();
6480
component.load();
6581
try {
@@ -75,6 +91,8 @@ export class InternalAPI {
7591
* @param path
7692
*/
7793
public async getContextForFile(path: string): Promise<JsExecutionContext> {
94+
validateAPIArgs(z.object({ path: z.string() }), { path });
95+
7896
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
7997
if (!file || !(file instanceof TFile)) {
8098
throw new Error(`File ${path} not found.`);
@@ -95,6 +113,8 @@ export class InternalAPI {
95113
* @param options
96114
*/
97115
public createExecutionGlobals(options: JsExecutionGlobalsConstructionOptions): JsExecutionGlobals {
116+
validateAPIArgs(z.object({ options: this.apiInstance.validators.jsExecutionGlobalsConstructionOptions }), { options });
117+
98118
return {
99119
app: this.apiInstance.app,
100120
engine: options.engine ?? this.apiInstance,

jsEngine/api/MarkdownAPI.ts

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
} from 'jsEngine/api/markdown/AbstractMarkdownElementContainer';
1313
import { MarkdownBuilder } from 'jsEngine/api/markdown/MarkdownBuilder';
1414
import { MarkdownString } from 'jsEngine/api/markdown/MarkdownString';
15+
import { validateAPIArgs } from 'jsEngine/utils/Validators';
16+
import { z } from 'zod';
1517

1618
/**
1719
* The markdown API provides utilities for creating markdown using js.
@@ -27,7 +29,7 @@ export class MarkdownAPI {
2729
* Creates a markdown builder.
2830
*/
2931
public createBuilder(): MarkdownBuilder {
30-
return new MarkdownBuilder();
32+
return new MarkdownBuilder(this.apiInstance);
3133
}
3234

3335
/**
@@ -38,7 +40,9 @@ export class MarkdownAPI {
3840
* @param markdown the string to wrap
3941
*/
4042
public create(markdown: string): MarkdownString {
41-
return new MarkdownString(markdown);
43+
validateAPIArgs(z.object({ markdown: z.string() }), { markdown });
44+
45+
return new MarkdownString(this.apiInstance, markdown);
4246
}
4347

4448
/**
@@ -47,7 +51,9 @@ export class MarkdownAPI {
4751
* @param text
4852
*/
4953
public createText(text: string): TextElement {
50-
return new TextElement(text, false, false, false);
54+
validateAPIArgs(z.object({ text: z.string() }), { text });
55+
56+
return new TextElement(this.apiInstance, text, false, false, false);
5157
}
5258

5359
/**
@@ -56,7 +62,9 @@ export class MarkdownAPI {
5662
* @param text
5763
*/
5864
public createBoldText(text: string): TextElement {
59-
return new TextElement(text, true, false, false);
65+
validateAPIArgs(z.object({ text: z.string() }), { text });
66+
67+
return new TextElement(this.apiInstance, text, true, false, false);
6068
}
6169

6270
/**
@@ -65,7 +73,9 @@ export class MarkdownAPI {
6573
* @param text
6674
*/
6775
public createCursiveText(text: string): TextElement {
68-
return new TextElement(text, false, true, false);
76+
validateAPIArgs(z.object({ text: z.string() }), { text });
77+
78+
return new TextElement(this.apiInstance, text, false, true, false);
6979
}
7080

7181
/**
@@ -74,7 +84,9 @@ export class MarkdownAPI {
7484
* @param text
7585
*/
7686
public createUnderlinedText(text: string): TextElement {
77-
return new TextElement(text, false, false, true);
87+
validateAPIArgs(z.object({ text: z.string() }), { text });
88+
89+
return new TextElement(this.apiInstance, text, false, false, true);
7890
}
7991

8092
/**
@@ -83,7 +95,9 @@ export class MarkdownAPI {
8395
* @param text
8496
*/
8597
public createCode(text: string): CodeElement {
86-
return new CodeElement(text);
98+
validateAPIArgs(z.object({ text: z.string() }), { text });
99+
100+
return new CodeElement(this.apiInstance, text);
87101
}
88102

89103
/**
@@ -92,7 +106,9 @@ export class MarkdownAPI {
92106
* @param content
93107
*/
94108
public createParagraph(content: string): ParagraphElement {
95-
return new ParagraphElement(content);
109+
validateAPIArgs(z.object({ content: z.string() }), { content });
110+
111+
return new ParagraphElement(this.apiInstance, content);
96112
}
97113

98114
/**
@@ -102,14 +118,16 @@ export class MarkdownAPI {
102118
* @param content the text of the heading
103119
*/
104120
public createHeading(level: number, content: string): HeadingElement {
105-
return new HeadingElement(level, content);
121+
validateAPIArgs(z.object({ level: z.number(), content: z.string() }), { level, content });
122+
123+
return new HeadingElement(this.apiInstance, level, content);
106124
}
107125

108126
/**
109127
* Creates a new markdown block quote element.
110128
*/
111129
public createBlockQuote(): BlockQuoteElement {
112-
return new BlockQuoteElement();
130+
return new BlockQuoteElement(this.apiInstance);
113131
}
114132

115133
/**
@@ -120,7 +138,23 @@ export class MarkdownAPI {
120138
* @param args the callout args, optional
121139
*/
122140
public createCallout(title: string, type: string, args: string = ''): CalloutElement {
123-
return new CalloutElement(title, type, args);
141+
validateAPIArgs(z.object({ title: z.string(), type: z.string(), args: z.string() }), { title, type, args });
142+
143+
return new CalloutElement(this.apiInstance, title, type, args);
144+
}
145+
146+
/**
147+
* Creates a new markdown collapsible callout element.
148+
*
149+
* @param title the title of the callout
150+
* @param type the type of the callout
151+
* @param args the callout args, optional
152+
* @param collapsed whether the callout should be collapsed by default, optional
153+
*/
154+
createCollapsibleCallout(title: string, type: string, args: string = '', collapsed: boolean = false): CalloutElement {
155+
validateAPIArgs(z.object({ title: z.string(), type: z.string(), args: z.string(), collapsed: z.boolean() }), { title, type, args, collapsed });
156+
157+
return new CalloutElement(this.apiInstance, title, type, args, true, collapsed);
124158
}
125159

126160
/**
@@ -130,7 +164,9 @@ export class MarkdownAPI {
130164
* @param content the content of the code block
131165
*/
132166
public createCodeBlock(language: string, content: string): CodeBlockElement {
133-
return new CodeBlockElement(language, content);
167+
validateAPIArgs(z.object({ language: z.string(), content: z.string() }), { language, content });
168+
169+
return new CodeBlockElement(this.apiInstance, language, content);
134170
}
135171

136172
/**
@@ -140,7 +176,9 @@ export class MarkdownAPI {
140176
* @param body the table body
141177
*/
142178
public createTable(header: string[], body: string[][]): TableElement {
143-
return new TableElement(header, body);
179+
validateAPIArgs(z.object({ header: z.array(z.string()), body: z.array(z.array(z.string())) }), { header, body });
180+
181+
return new TableElement(this.apiInstance, header, body);
144182
}
145183

146184
/**
@@ -149,6 +187,8 @@ export class MarkdownAPI {
149187
* @param ordered whether the list should be ordered or not (use 1. or -)
150188
*/
151189
createList(ordered: boolean): ListElement {
152-
return new ListElement(ordered);
190+
validateAPIArgs(z.object({ ordered: z.boolean() }), { ordered });
191+
192+
return new ListElement(this.apiInstance, ordered);
153193
}
154194
}

jsEngine/api/MessageAPI.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { API } from 'jsEngine/api/API';
22
import type { MessageManager, MessageType, MessageWrapper } from 'jsEngine/messages/MessageManager';
33
import { Message } from 'jsEngine/messages/MessageManager';
4+
import { validateAPIArgs } from 'jsEngine/utils/Validators';
5+
import { z } from 'zod';
46

57
export class MessageAPI {
68
readonly apiInstance: API;
@@ -12,10 +14,19 @@ export class MessageAPI {
1214
}
1315

1416
public createMessage(type: MessageType, title: string, content: string, code: string = ''): MessageWrapper {
17+
validateAPIArgs(z.object({ type: this.apiInstance.validators.messageType, title: z.string(), content: z.string(), code: z.string() }), {
18+
type,
19+
title,
20+
content,
21+
code,
22+
});
23+
1524
return this.messageManager.addMessage(new Message(type, title, content, code), this.apiInstance.instanceId);
1625
}
1726

1827
public getMessageById(id: string): MessageWrapper | undefined {
28+
validateAPIArgs(z.object({ id: z.string() }), { id });
29+
1930
return this.messageManager.messages.get().find(message => message.uuid === id);
2031
}
2132

0 commit comments

Comments
 (0)