@dolphy-app/extension-sdk 0.4.0 → 0.6.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 +28 -14
- package/dist/client.d.ts +40 -0
- package/dist/client.js +67 -0
- package/dist/define-entry-BTz2F3qv.js +22 -0
- package/dist/define-entry-lsuxzKdD.d.ts +77 -0
- package/dist/index-I_CXC6Lj.d.ts +1958 -0
- package/dist/index.d.ts +7 -111
- package/dist/index.js +5 -86
- package/dist/react.d.ts +51 -0
- package/dist/react.js +112 -0
- package/dist/rpc.d.ts +28 -0
- package/dist/rpc.js +37 -0
- package/dist/testing.d.ts +184 -241
- package/dist/testing.js +574 -464
- package/docs/debugging.md +82 -64
- package/docs/no-build.md +84 -48
- package/docs/quick-start.md +78 -56
- package/docs/recipe-command-panel.md +265 -118
- package/docs/recipe-event-storage.md +188 -128
- package/docs/recipe-exercise-type.md +271 -183
- package/docs/recipe-hooks.md +158 -0
- package/docs/recipe-import-export.md +48 -53
- package/docs/recipe-mountable.md +322 -0
- package/docs/recipe-react.md +352 -0
- package/docs/recipe-rpc-and-app.md +366 -0
- package/docs/recipe-settings.md +122 -100
- package/docs/recipe-theme.md +72 -40
- package/docs/recipe-when-dependencies.md +188 -82
- package/package.json +29 -7
- package/dist/answer-element-BOQcuxYh.js +0 -114
- package/dist/answer-view-D6wnyThb.d.ts +0 -28
- package/dist/runtime.d.ts +0 -17
- package/dist/runtime.js +0 -24
- package/docs/recipe-ui-kit.md +0 -172
package/README.md
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
# @dolphy-app/extension-sdk
|
|
2
2
|
|
|
3
|
-
SDK for extension authors:
|
|
4
|
-
|
|
5
|
-
The package version equals the version of the Dolphy app release it was published from (0.
|
|
6
|
-
|
|
7
|
-
`@dolphy-app/extension-sdk` — extension code
|
|
8
|
-
`
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
SDK for extension authors: defineServer, defineClient, defineRpc, usePanel, useInjection, useApp, useEngine, useRpc, defineMountable, callRpc, reactComponent, test harness
|
|
4
|
+
|
|
5
|
+
The package version equals the version of the Dolphy app release it was published from (0.6.0).
|
|
6
|
+
|
|
7
|
+
`@dolphy-app/extension-sdk` — extension code: `defineServer` and
|
|
8
|
+
`defineClient` describe the `server` and `client` exports of an extension
|
|
9
|
+
`src/index.ts`, `defineRpc` declares a call from a component to the server
|
|
10
|
+
part. Components of the client part use `useApp`, `useEngine`, `useRpc`,
|
|
11
|
+
`usePanel` and `useInjection` (`@dolphy-app/extension-sdk/client`; `vue` is a
|
|
12
|
+
peer dependency). A component is a Vue component (single-file `.vue`
|
|
13
|
+
components included) or a `Mountable` that `defineMountable` builds and that
|
|
14
|
+
draws with any framework; `reactComponent` and the React hooks
|
|
15
|
+
(`@dolphy-app/extension-sdk/react`) draw a React component, `react` and
|
|
16
|
+
`react-dom` being optional peer dependencies. `createTestServer`,
|
|
17
|
+
`createTestClient` and `mountForTest` (`@dolphy-app/extension-sdk/testing`)
|
|
18
|
+
run an extension against in-memory implementations of the app. The package
|
|
19
|
+
has no side effects: an extension `src/index.ts` can be imported in plain
|
|
20
|
+
Node.
|
|
13
21
|
|
|
14
22
|
```ts
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
23
|
+
import { defineClient, defineServer } from '@dolphy-app/extension-sdk';
|
|
24
|
+
import { defineRpc } from '@dolphy-app/extension-sdk/rpc';
|
|
25
|
+
import { useApp, useRpc } from '@dolphy-app/extension-sdk/client';
|
|
26
|
+
import { reactComponent } from '@dolphy-app/extension-sdk/react';
|
|
27
|
+
import { createTestServer } from '@dolphy-app/extension-sdk/testing';
|
|
17
28
|
```
|
|
18
29
|
|
|
19
30
|
The package ships a guide in `docs/` (`node_modules/@dolphy-app/extension-sdk/docs/`
|
|
@@ -22,11 +33,14 @@ after the install): a
|
|
|
22
33
|
[an exercise type](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-exercise-type.md),
|
|
23
34
|
[a theme](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-theme.md),
|
|
24
35
|
[a command and a panel](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-command-panel.md),
|
|
36
|
+
[a panel in React](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-react.md),
|
|
37
|
+
[a component of any framework](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-mountable.md),
|
|
25
38
|
[events and storage](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-event-storage.md),
|
|
39
|
+
[hooks before a session and a batch](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-hooks.md),
|
|
40
|
+
[calls between the parts, the engine and the window](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-rpc-and-app.md),
|
|
26
41
|
[settings](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-settings.md),
|
|
27
42
|
[an importer and an exporter](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-import-export.md),
|
|
28
|
-
[visibility conditions and dependencies](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-when-dependencies.md)
|
|
29
|
-
[a panel on the UI kit](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-ui-kit.md), a path
|
|
43
|
+
[visibility conditions and dependencies](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/recipe-when-dependencies.md), a path
|
|
30
44
|
[without a build](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/no-build.md) and notes on
|
|
31
45
|
[debugging](https://github.com/dolphy-app/dolphy/blob/main/packages/extension-sdk/docs/debugging.md).
|
|
32
46
|
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { t as ExtensionEngine } from "./index-I_CXC6Lj.js";
|
|
2
|
+
import { t as AppApi$1 } from "./define-entry-lsuxzKdD.js";
|
|
3
|
+
import { InjectionHandle, PanelHandle, RpcContract } from "@dolphy-app/extension-api";
|
|
4
|
+
//#region packages/extension-sdk/src/client.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The handle of the panel the component is drawn for: its id, the reactive
|
|
7
|
+
* `props` it was opened with, the reactive `context` and `call` for the
|
|
8
|
+
* commands of this extension. Only inside a panel component of the app.
|
|
9
|
+
*/
|
|
10
|
+
export declare const usePanel: <Commands extends string = string>() => PanelHandle<Commands>;
|
|
11
|
+
/**
|
|
12
|
+
* The handle of the injected component: the `target` element it is drawn at
|
|
13
|
+
* and the `position` relative to it. Only inside a component that
|
|
14
|
+
* `client.addInjection` registered.
|
|
15
|
+
*/
|
|
16
|
+
export declare const useInjection: () => InjectionHandle;
|
|
17
|
+
/**
|
|
18
|
+
* What the window lets an extension do: open a course, a lesson, an exercise,
|
|
19
|
+
* a panel or settings, show a toast, read the theme and the language, run a
|
|
20
|
+
* palette command, mount a component into an element. `theme` and `locale`
|
|
21
|
+
* are reactive. Only inside a component that the app draws.
|
|
22
|
+
*/
|
|
23
|
+
export declare const useApp: () => AppApi$1;
|
|
24
|
+
/**
|
|
25
|
+
* The engine client of the window: every method of the engine contract,
|
|
26
|
+
* writing ones included, and `subscribe` for the engine events. Only inside a
|
|
27
|
+
* component that the app draws.
|
|
28
|
+
*/
|
|
29
|
+
export declare const useEngine: () => ExtensionEngine;
|
|
30
|
+
/**
|
|
31
|
+
* Binds the contract to the server part of this extension: the returned
|
|
32
|
+
* function validates the input with `contract.input`, calls the handler of
|
|
33
|
+
* `server.handle` and validates the answer with `contract.output`. A schema
|
|
34
|
+
* violation, an error of the handler and an unavailable server reject the
|
|
35
|
+
* promise with an `Error` that carries the message. Call it in `setup`: it
|
|
36
|
+
* reads the extension id and the engine from the component's context. Only
|
|
37
|
+
* inside a component of an extension that the app draws.
|
|
38
|
+
*/
|
|
39
|
+
export declare const useRpc: <Input, Output>(contract: RpcContract<Input, Output>) => ((input: Input) => Promise<Output>);
|
|
40
|
+
//#endregion
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { callRpc } from "./rpc.js";
|
|
2
|
+
import { APP_KEY, ENGINE_KEY, EXTENSION_ID_KEY, INJECTION_HANDLE_KEY, PANEL_HANDLE_KEY } from "@dolphy-app/extension-api";
|
|
3
|
+
import { inject } from "vue";
|
|
4
|
+
|
|
5
|
+
//#region packages/extension-sdk/src/client.ts
|
|
6
|
+
/**
|
|
7
|
+
* The handle of the panel the component is drawn for: its id, the reactive
|
|
8
|
+
* `props` it was opened with, the reactive `context` and `call` for the
|
|
9
|
+
* commands of this extension. Only inside a panel component of the app.
|
|
10
|
+
*/
|
|
11
|
+
const usePanel = () => {
|
|
12
|
+
const handle = inject(PANEL_HANDLE_KEY, null);
|
|
13
|
+
if (handle === null) throw new Error("usePanel() works inside a panel component that the app draws, there is no panel here");
|
|
14
|
+
return handle;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The handle of the injected component: the `target` element it is drawn at
|
|
18
|
+
* and the `position` relative to it. Only inside a component that
|
|
19
|
+
* `client.addInjection` registered.
|
|
20
|
+
*/
|
|
21
|
+
const useInjection = () => {
|
|
22
|
+
const handle = inject(INJECTION_HANDLE_KEY, null);
|
|
23
|
+
if (handle === null) throw new Error("useInjection() works inside an injected component that the app draws, there is no injection here");
|
|
24
|
+
return handle;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* What the window lets an extension do: open a course, a lesson, an exercise,
|
|
28
|
+
* a panel or settings, show a toast, read the theme and the language, run a
|
|
29
|
+
* palette command, mount a component into an element. `theme` and `locale`
|
|
30
|
+
* are reactive. Only inside a component that the app draws.
|
|
31
|
+
*/
|
|
32
|
+
const useApp = () => {
|
|
33
|
+
const app = inject(APP_KEY, null);
|
|
34
|
+
if (app === null) throw new Error("useApp() works inside a component that the app draws, there is no app here");
|
|
35
|
+
return app;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The engine client of the window: every method of the engine contract,
|
|
39
|
+
* writing ones included, and `subscribe` for the engine events. Only inside a
|
|
40
|
+
* component that the app draws.
|
|
41
|
+
*/
|
|
42
|
+
const useEngine = () => {
|
|
43
|
+
const engine = inject(ENGINE_KEY, null);
|
|
44
|
+
if (engine === null) throw new Error("useEngine() works inside a component that the app draws, there is no engine here");
|
|
45
|
+
return engine;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Binds the contract to the server part of this extension: the returned
|
|
49
|
+
* function validates the input with `contract.input`, calls the handler of
|
|
50
|
+
* `server.handle` and validates the answer with `contract.output`. A schema
|
|
51
|
+
* violation, an error of the handler and an unavailable server reject the
|
|
52
|
+
* promise with an `Error` that carries the message. Call it in `setup`: it
|
|
53
|
+
* reads the extension id and the engine from the component's context. Only
|
|
54
|
+
* inside a component of an extension that the app draws.
|
|
55
|
+
*/
|
|
56
|
+
const useRpc = (contract) => {
|
|
57
|
+
const extensionId = inject(EXTENSION_ID_KEY, null);
|
|
58
|
+
if (extensionId === null) throw new Error("useRpc() works inside a component of an extension that the app draws, there is no extension here");
|
|
59
|
+
const engine = useEngine();
|
|
60
|
+
return (input) => callRpc({
|
|
61
|
+
engine,
|
|
62
|
+
extensionId
|
|
63
|
+
}, contract, input);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { useApp, useEngine, useInjection, usePanel, useRpc };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MOUNTABLE } from "@dolphy-app/extension-api";
|
|
2
|
+
|
|
3
|
+
//#region packages/extension-sdk/src/define-entry.ts
|
|
4
|
+
/**
|
|
5
|
+
* `export const panel = defineMountable<PanelProps, PanelHandle>((el, ctx) => { … return () => … })`:
|
|
6
|
+
* a component of any framework. `mount` draws into `el` and returns the
|
|
7
|
+
* cleanup that the app calls when it removes the element. Returns an object
|
|
8
|
+
* that carries the brand `isMountable` recognises.
|
|
9
|
+
*/
|
|
10
|
+
const defineMountable = /* @__NO_SIDE_EFFECTS__ */ (mount) => ({
|
|
11
|
+
[MOUNTABLE]: true,
|
|
12
|
+
mount
|
|
13
|
+
});
|
|
14
|
+
/** `export const server = defineServer((server) => { … })`: registers the server contributions. Returns `entry` as is; it only checks the types. */
|
|
15
|
+
const defineServer = /* @__NO_SIDE_EFFECTS__ */ (entry) => entry;
|
|
16
|
+
/** `export const client = defineClient((client) => { … })`: registers the client contributions. Returns `entry` as is; it only checks the types. */
|
|
17
|
+
const defineClient = /* @__NO_SIDE_EFFECTS__ */ (entry) => entry;
|
|
18
|
+
/** An exercise type registration with `Spec`, `Answer` and `View` inferred from the handlers; pass it to `server.registerExerciseType`. */
|
|
19
|
+
const defineExerciseType = /* @__NO_SIDE_EFFECTS__ */ (registration) => registration;
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { defineServer as i, defineExerciseType as n, defineMountable as r, defineClient as t };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { t as ExtensionEngine } from "./index-I_CXC6Lj.js";
|
|
2
|
+
import { AnswerViewProps, AppApi, ClientContext, Disposable, EntryResult, ExerciseTypeRegistration, InjectionHandle, InjectionProps, InjectionRegistration, MOUNTABLE, MarkdownBlockProps, MountContext, PanelHandle, PanelProps, PanelRegistration, ServerContext, ServerEntry, SettingValues, Unmount } from "@dolphy-app/extension-api";
|
|
3
|
+
import { Component } from "vue";
|
|
4
|
+
//#region packages/extension-sdk/src/define-entry.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* What the host gives the server part of an extension: `ServerContext` of the
|
|
7
|
+
* API with `engine` typed as `ExtensionEngine`.
|
|
8
|
+
*/
|
|
9
|
+
type ServerContext$1<S extends SettingValues = SettingValues> = ServerContext<S, ExtensionEngine>;
|
|
10
|
+
/** `export const server` of `src/index.ts`: registers the server contributions; the result, if any, runs when the extension is unloaded. */
|
|
11
|
+
type ServerEntry$1 = ServerEntry<ExtensionEngine>;
|
|
12
|
+
/**
|
|
13
|
+
* The window capabilities of `useApp()` and `ClientContext.app`: `AppApi` of
|
|
14
|
+
* the API with the component of `mountAt` typed as a Vue component.
|
|
15
|
+
*/
|
|
16
|
+
interface AppApi$1 extends Omit<AppApi, 'mountAt'> {
|
|
17
|
+
/** Mounts a Vue component into an element of the window, see `AppApi.mountAt` of the API; the props are passed to the component as is. */
|
|
18
|
+
mountAt(target: Element | string, component: Component, props?: Readonly<Record<string, unknown>>): Disposable;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What a `Mountable` gets: `MountContext` of the API with `engine` typed as
|
|
22
|
+
* `ExtensionEngine` and `app` as `AppApi`. `Handle` is `PanelHandle` in a
|
|
23
|
+
* panel, `InjectionHandle` in an injection, `undefined` elsewhere.
|
|
24
|
+
*/
|
|
25
|
+
interface MountContext$1<Props = unknown, Handle = undefined> extends Omit<MountContext<Props, ExtensionEngine, Handle>, 'app'> {
|
|
26
|
+
readonly app: AppApi$1;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A component of any framework: `mount` draws into `el` and returns the
|
|
30
|
+
* cleanup (see `Mountable` of the API). Build it with `defineMountable`.
|
|
31
|
+
*/
|
|
32
|
+
interface Mountable<Props = unknown, Handle = undefined> {
|
|
33
|
+
readonly [MOUNTABLE]: true;
|
|
34
|
+
mount(el: HTMLElement, ctx: MountContext$1<Props, Handle>): Unmount | Promise<Unmount>;
|
|
35
|
+
}
|
|
36
|
+
/** A panel: an app screen drawn by a Vue component or a `Mountable` of the extension (`client.addPanel`). */
|
|
37
|
+
interface PanelRegistration$1 extends Omit<PanelRegistration, 'component'> {
|
|
38
|
+
/** Inside a Vue component `usePanel()` reaches the props and the commands, in a `Mountable` `ctx.handle` does. */
|
|
39
|
+
component: Component | Mountable<PanelProps, PanelHandle>;
|
|
40
|
+
}
|
|
41
|
+
/** A component drawn in the window at the elements that match `target` (`client.addInjection`). */
|
|
42
|
+
interface InjectionRegistration$1 extends Omit<InjectionRegistration, 'component'> {
|
|
43
|
+
/** Inside a Vue component `useInjection()` reaches the target element and the position, in a `Mountable` `ctx.handle` does. */
|
|
44
|
+
component: Component | Mountable<InjectionProps, InjectionHandle>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* What the window gives the client part of an extension: `ClientContext` of
|
|
48
|
+
* the API with the components typed as Vue components or `Mountable`s, `app`
|
|
49
|
+
* typed as `AppApi` and `engine` as `ExtensionEngine`.
|
|
50
|
+
*/
|
|
51
|
+
interface ClientContext$1 extends Omit<ClientContext<ExtensionEngine>, 'app' | 'addPanel' | 'addInjection' | 'addAnswerView' | 'addMarkdownRenderer'> {
|
|
52
|
+
/** The same object as `useApp()` in a component. */
|
|
53
|
+
readonly app: AppApi$1;
|
|
54
|
+
addPanel(reg: PanelRegistration$1): Disposable;
|
|
55
|
+
addInjection(reg: InjectionRegistration$1): Disposable;
|
|
56
|
+
/** The component takes the `AnswerViewProps` props and emits `change` (`AnswerChange`) and `submit`. */
|
|
57
|
+
addAnswerView(exerciseTypeId: string, component: Component | Mountable<AnswerViewProps>): Disposable;
|
|
58
|
+
/** The component takes the `MarkdownBlockProps` props. */
|
|
59
|
+
addMarkdownRenderer(language: string, component: Component | Mountable<MarkdownBlockProps>): Disposable;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* `export const panel = defineMountable<PanelProps, PanelHandle>((el, ctx) => { … return () => … })`:
|
|
63
|
+
* a component of any framework. `mount` draws into `el` and returns the
|
|
64
|
+
* cleanup that the app calls when it removes the element. Returns an object
|
|
65
|
+
* that carries the brand `isMountable` recognises.
|
|
66
|
+
*/
|
|
67
|
+
declare const defineMountable: <Props = unknown, Handle = undefined>(mount: Mountable<Props, Handle>["mount"]) => Mountable<Props, Handle>;
|
|
68
|
+
/** `export const client` of `src/index.ts`: registers the client contributions; the result, if any, runs when the extension is unloaded. */
|
|
69
|
+
type ClientEntry = (client: ClientContext$1) => EntryResult | Promise<EntryResult>;
|
|
70
|
+
/** `export const server = defineServer((server) => { … })`: registers the server contributions. Returns `entry` as is; it only checks the types. */
|
|
71
|
+
declare const defineServer: (entry: ServerEntry$1) => ServerEntry$1;
|
|
72
|
+
/** `export const client = defineClient((client) => { … })`: registers the client contributions. Returns `entry` as is; it only checks the types. */
|
|
73
|
+
declare const defineClient: (entry: ClientEntry) => ClientEntry;
|
|
74
|
+
/** An exercise type registration with `Spec`, `Answer` and `View` inferred from the handlers; pass it to `server.registerExerciseType`. */
|
|
75
|
+
declare const defineExerciseType: <Spec, Answer, View>(registration: ExerciseTypeRegistration<Spec, Answer, View>) => ExerciseTypeRegistration<Spec, Answer, View>;
|
|
76
|
+
//#endregion
|
|
77
|
+
export { MountContext$1 as a, ServerContext$1 as c, defineExerciseType as d, defineMountable as f, InjectionRegistration$1 as i, ServerEntry$1 as l, ClientContext$1 as n, Mountable as o, defineServer as p, ClientEntry as r, PanelRegistration$1 as s, AppApi$1 as t, defineClient as u };
|