mindoodb-app-sdk 0.0.46 → 0.0.48

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 CHANGED
@@ -1084,6 +1084,43 @@ if (handled) {
1084
1084
  }
1085
1085
  ```
1086
1086
 
1087
+ **Nested lookups (`include`)** fetch related documents alongside the matching rows — from the same database or from any other database the app is mapped to. The host resolves a slot with **one scan per slot**, not one query per row, so a list with its related records is a single round trip:
1088
+
1089
+ ```ts
1090
+ const result = await db.documents.query<{
1091
+ customer: MindooDBAppQueryRow | null;
1092
+ lines: MindooDBAppQueryRow[];
1093
+ }>({
1094
+ filter: 'v.eq(v.field("type"), "invoice")',
1095
+ fields: ["total", "customerId"],
1096
+ include: {
1097
+ customer: {
1098
+ databaseId: "customers", // another mapped database; omit for this one
1099
+ cardinality: "one", // → a row or null
1100
+ localKey: "customerId", // this row's field holds the customer's id
1101
+ fields: ["name"],
1102
+ },
1103
+ lines: {
1104
+ cardinality: "many", // → an array (possibly empty, never null)
1105
+ filter: 'v.eq(v.field("invoiceId"), v.parentDocId())',
1106
+ sortBy: [{ field: "position", direction: "ascending" }],
1107
+ limit: 50,
1108
+ },
1109
+ },
1110
+ });
1111
+
1112
+ for (const row of result.rows) {
1113
+ console.log(row.fields.total, row.includes?.customer?.fields.name);
1114
+ console.log(row.includes?.lines.length);
1115
+ }
1116
+ ```
1117
+
1118
+ - Related documents live under `row.includes[slot]`, never as sibling keys, so a slot name can never collide with `docId` / `fields` / `lastModified`. Pass the slot types to `query<TIncludes>()` to have them typed.
1119
+ - The join condition relates the related document to the parent row: `v.parentDocId()` for the parent's id, `v.parent("<path>")` for one of its fields. `localKey` is shorthand for "this row's field holds the related document's id" (an array value joins every element). Exactly one such equality is allowed per slot — further conditions may narrow the related documents but must not mention the parent.
1120
+ - Slots nest (`row.includes.customer.includes.address`) up to three levels.
1121
+ - `databaseId` is checked like any other database parameter: the app must be mapped to it with the `read` capability, at every level. Unmapped or view-only ids are rejected with `database-not-found`, a missing capability with `forbidden`.
1122
+ - Hydration happens after sorting and paging, so only the returned rows are joined and `total` stays the unpaged match count. Per-slot `limit` is capped at 1000, and a query may carry at most 10 slots in total.
1123
+
1087
1124
  **Live queries** keep a query result up to date. The callback receives the initial result and runs again whenever the result actually changed — the host coalesces bursts of writes and fingerprints results, so you only hear about real changes:
1088
1125
 
1089
1126
  ```ts
@@ -1101,7 +1138,7 @@ await subscription.refresh();
1101
1138
  await subscription.dispose();
1102
1139
  ```
1103
1140
 
1104
- Live query results are pushed from the host over the bridge port — no polling. Subscriptions are cleaned up automatically when the app disconnects, but dispose them explicitly when the UI that consumes them unmounts.
1141
+ Live query results are pushed from the host over the bridge port — no polling. Subscriptions are cleaned up automatically when the app disconnects, but dispose them explicitly when the UI that consumes them unmounts. A live query with `include` also watches every related database, so a change to a joined document delivers a new result too.
1105
1142
 
1106
1143
  ### Incremental sync
1107
1144
 
@@ -1638,8 +1675,8 @@ Host-kept key/value state, the only durable storage a hosted app has. See [Stora
1638
1675
 
1639
1676
  | Method | Returns |
1640
1677
  | ---------------------------------- | -------------------------------------------- |
1641
- | `query(query?)` | `Promise<MindooDBAppQueryResult>` |
1642
- | `liveQuery(query, onResult)` | `Promise<MindooDBAppLiveQuerySubscription>` |
1678
+ | `query<TIncludes>(query?)` | `Promise<MindooDBAppQueryResult<TIncludes>>` |
1679
+ | `liveQuery<TIncludes>(query, onResult)` | `Promise<MindooDBAppLiveQuerySubscription>` |
1643
1680
  | `list(query?)` | `Promise<MindooDBAppDocumentListResult>` |
1644
1681
  | `listInaccessible(query?)` | `Promise<MindooDBAppInaccessibleDocumentListResult>` |
1645
1682
  | `get(docId)` | `Promise<MindooDBAppDocument \| null>` |
package/TESTING.md CHANGED
@@ -183,6 +183,41 @@ await db.documents.create({ set: { type: "invoice", total: 50 } });
183
183
  await sub.dispose();
184
184
  ```
185
185
 
186
+ Nested lookups (`include`) work too, including slots that name another mocked
187
+ database by its id, and they mirror the host's rules — one parent equality per
188
+ slot, `cardinality: "one"` failing loudly when several documents match, per-slot
189
+ `sortBy`/`limit`, and nesting. A write to a joined database re-fires the live
190
+ queries that join it, so cross-database joins can be tested end to end:
191
+
192
+ ```ts
193
+ const mock = createMockMindooDBAppBridge({
194
+ databases: [
195
+ {
196
+ info: { id: "billing", title: "Billing", capabilities: ["read", "create"] },
197
+ documents: [{ id: "inv_1", data: { type: "invoice", customerId: "cust_1" } }],
198
+ },
199
+ {
200
+ info: { id: "customers", title: "Customers", capabilities: ["read"] },
201
+ documents: [{ id: "cust_1", data: { name: "Acme" } }],
202
+ },
203
+ ],
204
+ });
205
+
206
+ const session = await mock.bridge.connect();
207
+ const billing = await session.openDatabase("billing");
208
+
209
+ const result = await billing.documents.query<{ customer: MindooDBAppQueryRow | null }>({
210
+ filter: 'v.eq(v.field("type"), "invoice")',
211
+ include: {
212
+ customer: { databaseId: "customers", cardinality: "one", localKey: "customerId" },
213
+ },
214
+ });
215
+ expect(result.rows[0].includes?.customer?.fields.name).toBe("Acme");
216
+ ```
217
+
218
+ An include slot naming a database that was not seeded fails, the same way Haven
219
+ rejects a database the app is not mapped to.
220
+
186
221
  ## Level 2 example
187
222
 
188
223
  This pattern keeps the real `createMindooDBAppBridge()` code path and replaces only the host side.
@@ -658,14 +658,28 @@ function normalizeDocumentQuery(query) {
658
658
  if (!query) {
659
659
  return {};
660
660
  }
661
- const { filter, ...rest } = query;
661
+ const { filter, include, ...rest } = query;
662
662
  return {
663
663
  ...rest,
664
- ...filter !== void 0 ? {
665
- filter: typeof filter === "string" ? parseMindooDBFormulaBooleanExpression(filter) : filter
666
- } : {}
664
+ ...filter !== void 0 ? { filter: normalizeQueryFilter(filter) } : {},
665
+ ...include !== void 0 ? { include: normalizeQueryIncludes(include) } : {}
667
666
  };
668
667
  }
668
+ function normalizeQueryFilter(filter) {
669
+ return typeof filter === "string" ? parseMindooDBFormulaBooleanExpression(filter) : filter;
670
+ }
671
+ function normalizeQueryIncludes(includes) {
672
+ const normalized = {};
673
+ for (const [slot, include] of Object.entries(includes)) {
674
+ const { filter, include: nested, ...rest } = include;
675
+ normalized[slot] = {
676
+ ...rest,
677
+ ...filter !== void 0 ? { filter: normalizeQueryFilter(filter) } : {},
678
+ ...nested !== void 0 ? { include: normalizeQueryIncludes(nested) } : {}
679
+ };
680
+ }
681
+ return normalized;
682
+ }
669
683
  function isThemeChangedMessage(message) {
670
684
  return message.kind === "theme-changed";
671
685
  }
@@ -1766,6 +1780,13 @@ var MindooDBAppSessionImpl = class {
1766
1780
  async getLicensedProducts() {
1767
1781
  return await this.rpc.call("session.getLicensedProducts", {});
1768
1782
  }
1783
+ /**
1784
+ * Asks the host to install another app. Only a URL crosses the bridge: the host
1785
+ * reads that origin's `haven-app.json` itself and asks the user.
1786
+ */
1787
+ async proposeApp(input) {
1788
+ return await this.rpc.call("apps.propose", input);
1789
+ }
1769
1790
  /** Lists the databases that the host has made available for this app session. */
1770
1791
  async listDatabases() {
1771
1792
  return await this.rpc.call("session.listDatabases", {});
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MindooDBAppAttachmentPreviewMode, a as MindooDBAppDragBindSourceOptions, b as MindooDBAppDragStartInput, c as MindooDBAppDragStartResult, d as MindooDBAppDragPreview, e as MindooDBAppBridge, f as MindooDBAppHostShortcutBinding, g as MindooDBAppHostShortcutAction, h as MindooDBAppHostingMode, i as MindooDBAppDatabase, j as MindooDBAppDocument, k as MindooDBAppRichTextSpan, l as MindooDBAppRichTextSnapshot } from './types-BaXvRKXY.js';
2
- export { m as MINDOODB_APP_SEAL_SIGNING_DOMAIN, n as MINDOODB_APP_WELL_KNOWN_DRAG_TYPES, o as MindooDBAppAccessDecision, p as MindooDBAppAccessTier, q as MindooDBAppAttachmentApi, r as MindooDBAppAttachmentChunk, s as MindooDBAppAttachmentInfo, t as MindooDBAppAttachmentPreviewOptions, u as MindooDBAppAttachmentPreviewSession, v as MindooDBAppAutomergeChangesPatch, w as MindooDBAppAutomergeChangesSince, x as MindooDBAppAutomergeGetOptions, y as MindooDBAppAutomergePatchResult, z as MindooDBAppAutomergeSnapshot, A as MindooDBAppBridgeBeforeCloseAck, B as MindooDBAppBridgeBeforeCloseMessage, C as MindooDBAppBridgeConnectMessage, D as MindooDBAppBridgeConnectOptions, E as MindooDBAppBridgeConnectedMessage, F as MindooDBAppBridgeDragDropMessage, G as MindooDBAppBridgeDragLeaveMessage, H as MindooDBAppBridgeDragOverMessage, I as MindooDBAppBridgeErrorPayload, J as MindooDBAppBridgeHandshakeErrorMessage, K as MindooDBAppBridgeLocaleChangedMessage, L as MindooDBAppBridgePortMessage, N as MindooDBAppBridgeQueryResultMessage, O as MindooDBAppBridgeRpcError, P as MindooDBAppBridgeRpcMessage, Q as MindooDBAppBridgeRpcRequest, R as MindooDBAppBridgeRpcSuccess, S as MindooDBAppBridgeShortcutInvokedMessage, T as MindooDBAppBridgeShortcutsChangedMessage, U as MindooDBAppBridgeStreamAbortRequest, V as MindooDBAppBridgeStreamAck, W as MindooDBAppBridgeStreamCloseRequest, X as MindooDBAppBridgeStreamError, Y as MindooDBAppBridgeStreamMessage, Z as MindooDBAppBridgeStreamOpenResult, _ as MindooDBAppBridgeStreamReadRequest, $ as MindooDBAppBridgeStreamWriteRequest, a0 as MindooDBAppBridgeThemeChangedMessage, a1 as MindooDBAppBridgeUiPreferencesChangedMessage, a2 as MindooDBAppBridgeViewChangedMessage, a3 as MindooDBAppBridgeViewportChangedMessage, a4 as MindooDBAppCapability, a5 as MindooDBAppConfiguredViewCategorizationStyle, a6 as MindooDBAppConfiguredViewColumn, a7 as MindooDBAppConfiguredViewColumnRole, a8 as MindooDBAppConfiguredViewExpressionMode, a9 as MindooDBAppConfiguredViewFieldExpression, aa as MindooDBAppConfiguredViewFilterDefinition, ab as MindooDBAppConfiguredViewFilterMode, ac as MindooDBAppConfiguredViewFilterRule, ad as MindooDBAppConfiguredViewFormulaExpression, ae as MindooDBAppConfiguredViewFormulaFilterDefinition, af as MindooDBAppConfiguredViewPreviewMode, ag as MindooDBAppConfiguredViewRuleFilterDefinition, ah as MindooDBAppConfiguredViewRuleMatchMode, ai as MindooDBAppConfiguredViewRuleOperator, aj as MindooDBAppConfiguredViewValueExpression, ak as MindooDBAppCreateDocumentInput, al as MindooDBAppCreateKeyInfo, am as MindooDBAppCreateViewInput, an as MindooDBAppCreateViewNavigatorInput, ao as MindooDBAppDatabaseInfo, ap as MindooDBAppDirectoryApi, aq as MindooDBAppDirectoryEntry, ar as MindooDBAppDocumentApi, as as MindooDBAppDocumentHeadCursorResult, at as MindooDBAppDocumentHistoryEntry, au as MindooDBAppDocumentListQuery, av as MindooDBAppDocumentListResult, aw as MindooDBAppDocumentQuery, ax as MindooDBAppDocumentRevisionId, ay as MindooDBAppDocumentScanPreset, az as MindooDBAppDocumentSummary, aA as MindooDBAppDragApi, aB as MindooDBAppDragCancelReason, aC as MindooDBAppDragDropEvent, aD as MindooDBAppDragEffect, aE as MindooDBAppDragHoverResult, aF as MindooDBAppDragOffer, aG as MindooDBAppDragOverEvent, aH as MindooDBAppDragPointer, aI as MindooDBAppDragProfile, aJ as MindooDBAppEntryAttachmentRef, aK as MindooDBAppEntryProvenance, aL as MindooDBAppExtractDocumentTextInput, aM as MindooDBAppExtractDocumentTextResult, aN as MindooDBAppExtractTextFormat, aO as MindooDBAppExtractionSetup, aP as MindooDBAppFulltextSetup, aQ as MindooDBAppHistoricalDocument, aR as MindooDBAppHistoryQuery, aS as MindooDBAppHostTheme, aT as MindooDBAppIdentityApi, aU as MindooDBAppInaccessibleDocument, aV as MindooDBAppInaccessibleDocumentListQuery, aW as MindooDBAppInaccessibleDocumentListResult, aX as MindooDBAppJsonListDeletePatch, aY as MindooDBAppJsonListInsertPatch, aZ as MindooDBAppJsonPatch, a_ as MindooDBAppJsonSetPatch, a$ as MindooDBAppJsonTextMarkPatch, b0 as MindooDBAppJsonTextSplicePatch, b1 as MindooDBAppJsonTextUnmarkPatch, b2 as MindooDBAppJsonUnsetPatch, b3 as MindooDBAppLaunchContext, b4 as MindooDBAppLiveQuerySubscription, b5 as MindooDBAppMenuAnchor, b6 as MindooDBAppMenuApi, b7 as MindooDBAppMenuCommandItem, b8 as MindooDBAppMenuDismissReason, b9 as MindooDBAppMenuDismissedResult, ba as MindooDBAppMenuItem, bb as MindooDBAppMenuKind, bc as MindooDBAppMenuPlacement, bd as MindooDBAppMenuPointAnchor, be as MindooDBAppMenuRect, bf as MindooDBAppMenuRectAnchor, bg as MindooDBAppMenuSelectionResult, bh as MindooDBAppMenuSeparatorItem, bi as MindooDBAppQueryCoverage, bj as MindooDBAppQueryResult, bk as MindooDBAppQueryRow, bl as MindooDBAppQuerySortKey, bm as MindooDBAppQueryTextClause, bn as MindooDBAppReadableAttachmentStream, bo as MindooDBAppRecipientOptions, bp as MindooDBAppResolvedViewDefinition, bq as MindooDBAppResolvedViewSource, br as MindooDBAppRevisionPhase, bs as MindooDBAppRevisionVerification, bt as MindooDBAppRichTextBlockSpan, bu as MindooDBAppRichTextGetOptions, bv as MindooDBAppRichTextImmutableString, bw as MindooDBAppRichTextMarkRange, bx as MindooDBAppRichTextMaterializeValue, by as MindooDBAppRichTextPatch, bz as MindooDBAppRichTextScalar, bA as MindooDBAppRichTextSpliceStep, bB as MindooDBAppRichTextStep, bC as MindooDBAppRichTextStepPatch, bD as MindooDBAppRichTextTextSpan, bE as MindooDBAppRuntime, bF as MindooDBAppScanAttachmentOptions, bG as MindooDBAppScanAttachmentResult, bH as MindooDBAppScopedDocId, bI as MindooDBAppScopedDocumentSummary, bJ as MindooDBAppSealIntent, bK as MindooDBAppSealedChannelOpenInput, bL as MindooDBAppSealedChannelOpenResult, bM as MindooDBAppSealedChannelRequestInput, bN as MindooDBAppSession, bO as MindooDBAppShowMenuInput, bP as MindooDBAppShowMenuResult, bQ as MindooDBAppSignPreview, bR as MindooDBAppSignPreviewScope, bS as MindooDBAppSignStatementInput, bT as MindooDBAppSignStatementResult, bU as MindooDBAppStorageApi, bV as MindooDBAppTextEdit, bW as MindooDBAppTextPatch, bX as MindooDBAppThemeMode, bY as MindooDBAppTimestampApi, bZ as MindooDBAppTimestampInput, b_ as MindooDBAppTimestampProvider, b$ as MindooDBAppTimestampResult, c0 as MindooDBAppUiPreferences, c1 as MindooDBAppUpdateDocumentInput, c2 as MindooDBAppViewCursorDocumentListResult, c3 as MindooDBAppViewCursorResult, c4 as MindooDBAppViewEntry, c5 as MindooDBAppViewEntryKind, c6 as MindooDBAppViewNavigator, c7 as MindooDBAppViewNavigatorExpansionState, c8 as MindooDBAppViewNavigatorOpenOptions, c9 as MindooDBAppViewNavigatorPageOptions, ca as MindooDBAppViewNavigatorPageResult, cb as MindooDBAppViewNavigatorRangeQuery, cc as MindooDBAppViewNavigatorSelectionState, cd as MindooDBAppViewUpdateStats, ce as MindooDBAppViewport, cf as MindooDBAppWellKnownDragType, cg as MindooDBAppWritableAttachmentStream } from './types-BaXvRKXY.js';
1
+ import { M as MindooDBAppAttachmentPreviewMode, a as MindooDBAppDragBindSourceOptions, b as MindooDBAppDragStartInput, c as MindooDBAppDragStartResult, d as MindooDBAppDragPreview, e as MindooDBAppBridge, f as MindooDBAppHostShortcutBinding, g as MindooDBAppHostShortcutAction, h as MindooDBAppHostingMode, i as MindooDBAppDatabase, j as MindooDBAppDocument, k as MindooDBAppRichTextSpan, l as MindooDBAppRichTextSnapshot } from './types-C-eyC839.js';
2
+ export { m as MINDOODB_APP_DEFINITION_FILE_NAME, n as MINDOODB_APP_DEFINITION_FORMAT, o as MINDOODB_APP_DEFINITION_PERMISSIONS, p as MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS, q as MINDOODB_APP_DEFINITION_VERSION, r as MINDOODB_APP_SEAL_SIGNING_DOMAIN, s as MINDOODB_APP_WELL_KNOWN_DRAG_TYPES, t as MindooDBAppAccessDecision, u as MindooDBAppAccessTier, v as MindooDBAppAttachmentApi, w as MindooDBAppAttachmentChunk, x as MindooDBAppAttachmentInfo, y as MindooDBAppAttachmentPreviewOptions, z as MindooDBAppAttachmentPreviewSession, A as MindooDBAppAutomergeChangesPatch, B as MindooDBAppAutomergeChangesSince, C as MindooDBAppAutomergeGetOptions, D as MindooDBAppAutomergePatchResult, E as MindooDBAppAutomergeSnapshot, F as MindooDBAppBridgeBeforeCloseAck, G as MindooDBAppBridgeBeforeCloseMessage, H as MindooDBAppBridgeConnectMessage, I as MindooDBAppBridgeConnectOptions, J as MindooDBAppBridgeConnectedMessage, K as MindooDBAppBridgeDragDropMessage, L as MindooDBAppBridgeDragLeaveMessage, N as MindooDBAppBridgeDragOverMessage, O as MindooDBAppBridgeErrorPayload, P as MindooDBAppBridgeHandshakeErrorMessage, Q as MindooDBAppBridgeLocaleChangedMessage, R as MindooDBAppBridgePortMessage, S as MindooDBAppBridgeQueryResultMessage, T as MindooDBAppBridgeRpcError, U as MindooDBAppBridgeRpcMessage, V as MindooDBAppBridgeRpcRequest, W as MindooDBAppBridgeRpcSuccess, X as MindooDBAppBridgeShortcutInvokedMessage, Y as MindooDBAppBridgeShortcutsChangedMessage, Z as MindooDBAppBridgeStreamAbortRequest, _ as MindooDBAppBridgeStreamAck, $ as MindooDBAppBridgeStreamCloseRequest, a0 as MindooDBAppBridgeStreamError, a1 as MindooDBAppBridgeStreamMessage, a2 as MindooDBAppBridgeStreamOpenResult, a3 as MindooDBAppBridgeStreamReadRequest, a4 as MindooDBAppBridgeStreamWriteRequest, a5 as MindooDBAppBridgeThemeChangedMessage, a6 as MindooDBAppBridgeUiPreferencesChangedMessage, a7 as MindooDBAppBridgeViewChangedMessage, a8 as MindooDBAppBridgeViewportChangedMessage, a9 as MindooDBAppCapability, aa as MindooDBAppConfiguredViewCategorizationStyle, ab as MindooDBAppConfiguredViewColumn, ac as MindooDBAppConfiguredViewColumnRole, ad as MindooDBAppConfiguredViewExpressionMode, ae as MindooDBAppConfiguredViewFieldExpression, af as MindooDBAppConfiguredViewFilterDefinition, ag as MindooDBAppConfiguredViewFilterMode, ah as MindooDBAppConfiguredViewFilterRule, ai as MindooDBAppConfiguredViewFormulaExpression, aj as MindooDBAppConfiguredViewFormulaFilterDefinition, ak as MindooDBAppConfiguredViewPreviewMode, al as MindooDBAppConfiguredViewRuleFilterDefinition, am as MindooDBAppConfiguredViewRuleMatchMode, an as MindooDBAppConfiguredViewRuleOperator, ao as MindooDBAppConfiguredViewValueExpression, ap as MindooDBAppCreateDocumentInput, aq as MindooDBAppCreateKeyInfo, ar as MindooDBAppCreateViewInput, as as MindooDBAppCreateViewNavigatorInput, at as MindooDBAppDatabaseInfo, au as MindooDBAppDefinition, av as MindooDBAppDefinitionDatabase, aw as MindooDBAppDefinitionPermission, ax as MindooDBAppDefinitionRegistrationPermission, ay as MindooDBAppDefinitionValidation, az as MindooDBAppDirectoryApi, aA as MindooDBAppDirectoryEntry, aB as MindooDBAppDocumentApi, aC as MindooDBAppDocumentHeadCursorResult, aD as MindooDBAppDocumentHistoryEntry, aE as MindooDBAppDocumentListQuery, aF as MindooDBAppDocumentListResult, aG as MindooDBAppDocumentQuery, aH as MindooDBAppDocumentRevisionId, aI as MindooDBAppDocumentScanPreset, aJ as MindooDBAppDocumentSummary, aK as MindooDBAppDragApi, aL as MindooDBAppDragCancelReason, aM as MindooDBAppDragDropEvent, aN as MindooDBAppDragEffect, aO as MindooDBAppDragHoverResult, aP as MindooDBAppDragOffer, aQ as MindooDBAppDragOverEvent, aR as MindooDBAppDragPointer, aS as MindooDBAppDragProfile, aT as MindooDBAppEntryAttachmentRef, aU as MindooDBAppEntryProvenance, aV as MindooDBAppExtractDocumentTextInput, aW as MindooDBAppExtractDocumentTextResult, aX as MindooDBAppExtractTextFormat, aY as MindooDBAppExtractionSetup, aZ as MindooDBAppFulltextSetup, a_ as MindooDBAppHistoricalDocument, a$ as MindooDBAppHistoryQuery, b0 as MindooDBAppHostTheme, b1 as MindooDBAppIdentityApi, b2 as MindooDBAppInaccessibleDocument, b3 as MindooDBAppInaccessibleDocumentListQuery, b4 as MindooDBAppInaccessibleDocumentListResult, b5 as MindooDBAppJsonListDeletePatch, b6 as MindooDBAppJsonListInsertPatch, b7 as MindooDBAppJsonPatch, b8 as MindooDBAppJsonSetPatch, b9 as MindooDBAppJsonTextMarkPatch, ba as MindooDBAppJsonTextSplicePatch, bb as MindooDBAppJsonTextUnmarkPatch, bc as MindooDBAppJsonUnsetPatch, bd as MindooDBAppLaunchContext, be as MindooDBAppLiveQuerySubscription, bf as MindooDBAppMenuAnchor, bg as MindooDBAppMenuApi, bh as MindooDBAppMenuCommandItem, bi as MindooDBAppMenuDismissReason, bj as MindooDBAppMenuDismissedResult, bk as MindooDBAppMenuItem, bl as MindooDBAppMenuKind, bm as MindooDBAppMenuPlacement, bn as MindooDBAppMenuPointAnchor, bo as MindooDBAppMenuRect, bp as MindooDBAppMenuRectAnchor, bq as MindooDBAppMenuSelectionResult, br as MindooDBAppMenuSeparatorItem, bs as MindooDBAppProposeAppInput, bt as MindooDBAppProposeAppResult, bu as MindooDBAppQueryCoverage, bv as MindooDBAppQueryInclude, bw as MindooDBAppQueryIncludeCardinality, bx as MindooDBAppQueryIncludeSlots, by as MindooDBAppQueryResult, bz as MindooDBAppQueryRow, bA as MindooDBAppQuerySortKey, bB as MindooDBAppQueryTextClause, bC as MindooDBAppReadableAttachmentStream, bD as MindooDBAppRecipientOptions, bE as MindooDBAppResolvedViewDefinition, bF as MindooDBAppResolvedViewSource, bG as MindooDBAppRevisionPhase, bH as MindooDBAppRevisionVerification, bI as MindooDBAppRichTextBlockSpan, bJ as MindooDBAppRichTextGetOptions, bK as MindooDBAppRichTextImmutableString, bL as MindooDBAppRichTextMarkRange, bM as MindooDBAppRichTextMaterializeValue, bN as MindooDBAppRichTextPatch, bO as MindooDBAppRichTextScalar, bP as MindooDBAppRichTextSpliceStep, bQ as MindooDBAppRichTextStep, bR as MindooDBAppRichTextStepPatch, bS as MindooDBAppRichTextTextSpan, bT as MindooDBAppRuntime, bU as MindooDBAppScanAttachmentOptions, bV as MindooDBAppScanAttachmentResult, bW as MindooDBAppScopedDocId, bX as MindooDBAppScopedDocumentSummary, bY as MindooDBAppSealIntent, bZ as MindooDBAppSealedChannelOpenInput, b_ as MindooDBAppSealedChannelOpenResult, b$ as MindooDBAppSealedChannelRequestInput, c0 as MindooDBAppSession, c1 as MindooDBAppShowMenuInput, c2 as MindooDBAppShowMenuResult, c3 as MindooDBAppSignPreview, c4 as MindooDBAppSignPreviewScope, c5 as MindooDBAppSignStatementInput, c6 as MindooDBAppSignStatementResult, c7 as MindooDBAppStorageApi, c8 as MindooDBAppTextEdit, c9 as MindooDBAppTextPatch, ca as MindooDBAppThemeMode, cb as MindooDBAppTimestampApi, cc as MindooDBAppTimestampInput, cd as MindooDBAppTimestampProvider, ce as MindooDBAppTimestampResult, cf as MindooDBAppUiPreferences, cg as MindooDBAppUpdateDocumentInput, ch as MindooDBAppViewCursorDocumentListResult, ci as MindooDBAppViewCursorResult, cj as MindooDBAppViewEntry, ck as MindooDBAppViewEntryKind, cl as MindooDBAppViewNavigator, cm as MindooDBAppViewNavigatorExpansionState, cn as MindooDBAppViewNavigatorOpenOptions, co as MindooDBAppViewNavigatorPageOptions, cp as MindooDBAppViewNavigatorPageResult, cq as MindooDBAppViewNavigatorRangeQuery, cr as MindooDBAppViewNavigatorSelectionState, cs as MindooDBAppViewUpdateStats, ct as MindooDBAppViewport, cu as MindooDBAppWellKnownDragType, cv as MindooDBAppWritableAttachmentStream, cw as resolveMindooDBAppDefinitionUrl, cx as validateMindooDBAppDefinition } from './types-C-eyC839.js';
3
3
  export { M as MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME, a as MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME, b as MINDOODB_APP_BUNDLE_MANIFEST_FORMAT, c as MINDOODB_APP_BUNDLE_MANIFEST_VERSION, d as MindooDBAppBundleArchive, e as MindooDBAppBundleFileEntry, f as MindooDBAppBundleHash, g as MindooDBAppBundleManifest, h as MindooDBAppBundleManifestValidation, i as buildMindooDBAppBundleContentHashInput, j as isMindooDBAppBundleHash, n as normalizeMindooDBAppBundlePath, v as validateMindooDBAppBundleManifest } from './appBundleManifest-B0xfpaXG.js';
4
4
  import { MindooDBAppBooleanExpression } from 'mindoodb-view-language';
5
5
  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';
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  releaseMindooDBAppBridgeSessions,
22
22
  sanitizeHostShortcutBindings,
23
23
  snapshotElementToPng
24
- } from "./chunk-XPPTUWW6.js";
24
+ } from "./chunk-TPYDERAK.js";
25
25
  import {
26
26
  MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
27
27
  MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
@@ -42,6 +42,265 @@ var MINDOODB_APP_WELL_KNOWN_DRAG_TYPES = [
42
42
  ];
43
43
  var MINDOODB_APP_SEAL_SIGNING_DOMAIN = "mindoodb-seal/v1";
44
44
 
45
+ // src/appDefinition.ts
46
+ var MINDOODB_APP_DEFINITION_FORMAT = "mindoodb.haven.app";
47
+ var MINDOODB_APP_DEFINITION_VERSION = 1;
48
+ var MINDOODB_APP_DEFINITION_FILE_NAME = "haven-app.json";
49
+ var MINDOODB_APP_DEFINITION_PERMISSIONS = [
50
+ "write",
51
+ "delete",
52
+ "history",
53
+ "attachments",
54
+ "views",
55
+ "sign",
56
+ "timestamps",
57
+ "directory",
58
+ "sealedchannel",
59
+ "proposeapps"
60
+ ];
61
+ var MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS = ["proposeapps"];
62
+ var LOGICAL_DATABASE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
63
+ function isPlainObject(value) {
64
+ return typeof value === "object" && value !== null && !Array.isArray(value);
65
+ }
66
+ function readOptionalString(raw, key, errors) {
67
+ const value = raw[key];
68
+ if (value === void 0 || value === null) {
69
+ return void 0;
70
+ }
71
+ if (typeof value !== "string") {
72
+ errors.push(`App definition ${key} must be a string.`);
73
+ return void 0;
74
+ }
75
+ const trimmed = value.trim();
76
+ return trimmed || void 0;
77
+ }
78
+ function readOptionalBoolean(raw, key, errors) {
79
+ const value = raw[key];
80
+ if (value === void 0 || value === null) {
81
+ return void 0;
82
+ }
83
+ if (typeof value !== "boolean") {
84
+ errors.push(`App definition ${key} must be a boolean.`);
85
+ return void 0;
86
+ }
87
+ return value || void 0;
88
+ }
89
+ function readStringRecord(raw, key, errors) {
90
+ const value = raw[key];
91
+ if (value === void 0 || value === null) {
92
+ return void 0;
93
+ }
94
+ if (!isPlainObject(value)) {
95
+ errors.push(`App definition ${key} must be an object of string values.`);
96
+ return void 0;
97
+ }
98
+ const result = {};
99
+ for (const [entryKey, entryValue] of Object.entries(value)) {
100
+ if (typeof entryValue !== "string") {
101
+ errors.push(`App definition ${key}.${entryKey} must be a string.`);
102
+ continue;
103
+ }
104
+ result[entryKey] = entryValue;
105
+ }
106
+ return Object.keys(result).length ? result : void 0;
107
+ }
108
+ function readPermissions(value, label, allowed, errors) {
109
+ if (value === void 0 || value === null) {
110
+ return void 0;
111
+ }
112
+ if (!Array.isArray(value)) {
113
+ errors.push(`${label} must be an array of permission names.`);
114
+ return void 0;
115
+ }
116
+ const result = [];
117
+ for (const entry of value) {
118
+ if (typeof entry !== "string" || !allowed.includes(entry)) {
119
+ errors.push(`${label} contains the unknown permission ${JSON.stringify(entry)}.`);
120
+ continue;
121
+ }
122
+ if (!result.includes(entry)) {
123
+ result.push(entry);
124
+ }
125
+ }
126
+ return result.length ? result : void 0;
127
+ }
128
+ function readNetworkAllowlist(value, errors) {
129
+ if (value === void 0 || value === null) {
130
+ return void 0;
131
+ }
132
+ if (!Array.isArray(value)) {
133
+ errors.push("App definition networkAllowlist must be an array of origins.");
134
+ return void 0;
135
+ }
136
+ const result = [];
137
+ for (const entry of value) {
138
+ if (typeof entry !== "string" || !entry.trim()) {
139
+ errors.push(`App definition networkAllowlist contains the invalid entry ${JSON.stringify(entry)}.`);
140
+ continue;
141
+ }
142
+ const trimmed = entry.trim();
143
+ if (!result.includes(trimmed)) {
144
+ result.push(trimmed);
145
+ }
146
+ }
147
+ return result.length ? result : void 0;
148
+ }
149
+ function readDatabases(value, errors) {
150
+ if (value === void 0 || value === null) {
151
+ return void 0;
152
+ }
153
+ if (!Array.isArray(value)) {
154
+ errors.push("App definition databases must be an array.");
155
+ return void 0;
156
+ }
157
+ const result = [];
158
+ const seen = /* @__PURE__ */ new Set();
159
+ value.forEach((entry, index) => {
160
+ if (!isPlainObject(entry)) {
161
+ errors.push(`App definition database #${index} must be an object.`);
162
+ return;
163
+ }
164
+ const logicalDatabaseId = typeof entry.logicalDatabaseId === "string" ? entry.logicalDatabaseId.trim() : "";
165
+ if (!logicalDatabaseId) {
166
+ errors.push(`App definition database #${index} is missing a non-empty logicalDatabaseId.`);
167
+ return;
168
+ }
169
+ if (!LOGICAL_DATABASE_ID_PATTERN.test(logicalDatabaseId)) {
170
+ errors.push(
171
+ `App definition database "${logicalDatabaseId}" must match ${String(LOGICAL_DATABASE_ID_PATTERN)}.`
172
+ );
173
+ return;
174
+ }
175
+ if (seen.has(logicalDatabaseId)) {
176
+ errors.push(`App definition lists the database "${logicalDatabaseId}" more than once.`);
177
+ return;
178
+ }
179
+ seen.add(logicalDatabaseId);
180
+ const databaseId = typeof entry.databaseId === "string" ? entry.databaseId.trim() : "";
181
+ if (entry.databaseId !== void 0 && entry.databaseId !== null && !databaseId) {
182
+ errors.push(`App definition database "${logicalDatabaseId}" has an empty databaseId.`);
183
+ }
184
+ result.push({
185
+ logicalDatabaseId,
186
+ label: typeof entry.label === "string" && entry.label.trim() ? entry.label.trim() : void 0,
187
+ databaseId: databaseId || void 0,
188
+ permissions: readPermissions(
189
+ entry.permissions,
190
+ `App definition database "${logicalDatabaseId}" permissions`,
191
+ MINDOODB_APP_DEFINITION_PERMISSIONS.filter(
192
+ (permission) => !MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS.includes(permission)
193
+ ),
194
+ errors
195
+ ),
196
+ create: entry.create === false ? false : void 0
197
+ });
198
+ });
199
+ return result.length ? result : void 0;
200
+ }
201
+ function validateMindooDBAppDefinition(raw) {
202
+ const errors = [];
203
+ if (!isPlainObject(raw)) {
204
+ return { definition: null, errors: ["App definition must be a JSON object."] };
205
+ }
206
+ if (raw.format !== MINDOODB_APP_DEFINITION_FORMAT) {
207
+ errors.push(
208
+ `App definition format must be "${MINDOODB_APP_DEFINITION_FORMAT}", received ${JSON.stringify(raw.format)}.`
209
+ );
210
+ }
211
+ if (raw.formatVersion !== MINDOODB_APP_DEFINITION_VERSION) {
212
+ errors.push(
213
+ `Unsupported app definition version ${JSON.stringify(raw.formatVersion)}, expected ${MINDOODB_APP_DEFINITION_VERSION}.`
214
+ );
215
+ }
216
+ const appId = typeof raw.appId === "string" ? raw.appId.trim() : "";
217
+ if (!appId) {
218
+ errors.push("App definition is missing a non-empty appId.");
219
+ }
220
+ const label = typeof raw.label === "string" ? raw.label.trim() : "";
221
+ if (!label) {
222
+ errors.push("App definition is missing a non-empty label.");
223
+ }
224
+ const runtime = raw.runtime;
225
+ if (runtime !== void 0 && runtime !== "iframe" && runtime !== "window") {
226
+ errors.push(`App definition runtime must be "iframe" or "window", received ${JSON.stringify(runtime)}.`);
227
+ }
228
+ const hosting = raw.hosting;
229
+ if (hosting !== void 0 && hosting !== "external" && hosting !== "hosted") {
230
+ errors.push(
231
+ `App definition hosting must be "external" or "hosted", received ${JSON.stringify(hosting)}.`
232
+ );
233
+ }
234
+ const description = readOptionalString(raw, "description", errors);
235
+ const version = readOptionalString(raw, "version", errors);
236
+ const defaultLaunchDatabaseId = readOptionalString(raw, "defaultLaunchDatabaseId", errors);
237
+ const networkAllowlist = readNetworkAllowlist(raw.networkAllowlist, errors);
238
+ const launchParameters = readStringRecord(raw, "launchParameters", errors);
239
+ const databases = readDatabases(raw.databases, errors);
240
+ const permissions = readPermissions(
241
+ raw.permissions,
242
+ "App definition permissions",
243
+ MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS,
244
+ errors
245
+ );
246
+ const allowPopups = readOptionalBoolean(raw, "allowPopups", errors);
247
+ const allowCamera = readOptionalBoolean(raw, "allowCamera", errors);
248
+ const allowMicrophone = readOptionalBoolean(raw, "allowMicrophone", errors);
249
+ const allowGeolocation = readOptionalBoolean(raw, "allowGeolocation", errors);
250
+ const allowWebRtc = readOptionalBoolean(raw, "allowWebRtc", errors);
251
+ const allowWorkers = readOptionalBoolean(raw, "allowWorkers", errors);
252
+ if (defaultLaunchDatabaseId && databases && !databases.some((database) => database.logicalDatabaseId === defaultLaunchDatabaseId)) {
253
+ errors.push(
254
+ `App definition defaultLaunchDatabaseId "${defaultLaunchDatabaseId}" is not one of the declared databases.`
255
+ );
256
+ }
257
+ if (errors.length) {
258
+ return { definition: null, errors };
259
+ }
260
+ return {
261
+ definition: {
262
+ format: MINDOODB_APP_DEFINITION_FORMAT,
263
+ formatVersion: MINDOODB_APP_DEFINITION_VERSION,
264
+ appId,
265
+ label,
266
+ description,
267
+ version,
268
+ runtime,
269
+ hosting,
270
+ networkAllowlist,
271
+ allowPopups,
272
+ allowCamera,
273
+ allowMicrophone,
274
+ allowGeolocation,
275
+ allowWebRtc,
276
+ allowWorkers,
277
+ permissions,
278
+ launchParameters,
279
+ defaultLaunchDatabaseId,
280
+ databases
281
+ },
282
+ errors: []
283
+ };
284
+ }
285
+ function resolveMindooDBAppDefinitionUrl(baseUrl) {
286
+ const trimmed = baseUrl.trim();
287
+ if (!trimmed) {
288
+ return "";
289
+ }
290
+ const suffix = `/${MINDOODB_APP_DEFINITION_FILE_NAME}`;
291
+ try {
292
+ const url = new URL(trimmed);
293
+ const path = url.pathname.replace(/\/+$/, "") || "/";
294
+ url.search = "";
295
+ url.hash = "";
296
+ url.pathname = path.endsWith(suffix) || path === suffix ? path : `${path === "/" ? "" : path}${suffix}`;
297
+ return url.toString();
298
+ } catch {
299
+ const withoutSlash = trimmed.replace(/\/+$/, "");
300
+ return withoutSlash.toLowerCase().endsWith(suffix) ? withoutSlash : `${withoutSlash}${suffix}`;
301
+ }
302
+ }
303
+
45
304
  // src/attachmentPreview.ts
46
305
  function canPreviewAttachment(fileName, mimeType) {
47
306
  const normalizedMimeType = mimeType.toLowerCase();
@@ -780,6 +1039,11 @@ export {
780
1039
  MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
781
1040
  MINDOODB_APP_BUNDLE_MANIFEST_FORMAT,
782
1041
  MINDOODB_APP_BUNDLE_MANIFEST_VERSION,
1042
+ MINDOODB_APP_DEFINITION_FILE_NAME,
1043
+ MINDOODB_APP_DEFINITION_FORMAT,
1044
+ MINDOODB_APP_DEFINITION_PERMISSIONS,
1045
+ MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS,
1046
+ MINDOODB_APP_DEFINITION_VERSION,
783
1047
  MINDOODB_APP_HOSTING_QUERY_PARAM,
784
1048
  MINDOODB_APP_MAX_DRAG_PAYLOAD_BYTES,
785
1049
  MINDOODB_APP_MAX_DRAG_PREVIEW_BYTES,
@@ -815,7 +1079,9 @@ export {
815
1079
  queryDocuments,
816
1080
  readMindooDBAppHostingMode,
817
1081
  releaseMindooDBAppBridgeSessions,
1082
+ resolveMindooDBAppDefinitionUrl,
818
1083
  sanitizeHostShortcutBindings,
819
1084
  snapshotElementToPng,
820
- validateMindooDBAppBundleManifest
1085
+ validateMindooDBAppBundleManifest,
1086
+ validateMindooDBAppDefinition
821
1087
  };
@@ -1,4 +1,4 @@
1
- import { an as MindooDBAppCreateViewNavigatorInput, c6 as MindooDBAppViewNavigator, b3 as MindooDBAppLaunchContext, ao as MindooDBAppDatabaseInfo, ar as MindooDBAppDocumentApi, c8 as MindooDBAppViewNavigatorOpenOptions, q as MindooDBAppAttachmentApi, aT as MindooDBAppIdentityApi, bY as MindooDBAppTimestampApi, ap as MindooDBAppDirectoryApi, j as MindooDBAppDocument, aP as MindooDBAppFulltextSetup, aO as MindooDBAppExtractionSetup, e as MindooDBAppBridge, bN as MindooDBAppSession, Q as MindooDBAppBridgeRpcRequest, aS as MindooDBAppHostTheme, ce as MindooDBAppViewport, c0 as MindooDBAppUiPreferences, bj as MindooDBAppQueryResult, cd as MindooDBAppViewUpdateStats, L as MindooDBAppBridgePortMessage, c as MindooDBAppDragStartResult, aI as MindooDBAppDragProfile } from '../types-BaXvRKXY.js';
1
+ import { as as MindooDBAppCreateViewNavigatorInput, cl as MindooDBAppViewNavigator, bd as MindooDBAppLaunchContext, at as MindooDBAppDatabaseInfo, aB as MindooDBAppDocumentApi, cn as MindooDBAppViewNavigatorOpenOptions, v as MindooDBAppAttachmentApi, b1 as MindooDBAppIdentityApi, cb as MindooDBAppTimestampApi, az as MindooDBAppDirectoryApi, j as MindooDBAppDocument, aZ as MindooDBAppFulltextSetup, aY as MindooDBAppExtractionSetup, bs as MindooDBAppProposeAppInput, bt as MindooDBAppProposeAppResult, e as MindooDBAppBridge, c0 as MindooDBAppSession, V as MindooDBAppBridgeRpcRequest, b0 as MindooDBAppHostTheme, ct as MindooDBAppViewport, cf as MindooDBAppUiPreferences, by as MindooDBAppQueryResult, cs as MindooDBAppViewUpdateStats, R as MindooDBAppBridgePortMessage, c as MindooDBAppDragStartResult, aS as MindooDBAppDragProfile } from '../types-C-eyC839.js';
2
2
  import * as mindoodb from 'mindoodb';
3
3
  import 'mindoodb-view-language';
4
4
 
@@ -98,6 +98,11 @@ interface CreateMockMindooDBAppSessionOptions {
98
98
  databases?: MockMindooDBAppDatabaseDefinition[];
99
99
  onDisconnect?: () => MaybePromise<void>;
100
100
  sealedChannel?: MockSealedChannelOptions;
101
+ /**
102
+ * Answer for `session.proposeApp`, standing in for the user's decision in the
103
+ * real host's consent dialog. Omitted means declined.
104
+ */
105
+ proposeApp?: (input: MindooDBAppProposeAppInput) => MindooDBAppProposeAppResult;
101
106
  /** Initial contents of `session.storage`. */
102
107
  storage?: Record<string, string>;
103
108
  }
@@ -107,6 +112,8 @@ interface MockMindooDBAppSessionController {
107
112
  getLaunchContext(): MindooDBAppLaunchContext;
108
113
  setLaunchContext(patch?: Partial<MindooDBAppLaunchContext>): MindooDBAppLaunchContext;
109
114
  getLicensedProducts(): string[];
115
+ /** Every `proposeApp` call the app under test made, in order. */
116
+ proposedApps: MindooDBAppProposeAppInput[];
110
117
  listDatabases(): MindooDBAppDatabaseInfo[];
111
118
  setDatabases(definitions: MockMindooDBAppDatabaseDefinition[]): void;
112
119
  emitThemeChange(theme: MindooDBAppHostTheme): void;
@@ -140,6 +147,8 @@ interface FakeBridgeHostController {
140
147
  session: MindooDBAppSession;
141
148
  readonly launchId: string;
142
149
  readonly requests: ReadonlyArray<MindooDBAppBridgeRpcRequest>;
150
+ /** Every `apps.propose` call the app under test made, in order. */
151
+ readonly proposedApps: ReadonlyArray<MindooDBAppProposeAppInput>;
143
152
  install(): void;
144
153
  dispose(): void;
145
154
  emitThemeChange(theme: MindooDBAppHostTheme): void;