mindoodb-app-sdk 0.0.41 → 0.0.43
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 +87 -1
- package/dist/appBundleManifest-B0xfpaXG.d.ts +62 -0
- package/dist/chunk-7RAKZZA6.js +1426 -0
- package/dist/chunk-LT4FK6LU.js +146 -0
- package/dist/index.d.ts +123 -3
- package/dist/index.js +292 -1170
- package/dist/testing/index.d.ts +10 -1
- package/dist/testing/index.js +85 -1
- package/dist/{types-B0zYIrv3.d.ts → types-DALNnPXo.d.ts} +122 -2
- package/dist/vite/index.d.ts +46 -0
- package/dist/vite/index.js +291 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1472,7 +1472,70 @@ wrangler deploy
|
|
|
1472
1472
|
|
|
1473
1473
|
Other static hosting options work just as well: Netlify, Vercel, any web server serving your `dist/` folder.
|
|
1474
1474
|
|
|
1475
|
-
|
|
1475
|
+
### Haven-hosted bundles
|
|
1476
|
+
|
|
1477
|
+
Instead of Haven loading your app from your server on every launch, you can hand Haven the build itself. Haven stores it in its own cache and serves it from a service worker on an opaque origin, so the app loads offline and runs in a stricter sandbox than an external URL gets.
|
|
1478
|
+
|
|
1479
|
+
You can upload a build directory by hand, but the useful form is to publish a **bundle manifest** next to your app and let Haven fetch and update it. Add the plugin to your Vite config:
|
|
1480
|
+
|
|
1481
|
+
```ts
|
|
1482
|
+
import { havenBundle } from "mindoodb-app-sdk/vite";
|
|
1483
|
+
|
|
1484
|
+
export default defineConfig({
|
|
1485
|
+
// Haven serves the bundle under a path it picks, so assets must be relative.
|
|
1486
|
+
base: "./",
|
|
1487
|
+
plugins: [havenBundle()],
|
|
1488
|
+
});
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
After `vite build`, `dist/` additionally contains `haven-bundle.json` (app id, version, a SHA-256 for every file, and one for the bundle as a whole) and `haven-bundle.zip` (all of it in one archive). The app's own service worker files are left out — Haven serves the bundle and a second service worker would only fight it for the scope. The user pastes the app origin into Haven's application settings (the same URL as for external hosting); Haven fetches `/haven-bundle.json` from there, downloads the archive, rejects it unless every hash matches the manifest, and installs it.
|
|
1492
|
+
|
|
1493
|
+
Haven then owns updates. It re-reads the manifest before launches and on a slow background timer, and installs a new version when the `contentHash` changes — which means an app in hosted mode should **not** run its own update logic:
|
|
1494
|
+
|
|
1495
|
+
```ts
|
|
1496
|
+
import { isHostedBundleRuntime } from "mindoodb-app-sdk";
|
|
1497
|
+
|
|
1498
|
+
if (!isHostedBundleRuntime()) {
|
|
1499
|
+
registerServiceWorker();
|
|
1500
|
+
}
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1503
|
+
Serving the manifest and archive to Haven needs CORS, since Haven is on a different origin than your app:
|
|
1504
|
+
|
|
1505
|
+
```
|
|
1506
|
+
/haven-bundle.json
|
|
1507
|
+
Access-Control-Allow-Origin: *
|
|
1508
|
+
Cache-Control: no-cache
|
|
1509
|
+
|
|
1510
|
+
/haven-bundle.zip
|
|
1511
|
+
Access-Control-Allow-Origin: *
|
|
1512
|
+
Cache-Control: public, max-age=300
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
The manifest must not be cached — it is the signal that a new version exists. The archive can be, because the manifest pins it by hash.
|
|
1516
|
+
|
|
1517
|
+
#### Storage in hosted mode
|
|
1518
|
+
|
|
1519
|
+
An opaque origin has no storage: touching `localStorage` throws a `SecurityError`. The host keeps a small amount of key/value state for the app instead, reachable at `session.storage`:
|
|
1520
|
+
|
|
1521
|
+
```ts
|
|
1522
|
+
await session.storage.set("my-app.theme", "dark");
|
|
1523
|
+
const theme = await session.storage.get("my-app.theme");
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
That is asynchronous, and most existing app code reads preferences synchronously during render. For those, install a shim before the app boots. It replaces the global `localStorage` with one hydrated from the host, serves reads from memory, and writes keys under the given prefixes back to the host in the background:
|
|
1527
|
+
|
|
1528
|
+
```ts
|
|
1529
|
+
// entry point, before importing the app
|
|
1530
|
+
await installHavenStorageShim({ persistPrefixes: ["my-app-"] });
|
|
1531
|
+
await import("./main");
|
|
1532
|
+
```
|
|
1533
|
+
|
|
1534
|
+
Outside hosted mode this does nothing and the real `localStorage` stays in place. Keys outside `persistPrefixes` still work but live only for the session — a deliberate choice, since the budget is meant for preferences.
|
|
1535
|
+
|
|
1536
|
+
Two things are worth knowing when you use it. Writes are coalesced, so a value written on every `pointermove` costs one message rather than sixty per second; still, prefer committing such a value once when the interaction ends. And the budget is finite (256 KB per installed app, 64 KB per value), so `setItem` can throw `QuotaExceededError` just as it can in a browser. Anything larger than a preference belongs in a database.
|
|
1537
|
+
|
|
1538
|
+
Haven asks the app to flush pending writes before it tears the iframe down, so a preference set immediately before closing is not lost. The shim answers that on its own.
|
|
1476
1539
|
|
|
1477
1540
|
## Local development workflow
|
|
1478
1541
|
|
|
@@ -1510,10 +1573,25 @@ Connect options: `launchId?`, `targetOrigin?`, `connectTimeoutMs?`.
|
|
|
1510
1573
|
| `closeSealedChannel(channelId)` | `Promise<void>` |
|
|
1511
1574
|
| `menus.show(input)` | `Promise<MindooDBAppShowMenuResult>` |
|
|
1512
1575
|
| `menus.hide()` | `Promise<void>` |
|
|
1576
|
+
| `storage` | `MindooDBAppStorageApi` |
|
|
1513
1577
|
| `onThemeChange(listener)` | `() => void` (unsubscribe) |
|
|
1514
1578
|
| `onViewportChange(listener)` | `() => void` (unsubscribe) |
|
|
1579
|
+
| `onBeforeClose(listener)` | `() => void` (unsubscribe) |
|
|
1515
1580
|
| `disconnect()` | `Promise<void>` |
|
|
1516
1581
|
|
|
1582
|
+
### MindooDBAppStorageApi
|
|
1583
|
+
|
|
1584
|
+
Host-kept key/value state, the only durable storage a hosted app has. See [Storage in hosted mode](#storage-in-hosted-mode).
|
|
1585
|
+
|
|
1586
|
+
| Method | Returns |
|
|
1587
|
+
| --------------------- | ----------------------------------- |
|
|
1588
|
+
| `snapshot(options?)` | `Promise<Record<string, string>>` |
|
|
1589
|
+
| `get(key)` | `Promise<string \| null>` |
|
|
1590
|
+
| `set(key, value)` | `Promise<void>` |
|
|
1591
|
+
| `remove(key)` | `Promise<void>` |
|
|
1592
|
+
| `keys(options?)` | `Promise<string[]>` |
|
|
1593
|
+
| `clear(options?)` | `Promise<void>` |
|
|
1594
|
+
|
|
1517
1595
|
### MindooDBAppDatabase
|
|
1518
1596
|
|
|
1519
1597
|
| Property / Method | Returns |
|
|
@@ -1642,6 +1720,14 @@ interface MindooDBAppUpdateDocumentInput {
|
|
|
1642
1720
|
| `createViewLanguage<T>()` | Create a typed expression builder for view definitions |
|
|
1643
1721
|
| `abbreviateCanonicalName(value)` | Convert a canonical Notes-style name like `cn=Jane/ou=Dev/o=Mindoo` to `Jane/Dev/Mindoo` |
|
|
1644
1722
|
| `expandAbbreviatedName(value)` | Convert an abbreviated Notes-style name like `Jane/Dev/Mindoo` to `cn=Jane/ou=Dev/o=Mindoo` |
|
|
1723
|
+
| `isHostedBundleRuntime(search?)` | Whether Haven is serving this app as a hosted bundle rather than from its own URL |
|
|
1724
|
+
| `installHavenStorageShim(options?)` | Replace `localStorage` with a host-backed shim in hosted mode; a no-op everywhere else |
|
|
1725
|
+
|
|
1726
|
+
The Vite plugin lives behind its own entrypoint:
|
|
1727
|
+
|
|
1728
|
+
| Function | Import from | Description |
|
|
1729
|
+
| ----------------------- | ------------------------- | -------------------------------------------------------- |
|
|
1730
|
+
| `havenBundle(options?)` | `mindoodb-app-sdk/vite` | Emit `haven-bundle.json` and `haven-bundle.zip` on build |
|
|
1645
1731
|
|
|
1646
1732
|
## Permissions and mappings
|
|
1647
1733
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared description of a built application bundle that a MindooDB host can install
|
|
3
|
+
* and update from a URL.
|
|
4
|
+
*
|
|
5
|
+
* The build side (`mindoodb-app-sdk/vite`) writes the manifest, the host side reads and
|
|
6
|
+
* validates it. Both import this module so the format has exactly one definition.
|
|
7
|
+
*/
|
|
8
|
+
declare const MINDOODB_APP_BUNDLE_MANIFEST_FORMAT = "mindoodb.app.bundle";
|
|
9
|
+
declare const MINDOODB_APP_BUNDLE_MANIFEST_VERSION = 1;
|
|
10
|
+
declare const MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME = "haven-bundle.json";
|
|
11
|
+
declare const MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME = "haven-bundle.zip";
|
|
12
|
+
/** A `sha256-<64 lowercase hex chars>` digest string. */
|
|
13
|
+
type MindooDBAppBundleHash = string;
|
|
14
|
+
interface MindooDBAppBundleFileEntry {
|
|
15
|
+
/** Bundle-relative POSIX path, e.g. `assets/index-a1b2c3.js`. */
|
|
16
|
+
path: string;
|
|
17
|
+
hash: MindooDBAppBundleHash;
|
|
18
|
+
size: number;
|
|
19
|
+
}
|
|
20
|
+
interface MindooDBAppBundleArchive {
|
|
21
|
+
/** Path of the archive relative to the manifest URL. */
|
|
22
|
+
path: string;
|
|
23
|
+
hash: MindooDBAppBundleHash;
|
|
24
|
+
size: number;
|
|
25
|
+
}
|
|
26
|
+
interface MindooDBAppBundleManifest {
|
|
27
|
+
format: typeof MINDOODB_APP_BUNDLE_MANIFEST_FORMAT;
|
|
28
|
+
formatVersion: number;
|
|
29
|
+
/** Stable identifier of the application, used to detect a swapped bundle source. */
|
|
30
|
+
appId: string;
|
|
31
|
+
/** Human-readable version. Informational only — updates key off `contentHash`. */
|
|
32
|
+
version: string;
|
|
33
|
+
/** Entry HTML file, always present in `files`. */
|
|
34
|
+
entry: string;
|
|
35
|
+
archive: MindooDBAppBundleArchive;
|
|
36
|
+
/** Identity of the bundle: a digest over the sorted file list. */
|
|
37
|
+
contentHash: MindooDBAppBundleHash;
|
|
38
|
+
generatedAt: string;
|
|
39
|
+
files: MindooDBAppBundleFileEntry[];
|
|
40
|
+
}
|
|
41
|
+
declare function isMindooDBAppBundleHash(value: unknown): value is MindooDBAppBundleHash;
|
|
42
|
+
/**
|
|
43
|
+
* Mirrors the host-side path rules: POSIX separators, no leading slash, no `.`/`..`
|
|
44
|
+
* segments and no empty segments. Returns `null` for paths that must be rejected.
|
|
45
|
+
*/
|
|
46
|
+
declare function normalizeMindooDBAppBundlePath(rawPath: unknown): string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Canonical byte input for `contentHash`. Both the build and the host derive the digest
|
|
49
|
+
* from this exact string so a mismatch always means the content really differs.
|
|
50
|
+
*/
|
|
51
|
+
declare function buildMindooDBAppBundleContentHashInput(files: readonly Pick<MindooDBAppBundleFileEntry, "path" | "hash">[]): string;
|
|
52
|
+
interface MindooDBAppBundleManifestValidation {
|
|
53
|
+
manifest: MindooDBAppBundleManifest | null;
|
|
54
|
+
errors: string[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Structural validation only — it does not verify any digest. Callers still have to
|
|
58
|
+
* compare `contentHash` and the per-file hashes against the bytes they downloaded.
|
|
59
|
+
*/
|
|
60
|
+
declare function validateMindooDBAppBundleManifest(raw: unknown): MindooDBAppBundleManifestValidation;
|
|
61
|
+
|
|
62
|
+
export { MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME as M, MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME as a, MINDOODB_APP_BUNDLE_MANIFEST_FORMAT as b, MINDOODB_APP_BUNDLE_MANIFEST_VERSION as c, type MindooDBAppBundleArchive as d, type MindooDBAppBundleFileEntry as e, type MindooDBAppBundleHash as f, type MindooDBAppBundleManifest as g, type MindooDBAppBundleManifestValidation as h, buildMindooDBAppBundleContentHashInput as i, isMindooDBAppBundleHash as j, normalizeMindooDBAppBundlePath as n, validateMindooDBAppBundleManifest as v };
|