mindoodb-app-sdk 0.0.4
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 +384 -0
- package/TESTING.md +185 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +634 -0
- package/dist/testing/index.d.ts +60 -0
- package/dist/testing/index.js +801 -0
- package/dist/types-BGdIfp-D.d.ts +372 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# mindoodb-app-sdk
|
|
2
|
+
|
|
3
|
+
`mindoodb-app-sdk` is the browser-side SDK for building apps that run inside MindooDB Haven and talk to the Haven host bridge.
|
|
4
|
+
|
|
5
|
+
Use it when your app is launched by Haven in an iframe or in a separate window and needs:
|
|
6
|
+
|
|
7
|
+
- launch metadata such as user, runtime, theme, and current iframe viewport
|
|
8
|
+
- access to the databases Haven mapped into the app session
|
|
9
|
+
- document, history, attachment, and view operations through a stable bridge API
|
|
10
|
+
- host-driven UI events such as theme and viewport changes
|
|
11
|
+
|
|
12
|
+
## What the Haven host bridge provides
|
|
13
|
+
|
|
14
|
+
The Haven host bridge is a `postMessage` + `MessageChannel` / `MessagePort` connection between your app and Haven.
|
|
15
|
+
|
|
16
|
+
Once connected, Haven provides:
|
|
17
|
+
|
|
18
|
+
- a `MindooDBAppSession` for the current launch
|
|
19
|
+
- a `launchContext` snapshot with app id, user, runtime, theme, launch parameters, and current viewport
|
|
20
|
+
- database handles that are already permission-scoped and mapped by Haven
|
|
21
|
+
- live host events:
|
|
22
|
+
- `onThemeChange()` when Haven switches between light and dark mode or theme presets
|
|
23
|
+
- `onViewportChange()` when the iframe size changes inside Haven
|
|
24
|
+
- CRUD, history, attachments, and app-defined virtual views over the same bridge
|
|
25
|
+
|
|
26
|
+
Your app does not open arbitrary MindooDB databases directly. Haven decides which logical databases are visible and what permissions each one has.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install mindoodb-app-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For local development against an unpublished workspace copy, prefer `npm link`:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# In the SDK package
|
|
38
|
+
npm link
|
|
39
|
+
|
|
40
|
+
# In your app project
|
|
41
|
+
npm link mindoodb-app-sdk
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
When you want to switch back to the published npm version, run `npm unlink mindoodb-app-sdk` in the app project and then `npm install`.
|
|
45
|
+
|
|
46
|
+
## Getting started
|
|
47
|
+
|
|
48
|
+
### 1. Register an app in Haven
|
|
49
|
+
|
|
50
|
+
Create an app registration in Haven that points to either:
|
|
51
|
+
|
|
52
|
+
- an external dev server or deployment URL, such as `http://localhost:4173`
|
|
53
|
+
- a hosted app bundle imported into Haven
|
|
54
|
+
|
|
55
|
+
Pick one runtime:
|
|
56
|
+
|
|
57
|
+
- `iframe`: the app is embedded in Haven
|
|
58
|
+
- `window`: the app opens in its own tab or popup
|
|
59
|
+
|
|
60
|
+
### 2. Let Haven launch the app
|
|
61
|
+
|
|
62
|
+
When Haven launches an app, it injects bridge launch parameters into the app URL:
|
|
63
|
+
|
|
64
|
+
- `mindoodbAppLaunchId`
|
|
65
|
+
- `mindoodbAppId`
|
|
66
|
+
- `mindoodbAppRuntime`
|
|
67
|
+
|
|
68
|
+
`createMindooDBAppBridge().connect()` reads `mindoodbAppLaunchId` from the current browser URL automatically.
|
|
69
|
+
|
|
70
|
+
### 3. Connect to the bridge
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { createMindooDBAppBridge } from "mindoodb-app-sdk";
|
|
74
|
+
|
|
75
|
+
const bridge = createMindooDBAppBridge();
|
|
76
|
+
const session = await bridge.connect();
|
|
77
|
+
const launchContext = await session.getLaunchContext();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If you need to override the launch id or target origin manually:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const session = await createMindooDBAppBridge().connect({
|
|
84
|
+
launchId: "custom-launch-id",
|
|
85
|
+
targetOrigin: "https://haven.example.com",
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 4. Read the launch context
|
|
90
|
+
|
|
91
|
+
The launch context is your initial snapshot of the Haven host state:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const launchContext = await session.getLaunchContext();
|
|
95
|
+
|
|
96
|
+
console.log(launchContext.runtime); // "iframe" | "window"
|
|
97
|
+
console.log(launchContext.theme.mode); // "light" | "dark"
|
|
98
|
+
console.log(launchContext.viewport); // { width, height } | null
|
|
99
|
+
console.log(launchContext.user.username);
|
|
100
|
+
console.log(launchContext.launchParameters);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`launchContext.viewport` is especially useful for responsive layouts inside embedded iframes. It is the initial viewport snapshot; later changes arrive through `onViewportChange()`.
|
|
104
|
+
|
|
105
|
+
### 5. Subscribe to host-driven events
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const stopThemeSync = session.onThemeChange((theme) => {
|
|
109
|
+
console.log("Theme changed:", theme.mode, theme.preset);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const stopViewportSync = session.onViewportChange((viewport) => {
|
|
113
|
+
console.log("Viewport changed:", viewport.width, viewport.height);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Later, during teardown:
|
|
117
|
+
stopThemeSync();
|
|
118
|
+
stopViewportSync();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The intended pattern is:
|
|
122
|
+
|
|
123
|
+
- use `getLaunchContext()` for the initial host snapshot
|
|
124
|
+
- use `onThemeChange()` and `onViewportChange()` for live updates
|
|
125
|
+
|
|
126
|
+
### 6. Discover the databases Haven exposed
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const databases = await session.listDatabases();
|
|
130
|
+
|
|
131
|
+
for (const database of databases) {
|
|
132
|
+
console.log(database.id, database.title, database.capabilities);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Do not hard-code assumptions about available databases or permissions. Always discover them from the session.
|
|
137
|
+
|
|
138
|
+
### 7. Open a database and work with documents
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const databases = await session.listDatabases();
|
|
142
|
+
const db = await session.openDatabase(databases[0]!.id);
|
|
143
|
+
|
|
144
|
+
const list = await db.documents.list({ limit: 25 });
|
|
145
|
+
const firstDoc = list.items[0] ? await db.documents.get(list.items[0].id) : null;
|
|
146
|
+
|
|
147
|
+
await db.documents.create({
|
|
148
|
+
data: {
|
|
149
|
+
employee: "Jane Doe",
|
|
150
|
+
workDate: "2026-04-04",
|
|
151
|
+
hours: 8,
|
|
152
|
+
},
|
|
153
|
+
decryptionKeyId: "payroll",
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Omit `decryptionKeyId` to keep using the tenant's `"default"` document key.
|
|
158
|
+
|
|
159
|
+
Document operations:
|
|
160
|
+
|
|
161
|
+
- `documents.list()`
|
|
162
|
+
- `documents.get()`
|
|
163
|
+
- `documents.create()`
|
|
164
|
+
- `documents.update()`
|
|
165
|
+
- `documents.delete()`
|
|
166
|
+
- `documents.listHistory()`
|
|
167
|
+
- `documents.getAtTimestamp()`
|
|
168
|
+
|
|
169
|
+
When creating a document, apps can optionally pass `decryptionKeyId` to use a named document key instead of `"default"`.
|
|
170
|
+
|
|
171
|
+
Attachment operations:
|
|
172
|
+
|
|
173
|
+
- `attachments.list(docId)`
|
|
174
|
+
- `attachments.remove(docId, attachmentName)`
|
|
175
|
+
- `attachments.openReadStream(docId, attachmentName)`
|
|
176
|
+
- `attachments.openWriteStream(docId, attachmentName, contentType?)`
|
|
177
|
+
|
|
178
|
+
## Quick start example
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { createMindooDBAppBridge, createViewLanguage } from "mindoodb-app-sdk";
|
|
182
|
+
|
|
183
|
+
const bridge = createMindooDBAppBridge();
|
|
184
|
+
const session = await bridge.connect();
|
|
185
|
+
|
|
186
|
+
const launchContext = await session.getLaunchContext();
|
|
187
|
+
console.log("Connected runtime:", launchContext.runtime);
|
|
188
|
+
console.log("Initial viewport:", launchContext.viewport);
|
|
189
|
+
|
|
190
|
+
session.onThemeChange((theme) => {
|
|
191
|
+
document.documentElement.dataset.theme = theme.mode;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
session.onViewportChange((viewport) => {
|
|
195
|
+
console.log("Resize:", viewport.width, viewport.height);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const databases = await session.listDatabases();
|
|
199
|
+
const db = await session.openDatabase(databases[0]!.id);
|
|
200
|
+
|
|
201
|
+
const list = await db.documents.list({ limit: 25 });
|
|
202
|
+
const firstDoc = list.items[0] ? await db.documents.get(list.items[0].id) : null;
|
|
203
|
+
|
|
204
|
+
const v = createViewLanguage<{ employee: string; hours: number; rate?: number }>();
|
|
205
|
+
const view = await db.views.create({
|
|
206
|
+
title: "Hours by employee",
|
|
207
|
+
defaultExpand: "collapsed",
|
|
208
|
+
filter: {
|
|
209
|
+
mode: "expression",
|
|
210
|
+
expression: v.gt(v.toNumber(v.field("hours")), v.number(0)),
|
|
211
|
+
},
|
|
212
|
+
columns: [
|
|
213
|
+
{
|
|
214
|
+
name: "employee",
|
|
215
|
+
title: "Employee",
|
|
216
|
+
role: "category",
|
|
217
|
+
expression: v.field("employee"),
|
|
218
|
+
sorting: "ascending",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "amount",
|
|
222
|
+
title: "Amount",
|
|
223
|
+
role: "display",
|
|
224
|
+
expression: v.let(
|
|
225
|
+
{
|
|
226
|
+
hours: v.toNumber(v.field("hours")),
|
|
227
|
+
rate: v.coalesce(v.toNumber(v.field("rate")), v.number(1)),
|
|
228
|
+
},
|
|
229
|
+
({ hours, rate }) => v.mul(v.coalesce(hours, v.number(0)), v.coalesce(rate, v.number(0))),
|
|
230
|
+
),
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const page = await view.page({ pageSize: 100 });
|
|
236
|
+
console.log(page.rows);
|
|
237
|
+
await view.dispose();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Views and expression language
|
|
241
|
+
|
|
242
|
+
Views are app-defined and session-scoped. Create a definition, page through rows, inspect categories, manage expansion state, then dispose the handle when done.
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { createViewLanguage } from "mindoodb-app-sdk";
|
|
246
|
+
|
|
247
|
+
const v = createViewLanguage<{
|
|
248
|
+
employee: string;
|
|
249
|
+
workDate: string;
|
|
250
|
+
hours: number;
|
|
251
|
+
rate?: number;
|
|
252
|
+
note?: string;
|
|
253
|
+
}>();
|
|
254
|
+
|
|
255
|
+
const view = await db.views.create({
|
|
256
|
+
title: "By employee",
|
|
257
|
+
defaultExpand: "collapsed",
|
|
258
|
+
filter: {
|
|
259
|
+
mode: "expression",
|
|
260
|
+
expression: v.gt(v.toNumber(v.field("hours")), v.number(0)),
|
|
261
|
+
},
|
|
262
|
+
columns: [
|
|
263
|
+
{
|
|
264
|
+
name: "employee",
|
|
265
|
+
title: "Employee",
|
|
266
|
+
role: "category",
|
|
267
|
+
expression: v.field("employee"),
|
|
268
|
+
sorting: "ascending",
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: "amount",
|
|
272
|
+
title: "Amount",
|
|
273
|
+
role: "display",
|
|
274
|
+
expression: v.let(
|
|
275
|
+
{
|
|
276
|
+
hours: v.toNumber(v.field("hours")),
|
|
277
|
+
rate: v.coalesce(v.toNumber(v.field("rate")), v.number(1)),
|
|
278
|
+
},
|
|
279
|
+
({ hours, rate }) => v.mul(v.coalesce(hours, v.number(0)), v.coalesce(rate, v.number(0))),
|
|
280
|
+
),
|
|
281
|
+
sorting: "descending",
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const page = await view.page({ pageSize: 100 });
|
|
287
|
+
const firstCategory = page.rows.find((row) => row.type === "category");
|
|
288
|
+
|
|
289
|
+
if (firstCategory) {
|
|
290
|
+
await view.expand(firstCategory.key);
|
|
291
|
+
const categoryRows = await view.pageCategory(firstCategory.key, { pageSize: 25 });
|
|
292
|
+
const categoryDocumentIds = await view.listCategoryDocumentIds(firstCategory.key);
|
|
293
|
+
const sameCategory = await view.getCategory({ path: firstCategory.categoryPath });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
await view.dispose();
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Available view-handle operations:
|
|
300
|
+
|
|
301
|
+
- `view.getDefinition()`
|
|
302
|
+
- `view.refresh()`
|
|
303
|
+
- `view.page({ pageSize, position, expansion, rootRowKey })`
|
|
304
|
+
- `view.getExpansionState()`
|
|
305
|
+
- `view.setExpansionState(state)`
|
|
306
|
+
- `view.expand(rowKey)`
|
|
307
|
+
- `view.collapse(rowKey)`
|
|
308
|
+
- `view.expandAll()`
|
|
309
|
+
- `view.collapseAll()`
|
|
310
|
+
- `view.getRow(rowKey)`
|
|
311
|
+
- `view.getCategory({ path })`
|
|
312
|
+
- `view.pageCategory(categoryKey, { pageSize, position })`
|
|
313
|
+
- `view.listCategoryDocumentIds(categoryKey)`
|
|
314
|
+
- `view.dispose()`
|
|
315
|
+
|
|
316
|
+
The expression language is fully declarative. Apps define filter and column logic through a typed builder API that compiles to JSON-safe expression objects. No app-provided JavaScript runs inside the bridge host.
|
|
317
|
+
|
|
318
|
+
Helpers currently include:
|
|
319
|
+
|
|
320
|
+
- field/value access: `field()`, `value()`, `origin()`
|
|
321
|
+
- literals and conversion: `literal()`, `string()`, `number()`, `boolean()`, `toNumber()`, `toString()`, `toBoolean()`
|
|
322
|
+
- math and comparisons: `add()`, `sub()`, `mul()`, `div()`, `mod()`, `eq()`, `neq()`, `gt()`, `gte()`, `lt()`, `lte()`
|
|
323
|
+
- boolean logic: `and()`, `or()`, `not()`
|
|
324
|
+
- string helpers: `concat()`, `lower()`, `upper()`, `trim()`, `contains()`, `startsWith()`, `endsWith()`
|
|
325
|
+
- null/existence helpers: `coalesce()`, `exists()`, `notExists()`
|
|
326
|
+
- date/path helpers: `datePart()`, `pathJoin()`
|
|
327
|
+
- control flow: `ifElse()`, `let()`
|
|
328
|
+
|
|
329
|
+
The dedicated language guide lives in the `mindoodb-view-language` package. The SDK re-exports `createViewLanguage()` for convenience.
|
|
330
|
+
|
|
331
|
+
## Testing without Haven
|
|
332
|
+
|
|
333
|
+
You do **not** need a full Haven environment to test apps that use `mindoodb-app-sdk`.
|
|
334
|
+
|
|
335
|
+
The SDK ships a dedicated `mindoodb-app-sdk/testing` entrypoint with two levels of test helpers:
|
|
336
|
+
|
|
337
|
+
- Level 1: simple app-test helpers that return a fake `MindooDBAppSession` and bridge object for unit, composable, store, and component tests
|
|
338
|
+
- Level 2: a fake bridge host harness for tests that should exercise the real `createMindooDBAppBridge()` handshake over `postMessage` and `MessageChannel`
|
|
339
|
+
|
|
340
|
+
Use Level 1 when you only need app behavior. Use Level 2 when you want to validate your bridge integration itself.
|
|
341
|
+
|
|
342
|
+
See [`TESTING.md`](./TESTING.md) for setup instructions and Vitest examples.
|
|
343
|
+
|
|
344
|
+
## Local development workflow
|
|
345
|
+
|
|
346
|
+
The recommended local setup is:
|
|
347
|
+
|
|
348
|
+
1. Run Haven locally.
|
|
349
|
+
2. Run your app on its own dev server, for example Vite on `http://localhost:4173`.
|
|
350
|
+
3. Register that URL in Haven as an external app.
|
|
351
|
+
4. Launch the app from Haven so it receives a real `mindoodbAppLaunchId`.
|
|
352
|
+
|
|
353
|
+
This gives you:
|
|
354
|
+
|
|
355
|
+
- normal browser dev-server behavior
|
|
356
|
+
- hot module reload
|
|
357
|
+
- the full Haven host bridge
|
|
358
|
+
- realistic iframe sizing and theme changes during development
|
|
359
|
+
|
|
360
|
+
The sample `mindoodb-timerecords` app in this workspace is a good reference for this setup.
|
|
361
|
+
|
|
362
|
+
This local development workflow is separate from the automated testing workflow above. Running Haven locally is useful for interactive development, but it is not required for Level 1 or Level 2 automated tests.
|
|
363
|
+
|
|
364
|
+
## Permissions and mappings
|
|
365
|
+
|
|
366
|
+
Apps never choose arbitrary database targets themselves. Haven decides:
|
|
367
|
+
|
|
368
|
+
- which logical database handles are visible to the app
|
|
369
|
+
- whether each handle can read, create, update, delete, inspect history, access attachments, or create views
|
|
370
|
+
- how each logical handle maps to a concrete tenant/database target
|
|
371
|
+
|
|
372
|
+
Your app should always react to what Haven grants instead of assuming a fixed environment.
|
|
373
|
+
|
|
374
|
+
## When not to use this SDK
|
|
375
|
+
|
|
376
|
+
`mindoodb-app-sdk` is for MindooDB apps that intentionally integrate with the Haven host bridge.
|
|
377
|
+
|
|
378
|
+
If you are embedding generic third-party web content that does not speak the MindooDB bridge protocol, use a general iframe integration such as `iframe-resizer` instead of this SDK.
|
|
379
|
+
|
|
380
|
+
## Current limitations
|
|
381
|
+
|
|
382
|
+
- the bridge currently expects a browser environment with `window`, `postMessage`, and `MessagePort`
|
|
383
|
+
- viewport updates are host-driven and only available when the app is actually launched by Haven
|
|
384
|
+
- your app is still responsible for its own UI layout and responsive behavior after receiving host theme and viewport updates
|
package/TESTING.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Testing `mindoodb-app-sdk` apps
|
|
2
|
+
|
|
3
|
+
You can test apps that use `mindoodb-app-sdk` without running a full Haven environment.
|
|
4
|
+
|
|
5
|
+
The SDK exposes a dedicated testing entrypoint:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
createFakeBridgeHost,
|
|
10
|
+
createMockMindooDBAppBridge,
|
|
11
|
+
createMockMindooDBAppSession,
|
|
12
|
+
} from "mindoodb-app-sdk/testing";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Pick a level
|
|
16
|
+
|
|
17
|
+
### Level 1: simple app tests
|
|
18
|
+
|
|
19
|
+
Use Level 1 for:
|
|
20
|
+
|
|
21
|
+
- composables
|
|
22
|
+
- hooks
|
|
23
|
+
- stores
|
|
24
|
+
- component tests
|
|
25
|
+
- logic that does not need to verify the transport layer
|
|
26
|
+
|
|
27
|
+
These helpers give you a fake `MindooDBAppSession` and bridge object without going through `postMessage` or `MessageChannel`.
|
|
28
|
+
|
|
29
|
+
### Level 2: bridge protocol tests
|
|
30
|
+
|
|
31
|
+
Use Level 2 when you want to exercise the real `createMindooDBAppBridge()` connection flow.
|
|
32
|
+
|
|
33
|
+
The fake host harness:
|
|
34
|
+
|
|
35
|
+
- installs a host window for the test
|
|
36
|
+
- accepts the `mindoodb-app:connect` handshake
|
|
37
|
+
- transfers a `MessagePort`
|
|
38
|
+
- responds to bridge RPC requests
|
|
39
|
+
- can emit theme and viewport change events
|
|
40
|
+
|
|
41
|
+
This is useful for integration-style tests that should prove your app works with the real SDK bridge behavior, but still without running Haven.
|
|
42
|
+
|
|
43
|
+
## Level 1 example
|
|
44
|
+
|
|
45
|
+
This pattern is a good fit when your app already depends on `createMindooDBAppBridge()` and you want to replace only that boundary in Vitest.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { describe, expect, it, vi } from "vitest";
|
|
49
|
+
|
|
50
|
+
vi.mock("mindoodb-app-sdk", async () => {
|
|
51
|
+
const actual = await vi.importActual<typeof import("mindoodb-app-sdk")>("mindoodb-app-sdk");
|
|
52
|
+
const { createMockMindooDBAppBridge } = await import("mindoodb-app-sdk/testing");
|
|
53
|
+
|
|
54
|
+
const mock = createMockMindooDBAppBridge({
|
|
55
|
+
launchContext: {
|
|
56
|
+
appId: "timerecords",
|
|
57
|
+
launchParameters: {
|
|
58
|
+
decryptionKeyId: "payroll",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
databases: [{
|
|
62
|
+
info: {
|
|
63
|
+
id: "main",
|
|
64
|
+
title: "Main",
|
|
65
|
+
capabilities: ["read", "create"],
|
|
66
|
+
},
|
|
67
|
+
}],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...actual,
|
|
72
|
+
createMindooDBAppBridge: () => mock.bridge,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Available Level 1 helpers:
|
|
78
|
+
|
|
79
|
+
- `createMockMindooDBAppSession()`
|
|
80
|
+
- `createMockMindooDBAppBridge()`
|
|
81
|
+
|
|
82
|
+
Both accept the same options:
|
|
83
|
+
|
|
84
|
+
- `launchContext`
|
|
85
|
+
- `databases`
|
|
86
|
+
- `onDisconnect`
|
|
87
|
+
|
|
88
|
+
Each database entry can provide:
|
|
89
|
+
|
|
90
|
+
- `info`
|
|
91
|
+
- `methods.documents`
|
|
92
|
+
- `methods.views`
|
|
93
|
+
- `methods.attachments`
|
|
94
|
+
|
|
95
|
+
## Level 2 example
|
|
96
|
+
|
|
97
|
+
This pattern keeps the real `createMindooDBAppBridge()` code path and replaces only the host side.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
101
|
+
import { createMindooDBAppBridge } from "mindoodb-app-sdk";
|
|
102
|
+
import { createFakeBridgeHost } from "mindoodb-app-sdk/testing";
|
|
103
|
+
|
|
104
|
+
describe("bridge integration", () => {
|
|
105
|
+
let host: ReturnType<typeof createFakeBridgeHost> | null = null;
|
|
106
|
+
|
|
107
|
+
afterEach(() => {
|
|
108
|
+
host?.dispose();
|
|
109
|
+
host = null;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("connects without Haven", async () => {
|
|
113
|
+
host = createFakeBridgeHost({
|
|
114
|
+
launchContext: {
|
|
115
|
+
appId: "timerecords",
|
|
116
|
+
launchId: "launch-1",
|
|
117
|
+
},
|
|
118
|
+
databases: [{
|
|
119
|
+
info: {
|
|
120
|
+
id: "main",
|
|
121
|
+
title: "Main",
|
|
122
|
+
capabilities: ["read"],
|
|
123
|
+
},
|
|
124
|
+
methods: {
|
|
125
|
+
documents: {
|
|
126
|
+
async list() {
|
|
127
|
+
return {
|
|
128
|
+
items: [{ id: "doc-1" }],
|
|
129
|
+
nextCursor: null,
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
host.install();
|
|
138
|
+
|
|
139
|
+
const session = await createMindooDBAppBridge().connect();
|
|
140
|
+
const databases = await session.listDatabases();
|
|
141
|
+
|
|
142
|
+
expect(databases[0]?.id).toBe("main");
|
|
143
|
+
|
|
144
|
+
host.emitViewportChange({
|
|
145
|
+
width: 720,
|
|
146
|
+
height: 480,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Available Level 2 helpers:
|
|
153
|
+
|
|
154
|
+
- `createFakeBridgeHost()`
|
|
155
|
+
|
|
156
|
+
Useful Level 2 methods:
|
|
157
|
+
|
|
158
|
+
- `install()`
|
|
159
|
+
- `dispose()`
|
|
160
|
+
- `emitThemeChange()`
|
|
161
|
+
- `emitViewportChange()`
|
|
162
|
+
- `setRequestHandler()`
|
|
163
|
+
- `clearRequestHandler()`
|
|
164
|
+
- `postPortMessage()`
|
|
165
|
+
|
|
166
|
+
## When to use which level
|
|
167
|
+
|
|
168
|
+
Choose Level 1 when:
|
|
169
|
+
|
|
170
|
+
- you are testing app behavior, not the bridge transport
|
|
171
|
+
- you want the smallest and fastest mock setup
|
|
172
|
+
- you already mock `mindoodb-app-sdk` in Vitest
|
|
173
|
+
|
|
174
|
+
Choose Level 2 when:
|
|
175
|
+
|
|
176
|
+
- you want to keep the real `createMindooDBAppBridge()` code path
|
|
177
|
+
- you want to verify launch-id driven connection behavior
|
|
178
|
+
- you want to test host-driven theme or viewport events through the bridge
|
|
179
|
+
- you want an integration-style test without starting Haven
|
|
180
|
+
|
|
181
|
+
## Local development vs automated tests
|
|
182
|
+
|
|
183
|
+
These helpers are for automated tests.
|
|
184
|
+
|
|
185
|
+
For interactive local development, it is still recommended to run Haven locally and launch the app from Haven so the app receives a real launch session and the full host environment.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { M as MindooDBAppBridge } from './types-BGdIfp-D.js';
|
|
2
|
+
export { a as MindooDBAppAttachmentApi, b as MindooDBAppAttachmentChunk, c as MindooDBAppAttachmentInfo, d as MindooDBAppBridgeConnectMessage, e as MindooDBAppBridgeConnectOptions, f as MindooDBAppBridgeConnectedMessage, g as MindooDBAppBridgeErrorPayload, h as MindooDBAppBridgePortMessage, i as MindooDBAppBridgeRpcError, j as MindooDBAppBridgeRpcMessage, k as MindooDBAppBridgeRpcRequest, l as MindooDBAppBridgeRpcSuccess, m as MindooDBAppBridgeStreamAbortRequest, n as MindooDBAppBridgeStreamAck, o as MindooDBAppBridgeStreamCloseRequest, p as MindooDBAppBridgeStreamError, q as MindooDBAppBridgeStreamMessage, r as MindooDBAppBridgeStreamOpenResult, s as MindooDBAppBridgeStreamReadRequest, t as MindooDBAppBridgeStreamWriteRequest, u as MindooDBAppBridgeThemeChangedMessage, v as MindooDBAppBridgeViewportChangedMessage, w as MindooDBAppCapability, x as MindooDBAppConfiguredViewCategorizationStyle, y as MindooDBAppConfiguredViewColumn, z as MindooDBAppConfiguredViewColumnRole, A as MindooDBAppConfiguredViewExpressionMode, B as MindooDBAppConfiguredViewFieldExpression, C as MindooDBAppConfiguredViewFilterDefinition, D as MindooDBAppConfiguredViewFilterMode, E as MindooDBAppConfiguredViewFilterRule, F as MindooDBAppConfiguredViewFormulaExpression, G as MindooDBAppConfiguredViewFormulaFilterDefinition, H as MindooDBAppConfiguredViewPreviewMode, I as MindooDBAppConfiguredViewRuleFilterDefinition, J as MindooDBAppConfiguredViewRuleMatchMode, K as MindooDBAppConfiguredViewRuleOperator, L as MindooDBAppConfiguredViewValueExpression, N as MindooDBAppCreateDocumentInput, O as MindooDBAppDatabase, P as MindooDBAppDatabaseInfo, Q as MindooDBAppDocument, R as MindooDBAppDocumentApi, S as MindooDBAppDocumentHistoryEntry, T as MindooDBAppDocumentListQuery, U as MindooDBAppDocumentListResult, V as MindooDBAppDocumentSummary, W as MindooDBAppHistoricalDocument, X as MindooDBAppHistoryQuery, Y as MindooDBAppHostTheme, Z as MindooDBAppLaunchContext, _ as MindooDBAppReadableAttachmentStream, $ as MindooDBAppResolvedViewDefinition, a0 as MindooDBAppResolvedViewSource, a1 as MindooDBAppRuntime, a2 as MindooDBAppSession, a3 as MindooDBAppThemeMode, a4 as MindooDBAppUpdateDocumentInput, a5 as MindooDBAppViewApi, a6 as MindooDBAppViewHandle, a7 as MindooDBAppViewport, a8 as MindooDBAppWritableAttachmentStream } from './types-BGdIfp-D.js';
|
|
3
|
+
export { MindooDBAppBooleanExpression, MindooDBAppExpression, MindooDBAppFilterDefinition, MindooDBAppViewCategoryChildrenPageRequest, MindooDBAppViewColumn, MindooDBAppViewColumnRole, MindooDBAppViewDefinition, MindooDBAppViewExpansionState, MindooDBAppViewExpressionBase, MindooDBAppViewExpressionDatePart, MindooDBAppViewExpressionOperation, MindooDBAppViewFieldExpression, MindooDBAppViewIfExpression, MindooDBAppViewLetExpression, MindooDBAppViewLiteralExpression, MindooDBAppViewLookupByPath, MindooDBAppViewOperationExpression, MindooDBAppViewOriginExpression, MindooDBAppViewPageRequest, MindooDBAppViewPageResult, MindooDBAppViewRow, MindooDBAppViewSortDirection, MindooDBAppViewTotalMode, MindooDBAppViewValueExpressionRef, MindooDBAppViewVariableExpression, createViewLanguage } from 'mindoodb-view-language';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates the browser-side bridge entry point used by MindooDB apps.
|
|
7
|
+
*
|
|
8
|
+
* The returned object performs the initial postMessage handshake with the
|
|
9
|
+
* Administrator host and exposes a session with document, view, and attachment
|
|
10
|
+
* APIs over a dedicated `MessagePort`.
|
|
11
|
+
*/
|
|
12
|
+
declare function createMindooDBAppBridge(): MindooDBAppBridge;
|
|
13
|
+
|
|
14
|
+
export { MindooDBAppBridge, createMindooDBAppBridge };
|