@dolphy-app/extension-sdk 0.3.0 → 0.4.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.
@@ -0,0 +1,161 @@
1
+ # Recipe: settings
2
+
3
+ Let the user configure the extension in Settings → Extensions. The app draws the
4
+ form from the definitions in the manifest, validates the values and stores them;
5
+ your code reads them. This recipe has no template of its own: start from
6
+ `blank` and replace the three files below, which are checked as a whole project.
7
+ See [quick-start.md](quick-start.md) for the commands.
8
+
9
+ ## The manifest
10
+
11
+ File `extension.json` (settings):
12
+
13
+ ```json
14
+ {
15
+ "$schema": "./node_modules/@dolphy-app/extension-api/dist/extension.schema.json",
16
+ "id": "acme.hello",
17
+ "version": "0.1.0",
18
+ "apiVersion": 1,
19
+ "name": "Hello settings",
20
+ "description": "A greeting command whose text follows the user's settings.",
21
+ "author": "your-github-login",
22
+ "tags": ["productivity"],
23
+ "contributes": {
24
+ "commands": [{ "id": "acme.hello.greet", "title": "Greet" }],
25
+ "settings": [
26
+ {
27
+ "id": "acme.hello.name",
28
+ "type": "string",
29
+ "label": "Name to greet",
30
+ "default": "world",
31
+ "maxLength": 40
32
+ },
33
+ {
34
+ "id": "acme.hello.times",
35
+ "type": "number",
36
+ "label": "Exclamation marks",
37
+ "default": 1,
38
+ "min": 1,
39
+ "max": 5,
40
+ "integer": true
41
+ },
42
+ {
43
+ "id": "acme.hello.style",
44
+ "type": "enum",
45
+ "label": "Style",
46
+ "default": "plain",
47
+ "options": [
48
+ { "value": "plain", "label": "Plain" },
49
+ { "value": "loud", "label": "Loud" }
50
+ ]
51
+ }
52
+ ]
53
+ }
54
+ }
55
+ ```
56
+
57
+ - Types: `boolean`, `string` (`maxLength`), `number` (`min`, `max`, `integer`)
58
+ and `enum` (`options: [{ value, label }]`).
59
+ - A setting `id` is the extension id or starts with `<id>.`. `default` must
60
+ satisfy the constraints, otherwise the manifest is rejected.
61
+ - `label` (1–60 characters) and `description` (up to 500) are shown as written;
62
+ they are data of the extension and are not translated.
63
+
64
+ ## The code
65
+
66
+ File `src/index.ts` (settings):
67
+
68
+ ```ts
69
+ import {
70
+ defineExtension,
71
+ inActivate,
72
+ notify,
73
+ } from '@dolphy-app/extension-sdk';
74
+
75
+ export const host = defineExtension({
76
+ commands: { 'acme.hello.greet': inActivate },
77
+ activate(ctx) {
78
+ ctx.commands.register('acme.hello.greet', () => {
79
+ // `get` is synchronous and returns the user's value or the default
80
+ const name = ctx.settings.get('acme.hello.name');
81
+ const marks = '!'.repeat(ctx.settings.get('acme.hello.times'));
82
+ const style = ctx.settings.get('acme.hello.style');
83
+ const text = `Hello, ${name}${marks}`;
84
+ return notify(style === 'loud' ? text.toUpperCase() : text);
85
+ });
86
+
87
+ // a change in Settings → Extensions reaches the running extension
88
+ ctx.settings.onDidChange((change) => {
89
+ ctx.logger.info({ id: change.id, value: change.value }, 'setting changed');
90
+ });
91
+ },
92
+ });
93
+ ```
94
+
95
+ - The ids are typed from the manifest: `ctx.settings.get('acme.hello.times')` is
96
+ a `number`, `style` is `'plain' | 'loud'`, an undeclared id does not compile.
97
+ - Read a value where you use it. Cache it only if you also subscribe with
98
+ `onDidChange`, as the `exercise` template does.
99
+ - The engine validates every value (type, range, integer, length, `options`)
100
+ before it reaches you, so the code needs no checks of its own.
101
+
102
+ ## The test
103
+
104
+ File `test/index.test.ts` (settings):
105
+
106
+ ```ts
107
+ import type { SettingContribution } from '@dolphy-app/extension-sdk';
108
+ import {
109
+ createMemorySettings,
110
+ loadCommands,
111
+ } from '@dolphy-app/extension-sdk/testing';
112
+ import { expect, it } from 'vitest';
113
+ import manifest from '../extension.json';
114
+ import { host } from '../src/index.ts';
115
+
116
+ const definitions = manifest.contributes.settings as SettingContribution[];
117
+
118
+ it('the greeting follows the settings, also after a change', async () => {
119
+ const settings = createMemorySettings(definitions);
120
+ const commands = await loadCommands(host, {
121
+ settings,
122
+ declaredCommands: ['acme.hello.greet'],
123
+ });
124
+
125
+ expect(await commands.run('acme.hello.greet')).toEqual({
126
+ kind: 'notify',
127
+ text: 'Hello, world!',
128
+ });
129
+
130
+ await settings.set('acme.hello.name', 'Ada');
131
+ await settings.set('acme.hello.times', 3);
132
+ await settings.set('acme.hello.style', 'loud');
133
+ expect(await commands.run('acme.hello.greet')).toEqual({
134
+ kind: 'notify',
135
+ text: 'HELLO, ADA!!!',
136
+ });
137
+ await commands.dispose();
138
+ });
139
+
140
+ it('the memory settings reject a value the engine would reject', async () => {
141
+ const settings = createMemorySettings(definitions);
142
+ await expect(settings.set('acme.hello.times', 9)).rejects.toThrow(
143
+ 'acme.hello.times',
144
+ );
145
+ await expect(settings.set('acme.hello.style', 'quiet')).rejects.toThrow(
146
+ 'acme.hello.style',
147
+ );
148
+ });
149
+ ```
150
+
151
+ `createMemorySettings(definitions, values?)` is the settings the host gives
152
+ `ctx`: `get` returns the default until a value is set, `set(id, value)` checks
153
+ the value against the definition and calls the `onDidChange` subscribers. Pass
154
+ it to `loadCommands` (or `loadEvents`, `loadExerciseType`) through `settings`.
155
+ `loadEvents` takes the definitions and `settingValues` directly.
156
+
157
+ ## Try and ship
158
+
159
+ Run `pnpm dev`, start the app with `DOLPHY_DEV_EXTENSIONS`, open Settings →
160
+ Extensions, press "Settings" on the extension, change a value and run "Greet"
161
+ from the palette.
@@ -0,0 +1,116 @@
1
+ # Recipe: a theme
2
+
3
+ A theme is data: no TypeScript, no `main.mjs`. This recipe is the `theme`
4
+ template (`npx @dolphy-app/create-extension <dir> --id acme.hello --template theme`).
5
+ The files below are exactly what the generator writes for the id `acme.hello`.
6
+ See [quick-start.md](quick-start.md) for the commands.
7
+
8
+ ## The manifest
9
+
10
+ File `extension.json` (theme):
11
+
12
+ ```json
13
+ {
14
+ "$schema": "./node_modules/@dolphy-app/extension-api/dist/extension.schema.json",
15
+ "id": "acme.hello",
16
+ "version": "0.1.0",
17
+ "apiVersion": 1,
18
+ "name": "Midnight",
19
+ "description": "A dark color theme with an amber accent for the Dolphy app.",
20
+ "author": "your-github-login",
21
+ "tags": ["theme"],
22
+ "contributes": {
23
+ "themes": [
24
+ {
25
+ "id": "acme.hello",
26
+ "label": "Midnight",
27
+ "dark": true,
28
+ "colors": {
29
+ "background": "#101820",
30
+ "surface": "#1B2733",
31
+ "on-background": "#E6EDF3",
32
+ "on-surface": "#E6EDF3",
33
+ "primary": "#FFB000",
34
+ "on-primary": "#101820"
35
+ },
36
+ "variables": { "border-opacity": 0.2 }
37
+ }
38
+ ]
39
+ }
40
+ }
41
+ ```
42
+
43
+ - `contributes.themes[]` has an `id` (not `system`, `light` or `dark`), a
44
+ `label` of 1–60 characters and `dark`, which says whether the theme is dark
45
+ and so picks the base colors of the interface that `colors` then override.
46
+ - `colors` are `#rrggbb` or `#rrggbbaa` values for a fixed list of roles
47
+ (`background`, `surface`, `primary`, the `on-…` colors for text drawn on them,
48
+ `error`, `success`…). A key outside the list is rejected by `pnpm validate`.
49
+ Pair every background with a readable text color.
50
+ - `variables` are optional tokens from a fixed list; here `border-opacity`, a
51
+ number from 0 to 1.
52
+ - `build` of a project with no code writes only `extension.json` to `dist-ext`.
53
+
54
+ ## The test
55
+
56
+ File `test/theme.test.ts` (theme):
57
+
58
+ ```ts
59
+ import { describe, expect, it } from 'vitest';
60
+ import manifest from '../extension.json';
61
+
62
+ const [theme] = manifest.contributes.themes;
63
+ const colors: Record<string, string> = theme.colors;
64
+
65
+ // WCAG relative luminance of a #rrggbb color
66
+ const luminance = (hex: string): number => {
67
+ const [r, g, b] = [1, 3, 5].map((start) => {
68
+ const channel = Number.parseInt(hex.slice(start, start + 2), 16) / 255;
69
+ return channel <= 0.03928 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
70
+ }) as [number, number, number];
71
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
72
+ };
73
+
74
+ const contrast = (foreground: string, background: string): number => {
75
+ const [light, dark] = [luminance(foreground), luminance(background)].sort(
76
+ (a, b) => b - a,
77
+ ) as [number, number];
78
+ return (light + 0.05) / (dark + 0.05);
79
+ };
80
+
81
+ describe('acme.hello: theme', () => {
82
+ it.each([
83
+ ['on-surface', 'surface'],
84
+ ['on-background', 'background'],
85
+ ['on-primary', 'primary'],
86
+ ])('%s on %s has a contrast of at least 4.5:1', (foreground, background) => {
87
+ expect(contrast(colors[foreground] as string, colors[background] as string))
88
+ .toBeGreaterThanOrEqual(4.5);
89
+ });
90
+
91
+ it('a dark theme has a dark background and a light text', () => {
92
+ const background = luminance(colors['background'] as string);
93
+ const text = luminance(colors['on-background'] as string);
94
+ expect(theme.dark ? background < text : background > text).toBe(true);
95
+ });
96
+ });
97
+ ```
98
+
99
+ A project with no code still has something worth testing. The test reads the
100
+ manifest and checks the WCAG contrast of each text color against its background
101
+ (at least 4.5:1) and that `dark` agrees with the colors. A failing contrast is a
102
+ real bug the learner would see, and the test also gives `vitest run` a file to
103
+ run. Keep the test when you change the colors.
104
+
105
+ ## Try and ship
106
+
107
+ ```sh
108
+ pnpm install
109
+ pnpm test
110
+ pnpm dev
111
+ ```
112
+
113
+ With `DOLPHY_DEV_EXTENSIONS` pointing at `dist-ext` (see the quick start) the
114
+ theme appears as a tile in Settings → Appearance next to System, Light and
115
+ Dark; saving `extension.json` is picked up by the running app. Change the id, the label and the colors; change `tags` and
116
+ `description` too, the catalog shows them.
@@ -0,0 +1,172 @@
1
+ # Recipe: a panel on the UI kit
2
+
3
+ `@dolphy-app/extension-ui` gives a panel ready, accessible elements, so you do
4
+ not write markup, keyboard handling or dark-theme colours yourself. This recipe
5
+ has no template of its own: start from `blank`, install the kit
6
+ (`pnpm add @dolphy-app/extension-ui`) and replace the three files below, which
7
+ are checked as a whole project. See [quick-start.md](quick-start.md) for the
8
+ commands.
9
+
10
+ ## The manifest
11
+
12
+ File `extension.json` (ui-kit):
13
+
14
+ ```json
15
+ {
16
+ "$schema": "./node_modules/@dolphy-app/extension-api/dist/extension.schema.json",
17
+ "id": "acme.hello",
18
+ "version": "0.1.0",
19
+ "apiVersion": 1,
20
+ "name": "Hello notes",
21
+ "description": "A panel with a text field, a button and a list built on the UI kit.",
22
+ "author": "your-github-login",
23
+ "tags": ["productivity"],
24
+ "contributes": {
25
+ "panels": [{ "id": "acme.hello.view", "title": "Notes" }]
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## The code
31
+
32
+ File `src/index.ts` (ui-kit):
33
+
34
+ ```ts
35
+ import { defineExtensionPanel } from '@dolphy-app/extension-sdk';
36
+ import type { ExtensionPanels } from '@dolphy-app/extension-sdk';
37
+ import {
38
+ button,
39
+ card,
40
+ emptyState,
41
+ list,
42
+ textField,
43
+ } from '@dolphy-app/extension-ui';
44
+
45
+ export const panels = {
46
+ 'acme.hello.view': defineExtensionPanel({
47
+ mount(container) {
48
+ const notes: string[] = [];
49
+ let draft = '';
50
+ const status = document.createElement('p');
51
+ status.setAttribute('role', 'status');
52
+ const body = document.createElement('div');
53
+ // elements are static: draw the list again with replaceChildren
54
+ const render = () => {
55
+ body.replaceChildren(
56
+ notes.length === 0
57
+ ? emptyState({
58
+ title: 'No notes yet',
59
+ description: 'Type a note and press Add.',
60
+ })
61
+ : list({
62
+ label: 'Notes',
63
+ emptyText: 'No notes',
64
+ items: notes.map((note, index) => ({
65
+ id: String(index),
66
+ label: note,
67
+ })),
68
+ onSelect: (id) => {
69
+ status.textContent = `Selected note ${Number(id) + 1}`;
70
+ },
71
+ }),
72
+ );
73
+ };
74
+ render();
75
+ container.append(
76
+ card({
77
+ title: 'Notes',
78
+ children: [
79
+ textField({
80
+ label: 'Note text',
81
+ onInput: (value) => {
82
+ draft = value;
83
+ },
84
+ }),
85
+ button({
86
+ label: 'Add',
87
+ variant: 'primary',
88
+ onClick: () => {
89
+ if (draft.trim() === '') return;
90
+ notes.push(draft.trim());
91
+ status.textContent = `Notes: ${notes.length}`;
92
+ render();
93
+ },
94
+ }),
95
+ body,
96
+ ],
97
+ }),
98
+ status,
99
+ );
100
+ },
101
+ }),
102
+ } satisfies ExtensionPanels;
103
+ ```
104
+
105
+ - `list`, `button`, `textField`, `select`, `toggle`, `card` and `emptyState`
106
+ return DOM elements you append yourself. Each has a role, a visible name and
107
+ keyboard control (a `list` is one tab stop, arrows move, Enter or Space
108
+ select).
109
+ - Colours and spacing come from the CSS variables of the frame theme, so the
110
+ light and the dark theme work with no code of yours. Text is always set as
111
+ `textContent`; the kit never parses markup.
112
+ - Elements keep no state of their own. To change a list, build a new one and
113
+ call `replaceWith` / `replaceChildren`.
114
+ - The whole kit is under 10 KiB gzipped, and it adds no permission: the panel
115
+ still runs in the isolated frame with no network.
116
+
117
+ ## The test
118
+
119
+ File `test/index.test.ts` (ui-kit):
120
+
121
+ ```ts
122
+ // @vitest-environment happy-dom
123
+ import { loadPanel } from '@dolphy-app/extension-sdk/testing';
124
+ import { afterEach, describe, expect, it } from 'vitest';
125
+ import { panels } from '../src/index.ts';
126
+
127
+ const disposables: { dispose(): unknown }[] = [];
128
+ afterEach(async () => {
129
+ await Promise.all(disposables.splice(0).map((item) => item.dispose()));
130
+ });
131
+
132
+ describe('acme.hello: panel', () => {
133
+ it('starts with an empty state and lists an added note', async () => {
134
+ const panel = await loadPanel(panels, 'acme.hello.view');
135
+ disposables.push(panel);
136
+ expect(panel.container.textContent).toContain('No notes yet');
137
+
138
+ const input = panel.container.querySelector('input') as HTMLInputElement;
139
+ input.value = 'Buy milk';
140
+ input.dispatchEvent(new Event('input', { bubbles: true }));
141
+ const add = [...panel.container.querySelectorAll('button')].find(
142
+ (element) => element.textContent === 'Add',
143
+ ) as HTMLButtonElement;
144
+ add.click();
145
+
146
+ const options = panel.container.querySelectorAll('[role="option"]');
147
+ expect(options).toHaveLength(1);
148
+ expect(options[0]?.textContent).toContain('Buy milk');
149
+ expect(panel.container.querySelector('[role="status"]')?.textContent).toBe(
150
+ 'Notes: 1',
151
+ );
152
+ });
153
+
154
+ it('ignores an empty note', async () => {
155
+ const panel = await loadPanel(panels, 'acme.hello.view');
156
+ disposables.push(panel);
157
+ const add = [...panel.container.querySelectorAll('button')].find(
158
+ (element) => element.textContent === 'Add',
159
+ ) as HTMLButtonElement;
160
+ add.click();
161
+ expect(panel.container.textContent).toContain('No notes yet');
162
+ });
163
+ });
164
+ ```
165
+
166
+ `loadPanel` mounts the panel in happy-dom the way the frame does. The kit's
167
+ own tests check its roles and keys; test what your panel does with them.
168
+
169
+ ## Try and ship
170
+
171
+ Run `pnpm dev`, start the app with `DOLPHY_DEV_EXTENSIONS` and open the panel
172
+ from the sidebar. Switch the theme in Settings → Appearance: the panel follows.
@@ -0,0 +1,158 @@
1
+ # Recipe: visibility conditions and dependencies
2
+
3
+ Show a command, a panel or a widget only where it makes sense (`when`), and
4
+ require another extension (`dependencies`). Both are manifest data, so this
5
+ recipe is a small project whose test reads the manifest. This recipe has no
6
+ template of its own: start from `blank` and replace the three files below,
7
+ which are checked as a whole project. See [quick-start.md](quick-start.md) for
8
+ the commands.
9
+
10
+ ## The manifest
11
+
12
+ File `extension.json` (when-dependencies):
13
+
14
+ ```json
15
+ {
16
+ "$schema": "./node_modules/@dolphy-app/extension-api/dist/extension.schema.json",
17
+ "id": "acme.hello",
18
+ "version": "0.1.0",
19
+ "apiVersion": 1,
20
+ "name": "Hello report",
21
+ "description": "A report command that appears only on the Courses screen.",
22
+ "author": "your-github-login",
23
+ "tags": ["productivity"],
24
+ "dependencies": [
25
+ { "id": "acme.cards", "range": ">=1.0.0 <2.0.0" },
26
+ { "id": "dolphy.choice" }
27
+ ],
28
+ "contributes": {
29
+ "commands": [
30
+ {
31
+ "id": "acme.hello.report",
32
+ "title": "Course report",
33
+ "when": "route == 'courses' && course.active"
34
+ }
35
+ ],
36
+ "panels": [
37
+ {
38
+ "id": "acme.hello.board",
39
+ "title": "Course board",
40
+ "when": "course.active && !session.active"
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ ```
46
+
47
+ - `when` is a boolean expression over five keys of the app window: `route`
48
+ (the name of the current screen), `course.active` (one course is in focus),
49
+ `session.active` (a study session is open), `locale` (`ru` or `en`) and
50
+ `theme.dark`. Operators: `==`, `!=`, `in ('a', 'b')`, `&&`, `||`, `!` and
51
+ parentheses; strings are in single quotes. At most 200 characters. An unknown
52
+ key or value, a wrong type or a syntax error is a manifest error with the
53
+ position, so `pnpm validate` catches a typo before the app does.
54
+ - While the condition is false a command is not in the palette and its keys do
55
+ nothing, a panel's menu item is hidden, and a widget is not drawn. The value
56
+ follows the route, the course, the session, the language and the theme
57
+ without a reload. Your own code still reaches the command (`ctx.call`) and
58
+ the panel (`openPanel`): `when` hides, it does not forbid.
59
+ - `dependencies` lists up to 16 extensions that must be present, enabled,
60
+ loaded and in the version `range` (comparators separated by a space, such as
61
+ `>=1.0.0 <2.0.0`; no `^` or `~`). Otherwise the extension is shown as
62
+ "dependencies not met" in Settings → Extensions, with the missing, disabled
63
+ or mismatched one named, and contributes nothing. Disabling or enabling a
64
+ dependency updates the dependents at once.
65
+ - Nothing installs a dependency for the user, and extensions cannot call each
66
+ other: a dependency only says "this must be there". A repeat and a dependency
67
+ on yourself are manifest errors; extensions that depend on each other in a
68
+ circle are not loaded.
69
+
70
+ ## The code
71
+
72
+ File `src/index.ts` (when-dependencies):
73
+
74
+ ```ts
75
+ import { defineExtension, defineExtensionPanel } from '@dolphy-app/extension-sdk';
76
+ import type { ExtensionPanels } from '@dolphy-app/extension-sdk';
77
+
78
+ export const host = defineExtension({
79
+ commands: { 'acme.hello.report': () => ({ courses: 1 }) },
80
+ });
81
+
82
+ export const panels = {
83
+ 'acme.hello.board': defineExtensionPanel({
84
+ mount(container) {
85
+ container.textContent = 'Board';
86
+ },
87
+ }),
88
+ } satisfies ExtensionPanels;
89
+ ```
90
+
91
+ The conditions are in the manifest, so the code is ordinary: it does not check
92
+ where it is shown. The command and the panel are only reachable where the
93
+ condition is true.
94
+
95
+ ## The test
96
+
97
+ File `test/when.test.ts` (when-dependencies):
98
+
99
+ ```ts
100
+ import { evaluateWhen, parseWhen } from '@dolphy-app/extension-sdk';
101
+ import type { WhenContext } from '@dolphy-app/extension-sdk';
102
+ import { describe, expect, it } from 'vitest';
103
+ import manifest from '../extension.json';
104
+
105
+ const context = (overrides: Partial<WhenContext> = {}): WhenContext => ({
106
+ route: 'courses',
107
+ 'course.active': true,
108
+ 'session.active': false,
109
+ locale: 'en',
110
+ 'theme.dark': false,
111
+ ...overrides,
112
+ });
113
+
114
+ const [command] = manifest.contributes.commands;
115
+ const [panel] = manifest.contributes.panels;
116
+
117
+ describe('acme.hello: when', () => {
118
+ it('shows the report on the Courses screen with a course in focus', () => {
119
+ const when = parseWhen(command.when);
120
+ expect(evaluateWhen(when, context())).toBe(true);
121
+ expect(evaluateWhen(when, context({ route: 'daily-plan' }))).toBe(
122
+ false,
123
+ );
124
+ expect(
125
+ evaluateWhen(when, context({ 'course.active': false })),
126
+ ).toBe(false);
127
+ });
128
+
129
+ it('hides the board during a study session', () => {
130
+ const when = parseWhen(panel.when);
131
+ expect(evaluateWhen(when, context())).toBe(true);
132
+ expect(
133
+ evaluateWhen(when, context({ 'session.active': true })),
134
+ ).toBe(false);
135
+ });
136
+
137
+ it('reports a typo in a key with its position', () => {
138
+ expect(() => parseWhen("rout == 'courses'")).toThrow();
139
+ });
140
+ });
141
+
142
+ describe('acme.hello: dependencies', () => {
143
+ it('names each required extension once, and not itself', () => {
144
+ const ids = manifest.dependencies.map(({ id }) => id);
145
+ expect(new Set(ids).size).toBe(ids.length);
146
+ expect(ids).not.toContain(manifest.id);
147
+ });
148
+ });
149
+ ```
150
+
151
+ `parseWhen` and `evaluateWhen` are the functions the app and `dolphy-ext
152
+ validate` use. `evaluateWhen` is pure: pass an object with the five keys.
153
+
154
+ ## Try and ship
155
+
156
+ Run `pnpm validate`: it fails on a bad `when` or a bad `dependencies` entry.
157
+ In the app the extension's row in Settings → Extensions shows "dependencies not
158
+ met" until `acme.cards` is installed and enabled.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dolphy-app/extension-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "SDK for extension authors: defineExtension, defineAnswerView, test helpers",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,10 +20,11 @@
20
20
  },
21
21
  "types": "./dist/index.d.ts",
22
22
  "files": [
23
- "dist"
23
+ "dist",
24
+ "docs"
24
25
  ],
25
26
  "dependencies": {
26
- "@dolphy-app/extension-api": "0.3.0",
27
+ "@dolphy-app/extension-api": "0.4.0",
27
28
  "ajv": "^8"
28
29
  },
29
30
  "engines": {