mindoodb-app-sdk 0.0.49 → 0.0.53

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/dist/index.js CHANGED
@@ -6,8 +6,10 @@ import {
6
6
  MINDOODB_APP_MAX_DRAG_PREVIEW_BYTES,
7
7
  MINDOODB_APP_MAX_DRAG_PREVIEW_EDGE,
8
8
  MINDOODB_APP_MAX_DRAG_TYPES,
9
+ MINDOODB_APP_VALUE_TAG,
9
10
  MINIMAL_DRAG_PREVIEW_PNG,
10
11
  MindooDBAppDragInputError,
12
+ MindooDBAppValue,
11
13
  applyHostedDocumentOverscrollContain,
12
14
  bindDragSource,
13
15
  createMindooDBAppBridge,
@@ -21,7 +23,7 @@ import {
21
23
  releaseMindooDBAppBridgeSessions,
22
24
  sanitizeHostShortcutBindings,
23
25
  snapshotElementToPng
24
- } from "./chunk-TPYDERAK.js";
26
+ } from "./chunk-6NTVTL6G.js";
25
27
  import {
26
28
  MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
27
29
  MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
@@ -59,6 +61,9 @@ var MINDOODB_APP_DEFINITION_PERMISSIONS = [
59
61
  "proposeapps"
60
62
  ];
61
63
  var MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS = ["proposeapps"];
64
+ var MINDOODB_APP_LISTING_SUMMARY_MAX = 300;
65
+ var MINDOODB_APP_LISTING_DESCRIPTION_MAX = 4e3;
66
+ var MINDOODB_APP_LISTING_MAX_SCREENSHOTS = 8;
62
67
  var LOGICAL_DATABASE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
63
68
  function isPlainObject(value) {
64
69
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -198,6 +203,156 @@ function readDatabases(value, errors) {
198
203
  });
199
204
  return result.length ? result : void 0;
200
205
  }
206
+ function readLocalizedText(value, label, maxLength, errors) {
207
+ if (value === void 0 || value === null) {
208
+ return void 0;
209
+ }
210
+ if (typeof value === "string") {
211
+ const trimmed = value.trim();
212
+ if (trimmed.length > maxLength) {
213
+ errors.push(`${label} must not be longer than ${maxLength} characters.`);
214
+ return void 0;
215
+ }
216
+ return trimmed || void 0;
217
+ }
218
+ if (!isPlainObject(value)) {
219
+ errors.push(`${label} must be a string or an object of per-locale strings.`);
220
+ return void 0;
221
+ }
222
+ const result = {};
223
+ for (const [locale, text] of Object.entries(value)) {
224
+ if (typeof text !== "string") {
225
+ errors.push(`${label}.${locale} must be a string.`);
226
+ continue;
227
+ }
228
+ const trimmed = text.trim();
229
+ if (trimmed.length > maxLength) {
230
+ errors.push(`${label}.${locale} must not be longer than ${maxLength} characters.`);
231
+ continue;
232
+ }
233
+ if (trimmed) {
234
+ result[locale] = trimmed;
235
+ }
236
+ }
237
+ if (!Object.keys(result).length) {
238
+ return void 0;
239
+ }
240
+ if (!result.en) {
241
+ errors.push(`${label} must include an "en" entry as the fallback for other locales.`);
242
+ return void 0;
243
+ }
244
+ return result;
245
+ }
246
+ function readListingAssetPath(value, label, errors) {
247
+ if (value === void 0 || value === null) {
248
+ return void 0;
249
+ }
250
+ if (typeof value !== "string" || !value.trim()) {
251
+ errors.push(`${label} must be a non-empty string.`);
252
+ return void 0;
253
+ }
254
+ const trimmed = value.trim();
255
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed)?.[1]?.toLowerCase();
256
+ if (scheme && scheme !== "https") {
257
+ errors.push(`${label} must be a path on the app origin or an https: URL.`);
258
+ return void 0;
259
+ }
260
+ if (trimmed.startsWith("//")) {
261
+ errors.push(`${label} must be a path on the app origin or an https: URL.`);
262
+ return void 0;
263
+ }
264
+ return trimmed;
265
+ }
266
+ function readListing(value, errors) {
267
+ if (value === void 0 || value === null) {
268
+ return void 0;
269
+ }
270
+ if (!isPlainObject(value)) {
271
+ errors.push("App definition listing must be an object.");
272
+ return void 0;
273
+ }
274
+ const listing = {};
275
+ const summary = readLocalizedText(
276
+ value.summary,
277
+ "App definition listing.summary",
278
+ MINDOODB_APP_LISTING_SUMMARY_MAX,
279
+ errors
280
+ );
281
+ if (summary) listing.summary = summary;
282
+ const description = readLocalizedText(
283
+ value.description,
284
+ "App definition listing.description",
285
+ MINDOODB_APP_LISTING_DESCRIPTION_MAX,
286
+ errors
287
+ );
288
+ if (description) listing.description = description;
289
+ const icon = readListingAssetPath(value.icon, "App definition listing.icon", errors);
290
+ if (icon) listing.icon = icon;
291
+ if (value.screenshots !== void 0 && value.screenshots !== null) {
292
+ if (!Array.isArray(value.screenshots)) {
293
+ errors.push("App definition listing.screenshots must be an array.");
294
+ } else if (value.screenshots.length > MINDOODB_APP_LISTING_MAX_SCREENSHOTS) {
295
+ errors.push(
296
+ `App definition listing.screenshots must not list more than ${MINDOODB_APP_LISTING_MAX_SCREENSHOTS} images.`
297
+ );
298
+ } else {
299
+ const screenshots = [];
300
+ value.screenshots.forEach((entry, index) => {
301
+ const label = `App definition listing.screenshots[${index}]`;
302
+ const raw = typeof entry === "string" ? { file: entry } : entry;
303
+ if (!isPlainObject(raw)) {
304
+ errors.push(`${label} must be a path or an object with a file.`);
305
+ return;
306
+ }
307
+ const file = readListingAssetPath(raw.file, `${label}.file`, errors);
308
+ if (!file) {
309
+ return;
310
+ }
311
+ const caption = readLocalizedText(raw.caption, `${label}.caption`, MINDOODB_APP_LISTING_SUMMARY_MAX, errors);
312
+ screenshots.push(caption ? { file, caption } : { file });
313
+ });
314
+ if (screenshots.length) listing.screenshots = screenshots;
315
+ }
316
+ }
317
+ if (value.publisher !== void 0 && value.publisher !== null) {
318
+ const publisher = isPlainObject(value.publisher) ? value.publisher : {};
319
+ const name = typeof publisher.name === "string" ? publisher.name.trim() : "";
320
+ if (!name) {
321
+ errors.push("App definition listing.publisher must be an object with a non-empty name.");
322
+ } else {
323
+ const url = readListingAssetPath(publisher.url, "App definition listing.publisher.url", errors);
324
+ if (url && !/^https:\/\//i.test(url)) {
325
+ errors.push("App definition listing.publisher.url must be an absolute https: URL.");
326
+ } else {
327
+ listing.publisher = url ? { name, url } : { name };
328
+ }
329
+ }
330
+ }
331
+ return Object.keys(listing).length ? listing : void 0;
332
+ }
333
+ function resolveMindooDBAppLocalizedText(text, locale) {
334
+ if (text === void 0) {
335
+ return "";
336
+ }
337
+ if (typeof text === "string") {
338
+ return text;
339
+ }
340
+ const normalized = locale.trim();
341
+ const language = normalized.split(/[-_]/)[0]?.toLowerCase() ?? "";
342
+ return text[normalized] ?? text[normalized.toLowerCase()] ?? (language ? text[language] : void 0) ?? text.en;
343
+ }
344
+ function resolveMindooDBAppListingAssetUrl(path, originUrl) {
345
+ try {
346
+ const base = new URL(originUrl.endsWith("/") ? originUrl : `${originUrl}/`);
347
+ const resolved = new URL(path, base);
348
+ if (resolved.protocol !== "https:" && resolved.protocol !== base.protocol) {
349
+ return null;
350
+ }
351
+ return resolved.toString();
352
+ } catch {
353
+ return null;
354
+ }
355
+ }
201
356
  function validateMindooDBAppDefinition(raw) {
202
357
  const errors = [];
203
358
  if (!isPlainObject(raw)) {
@@ -249,6 +404,7 @@ function validateMindooDBAppDefinition(raw) {
249
404
  const allowGeolocation = readOptionalBoolean(raw, "allowGeolocation", errors);
250
405
  const allowWebRtc = readOptionalBoolean(raw, "allowWebRtc", errors);
251
406
  const allowWorkers = readOptionalBoolean(raw, "allowWorkers", errors);
407
+ const listing = readListing(raw.listing, errors);
252
408
  if (defaultLaunchDatabaseId && databases && !databases.some((database) => database.logicalDatabaseId === defaultLaunchDatabaseId)) {
253
409
  errors.push(
254
410
  `App definition defaultLaunchDatabaseId "${defaultLaunchDatabaseId}" is not one of the declared databases.`
@@ -277,7 +433,8 @@ function validateMindooDBAppDefinition(raw) {
277
433
  permissions,
278
434
  launchParameters,
279
435
  defaultLaunchDatabaseId,
280
- databases
436
+ databases,
437
+ listing
281
438
  },
282
439
  errors: []
283
440
  };
@@ -387,6 +544,22 @@ function expandAbbreviatedName(value) {
387
544
 
388
545
  // src/client/hosting.ts
389
546
  var MINDOODB_APP_HOSTING_QUERY_PARAM = "mindoodbAppHosting";
547
+ var MINDOODB_APP_LAUNCH_ID_QUERY_PARAM = "mindoodbAppLaunchId";
548
+ function readMindooDBAppLaunchId(search) {
549
+ const raw = search ?? (typeof window === "undefined" ? "" : window.location.search);
550
+ if (!raw) {
551
+ return null;
552
+ }
553
+ try {
554
+ const launchId = new URLSearchParams(raw).get(MINDOODB_APP_LAUNCH_ID_QUERY_PARAM)?.trim();
555
+ return launchId || null;
556
+ } catch {
557
+ return null;
558
+ }
559
+ }
560
+ function isLaunchedByHaven(search) {
561
+ return readMindooDBAppLaunchId(search) !== null;
562
+ }
390
563
  function readMindooDBAppHostingMode(search) {
391
564
  const raw = search ?? (typeof window === "undefined" ? "" : window.location.search);
392
565
  if (!raw) {
@@ -402,6 +575,195 @@ function isHostedBundleRuntime(search) {
402
575
  return readMindooDBAppHostingMode(search) === "hosted";
403
576
  }
404
577
 
578
+ // src/landing.ts
579
+ var DEFAULT_HAVEN_URL = "https://haven.mindoodb.com";
580
+ var APP_ICON_FALLBACKS = ["appicon.png", "appicon.webp", "appicon.svg"];
581
+ var STRINGS = {
582
+ en: {
583
+ eyebrow: "App for MindooDB Haven",
584
+ cta: "Open in MindooDB Haven",
585
+ ctaHint: "Haven sets up your encrypted workspace and installs {label}. Already using Haven? Then the app is simply added.",
586
+ from: "from {origin}",
587
+ by: "by {publisher}",
588
+ screenshots: "Screenshots",
589
+ footer: "{label} runs inside MindooDB Haven, which keeps its data end-to-end encrypted and local-first. Opening this address on its own shows only this page.",
590
+ learnMore: "What is MindooDB?",
591
+ fallbackSummary: "This app runs inside MindooDB Haven."
592
+ },
593
+ de: {
594
+ eyebrow: "App f\xFCr MindooDB Haven",
595
+ cta: "In MindooDB Haven \xF6ffnen",
596
+ ctaHint: "Haven richtet deine verschl\xFCsselte Umgebung ein und installiert {label}. Du nutzt Haven schon? Dann wird die App einfach hinzugef\xFCgt.",
597
+ from: "von {origin}",
598
+ by: "von {publisher}",
599
+ screenshots: "Screenshots",
600
+ footer: "{label} l\xE4uft in MindooDB Haven, das ihre Daten Ende-zu-Ende-verschl\xFCsselt und lokal h\xE4lt. Direkt aufgerufen zeigt diese Adresse nur diese Seite.",
601
+ learnMore: "Was ist MindooDB?",
602
+ fallbackSummary: "Diese App l\xE4uft in MindooDB Haven."
603
+ }
604
+ };
605
+ function stringsFor(locale) {
606
+ return locale.toLowerCase().startsWith("de") ? STRINGS.de : STRINGS.en;
607
+ }
608
+ function format(template, values) {
609
+ return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
610
+ }
611
+ function resolveCurrentHavenAppUrl(href = window.location.href) {
612
+ const url = new URL(href);
613
+ url.search = "";
614
+ url.hash = "";
615
+ url.pathname = url.pathname.replace(/\/index\.html?$/i, "/");
616
+ return url.toString();
617
+ }
618
+ function buildHavenAppInstallUrl(appUrl, havenUrl = DEFAULT_HAVEN_URL) {
619
+ const url = new URL(havenUrl);
620
+ url.searchParams.set("app", appUrl);
621
+ return url.toString();
622
+ }
623
+ async function loadDefinition(appUrl) {
624
+ try {
625
+ const definitionUrl = new URL(MINDOODB_APP_DEFINITION_FILE_NAME, appUrl.endsWith("/") ? appUrl : `${appUrl}/`);
626
+ const response = await fetch(definitionUrl, { headers: { Accept: "application/json" } });
627
+ if (!response.ok) {
628
+ return null;
629
+ }
630
+ return validateMindooDBAppDefinition(await response.json()).definition;
631
+ } catch {
632
+ return null;
633
+ }
634
+ }
635
+ function el(tag, className, text) {
636
+ const node = document.createElement(tag);
637
+ if (className) node.className = className;
638
+ if (text !== void 0) node.textContent = text;
639
+ return node;
640
+ }
641
+ var STYLE_ID = "mdb-landing-style";
642
+ var STYLE = `
643
+ .mdb-landing{--bg:#f4f6fb;--card:#fff;--text:#172033;--muted:#5b6478;--line:#e2e6ef;--accent:#1f3a8a;--accent-text:#fff;
644
+ min-height:100vh;box-sizing:border-box;margin:0;padding:48px 16px;background:var(--bg);color:var(--text);
645
+ font:16px/1.55 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
646
+ @media (prefers-color-scheme:dark){.mdb-landing{--bg:#0e1320;--card:#161d2e;--text:#e8ecf5;--muted:#9aa3b8;--line:#263049;--accent:#8fb0ff;--accent-text:#0e1320}}
647
+ .mdb-landing *{box-sizing:border-box}
648
+ .mdb-landing__card{max-width:880px;margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:24px;padding:40px;box-shadow:0 20px 60px rgba(15,23,42,.08)}
649
+ .mdb-landing__head{display:flex;gap:24px;align-items:center}
650
+ .mdb-landing__icon{width:96px;height:96px;border-radius:22px;object-fit:cover;flex:none;box-shadow:0 10px 30px rgba(15,23,42,.18)}
651
+ .mdb-landing__eyebrow{margin:0 0 4px;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
652
+ .mdb-landing__title{margin:0;font-size:clamp(26px,4vw,36px);line-height:1.15}
653
+ .mdb-landing__meta{margin:6px 0 0;color:var(--muted);font-size:14px}
654
+ .mdb-landing__summary{margin:28px 0 0;font-size:19px}
655
+ .mdb-landing__cta{display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin:28px 0 0}
656
+ .mdb-landing__button{display:inline-block;padding:14px 22px;border-radius:12px;background:var(--accent);color:var(--accent-text);font-weight:600;text-decoration:none}
657
+ .mdb-landing__button:focus-visible{outline:3px solid var(--accent);outline-offset:3px}
658
+ .mdb-landing__hint{margin:0;max-width:420px;color:var(--muted);font-size:14px}
659
+ .mdb-landing__description{margin:32px 0 0}
660
+ .mdb-landing__description p{margin:0 0 12px}
661
+ .mdb-landing__shots-title{margin:32px 0 12px;font-size:15px;color:var(--muted);font-weight:600}
662
+ .mdb-landing__shots{display:grid;grid-auto-flow:column;grid-auto-columns:min(78%,420px);gap:16px;overflow-x:auto;padding-bottom:8px;scroll-snap-type:x mandatory}
663
+ .mdb-landing__shot{margin:0;scroll-snap-align:start}
664
+ .mdb-landing__shot img{width:100%;border-radius:14px;border:1px solid var(--line);display:block}
665
+ .mdb-landing__shot figcaption{margin-top:8px;font-size:13px;color:var(--muted)}
666
+ .mdb-landing__footer{margin:36px 0 0;padding-top:20px;border-top:1px solid var(--line);color:var(--muted);font-size:13px}
667
+ .mdb-landing__footer a{color:inherit}
668
+ @media (max-width:560px){.mdb-landing{padding:16px}.mdb-landing__card{padding:24px;border-radius:18px}.mdb-landing__head{flex-direction:column;align-items:flex-start}.mdb-landing__icon{width:72px;height:72px}}
669
+ `;
670
+ async function renderHavenAppLandingPage(options = {}) {
671
+ const appUrl = options.appUrl ?? resolveCurrentHavenAppUrl();
672
+ const locale = options.locale ?? (typeof navigator === "undefined" ? "en" : navigator.language || "en");
673
+ const strings = stringsFor(locale);
674
+ const definition = options.definition ?? await loadDefinition(appUrl);
675
+ const installUrl = buildHavenAppInstallUrl(appUrl, options.havenUrl);
676
+ const listing = definition?.listing;
677
+ const label = definition?.label ?? (document.title || new URL(appUrl).host);
678
+ const origin = new URL(appUrl).host;
679
+ if (!document.getElementById(STYLE_ID)) {
680
+ const style = el("style");
681
+ style.id = STYLE_ID;
682
+ style.textContent = STYLE;
683
+ document.head.append(style);
684
+ }
685
+ document.title = label;
686
+ const root = el("main", "mdb-landing");
687
+ root.dataset.testid = "haven-app-landing";
688
+ const card = el("article", "mdb-landing__card");
689
+ root.append(card);
690
+ const head = el("header", "mdb-landing__head");
691
+ const iconCandidates = (listing?.icon ? [listing.icon] : [...APP_ICON_FALLBACKS]).map((path) => resolveMindooDBAppListingAssetUrl(path, appUrl)).filter((url) => Boolean(url));
692
+ if (iconCandidates.length) {
693
+ const icon = el("img", "mdb-landing__icon");
694
+ let next = 0;
695
+ icon.alt = "";
696
+ icon.addEventListener("error", () => {
697
+ next += 1;
698
+ if (next < iconCandidates.length) {
699
+ icon.src = iconCandidates[next];
700
+ } else {
701
+ icon.remove();
702
+ }
703
+ });
704
+ icon.src = iconCandidates[0];
705
+ head.append(icon);
706
+ }
707
+ const titles = el("div");
708
+ titles.append(el("p", "mdb-landing__eyebrow", strings.eyebrow), el("h1", "mdb-landing__title", label));
709
+ const meta = [format(strings.from, { origin })];
710
+ if (listing?.publisher) {
711
+ meta.unshift(format(strings.by, { publisher: listing.publisher.name }));
712
+ }
713
+ titles.append(el("p", "mdb-landing__meta", meta.join(" \xB7 ")));
714
+ head.append(titles);
715
+ card.append(head);
716
+ const summary = resolveMindooDBAppLocalizedText(listing?.summary, locale) || definition?.description || strings.fallbackSummary;
717
+ card.append(el("p", "mdb-landing__summary", summary));
718
+ const cta = el("div", "mdb-landing__cta");
719
+ const button = el("a", "mdb-landing__button", strings.cta);
720
+ button.href = installUrl;
721
+ button.rel = "noopener";
722
+ button.dataset.testid = "haven-app-landing-install";
723
+ cta.append(button, el("p", "mdb-landing__hint", format(strings.ctaHint, { label })));
724
+ card.append(cta);
725
+ const description = resolveMindooDBAppLocalizedText(listing?.description, locale);
726
+ if (description) {
727
+ const block = el("section", "mdb-landing__description");
728
+ for (const paragraph of description.split(/\n\s*\n/)) {
729
+ if (paragraph.trim()) block.append(el("p", void 0, paragraph.trim()));
730
+ }
731
+ card.append(block);
732
+ }
733
+ const screenshots = (listing?.screenshots ?? []).map((shot) => ({
734
+ url: resolveMindooDBAppListingAssetUrl(shot.file, appUrl),
735
+ caption: resolveMindooDBAppLocalizedText(shot.caption, locale)
736
+ })).filter((shot) => Boolean(shot.url));
737
+ if (screenshots.length) {
738
+ card.append(el("h2", "mdb-landing__shots-title", strings.screenshots));
739
+ const strip = el("div", "mdb-landing__shots");
740
+ for (const shot of screenshots) {
741
+ const figure = el("figure", "mdb-landing__shot");
742
+ const image = el("img");
743
+ image.src = shot.url;
744
+ image.alt = shot.caption;
745
+ image.loading = "lazy";
746
+ figure.append(image);
747
+ if (shot.caption) figure.append(el("figcaption", void 0, shot.caption));
748
+ strip.append(figure);
749
+ }
750
+ card.append(strip);
751
+ }
752
+ const footer = el("footer", "mdb-landing__footer");
753
+ footer.append(format(strings.footer, { label }), " ");
754
+ const learnMore = el("a", void 0, strings.learnMore);
755
+ learnMore.href = "https://mindoodb.com";
756
+ learnMore.rel = "noopener";
757
+ footer.append(learnMore);
758
+ card.append(footer);
759
+ const container = options.container ?? document.body;
760
+ container.replaceChildren(root);
761
+ if (container === document.body) {
762
+ document.body.style.margin = "0";
763
+ }
764
+ return { definition, installUrl };
765
+ }
766
+
405
767
  // src/client/havenStorageShim.ts
406
768
  var DEFAULT_QUOTA_BYTES = 256 * 1024;
407
769
  var DEFAULT_MAX_VALUE_BYTES = 64 * 1024;
@@ -729,11 +1091,11 @@ var MindooDBRichTextHandle = class {
729
1091
  this.pollTimer = null;
730
1092
  }
731
1093
  }
732
- reconcile(document, snapshot) {
1094
+ reconcile(document2, snapshot) {
733
1095
  const previous = JSON.stringify(this.spansInternal);
734
1096
  this.spansInternal = cloneSpans(snapshot.spans);
735
1097
  this.baseMaterializedText = spansToPlainText(snapshot.spans);
736
- this.heads = snapshot.heads ?? document.heads ?? [];
1098
+ this.heads = snapshot.heads ?? document2.heads ?? [];
737
1099
  this.dirtyInternal = false;
738
1100
  const changed = JSON.stringify(this.spansInternal) !== previous;
739
1101
  if (changed) {
@@ -752,14 +1114,14 @@ var MindooDBRichTextHandle = class {
752
1114
  if (this.dirtyInternal) {
753
1115
  return false;
754
1116
  }
755
- const document = await this.options.database.documents.get(
1117
+ const document2 = await this.options.database.documents.get(
756
1118
  this.options.document.id
757
1119
  );
758
- if (!document) {
1120
+ if (!document2) {
759
1121
  return false;
760
1122
  }
761
1123
  const snapshot = await this.refresh();
762
- return this.reconcile(document, snapshot);
1124
+ return this.reconcile(document2, snapshot);
763
1125
  }
764
1126
  async flush(updateSpansConfig) {
765
1127
  if (!this.dirtyInternal) {
@@ -776,7 +1138,7 @@ var MindooDBRichTextHandle = class {
776
1138
  const localSpansAtFlush = cloneSpans(this.spansInternal);
777
1139
  const localTextAtFlush = spansToPlainText(localSpansAtFlush);
778
1140
  const textChanged = localTextAtFlush !== this.baseMaterializedText;
779
- const document = textChanged ? await this.flushTextSteps(localTextAtFlush) : await this.options.database.documents.update(
1141
+ const document2 = textChanged ? await this.flushTextSteps(localTextAtFlush) : await this.options.database.documents.update(
780
1142
  this.options.document.id,
781
1143
  {
782
1144
  richText: [{
@@ -794,13 +1156,13 @@ var MindooDBRichTextHandle = class {
794
1156
  const reconciled = JSON.stringify(snapshot.spans) !== JSON.stringify(localSpansAtFlush);
795
1157
  this.spansInternal = cloneSpans(snapshot.spans);
796
1158
  this.baseMaterializedText = spansToPlainText(snapshot.spans);
797
- this.heads = snapshot.heads ?? document.heads ?? [];
1159
+ this.heads = snapshot.heads ?? document2.heads ?? [];
798
1160
  this.dirtyInternal = false;
799
1161
  if (reconciled) {
800
1162
  this.emitChange();
801
1163
  }
802
1164
  return {
803
- document,
1165
+ document: document2,
804
1166
  snapshot,
805
1167
  reconciled
806
1168
  };
@@ -938,11 +1300,11 @@ var MindooDBTextBuffer = class {
938
1300
  * Returns whether the text value changed. Use this after an external refresh
939
1301
  * or after receiving a document update through another app-level mechanism.
940
1302
  */
941
- reconcile(document) {
942
- const nextValue = readTextAtPath(document.data, this.options.path);
1303
+ reconcile(document2) {
1304
+ const nextValue = readTextAtPath(document2.data, this.options.path);
943
1305
  const changed = nextValue !== this.valueInternal;
944
1306
  this.valueInternal = nextValue;
945
- this.heads = document.heads ? [...document.heads] : [];
1307
+ this.heads = document2.heads ? [...document2.heads] : [];
946
1308
  this.pendingEdits = [];
947
1309
  return changed;
948
1310
  }
@@ -964,20 +1326,20 @@ var MindooDBTextBuffer = class {
964
1326
  };
965
1327
  }
966
1328
  const localValueAtFlush = this.valueInternal;
967
- const document = await this.options.database.documents.update(this.options.document.id, {
1329
+ const document2 = await this.options.database.documents.update(this.options.document.id, {
968
1330
  text: [{
969
1331
  path: this.options.path,
970
1332
  baseHeads: this.heads,
971
1333
  edits: this.pendingEdits.map((edit) => ({ ...edit }))
972
1334
  }]
973
1335
  });
974
- const nextValue = readTextAtPath(document.data, this.options.path);
1336
+ const nextValue = readTextAtPath(document2.data, this.options.path);
975
1337
  const reconciled = nextValue !== localValueAtFlush;
976
1338
  this.valueInternal = nextValue;
977
- this.heads = document.heads ? [...document.heads] : [];
1339
+ this.heads = document2.heads ? [...document2.heads] : [];
978
1340
  this.pendingEdits = [];
979
1341
  return {
980
- document,
1342
+ document: document2,
981
1343
  value: this.valueInternal,
982
1344
  reconciled
983
1345
  };
@@ -1031,10 +1393,29 @@ async function queryDocuments(db, filter, sortBy, options) {
1031
1393
  const expression = typeof filter === "string" ? parseMindooDBFormulaBooleanExpression(filter) : filter;
1032
1394
  return db.query({ filter: expression, sortBy }, options);
1033
1395
  }
1396
+
1397
+ // src/wordChunks.ts
1398
+ function inheritDocumentEncryption(parent, input) {
1399
+ if (input.decryptionKeyId || input.recipients) return input;
1400
+ if (!parent.decryptionKeyId) return input;
1401
+ return { ...input, decryptionKeyId: parent.decryptionKeyId };
1402
+ }
1403
+ async function watchWordChunks(database, parentId, listener) {
1404
+ const subscription = await database.documents.liveQuery(
1405
+ {
1406
+ filter: `v.and(v.eq(v.field("type"), "wordChunk"), v.eq(v.field("parentId"), ${JSON.stringify(parentId)}))`
1407
+ },
1408
+ (result) => {
1409
+ listener(result.rows.map((row) => row.docId));
1410
+ }
1411
+ );
1412
+ return () => subscription.dispose();
1413
+ }
1034
1414
  export {
1035
1415
  DEFAULT_DRAG_LONG_PRESS_MS,
1036
1416
  DEFAULT_DRAG_POINTER_THRESHOLD_PX,
1037
1417
  DEFAULT_HAVEN_HOST_SHORTCUTS,
1418
+ DEFAULT_HAVEN_URL,
1038
1419
  MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
1039
1420
  MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
1040
1421
  MINDOODB_APP_BUNDLE_MANIFEST_FORMAT,
@@ -1045,19 +1426,26 @@ export {
1045
1426
  MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS,
1046
1427
  MINDOODB_APP_DEFINITION_VERSION,
1047
1428
  MINDOODB_APP_HOSTING_QUERY_PARAM,
1429
+ MINDOODB_APP_LAUNCH_ID_QUERY_PARAM,
1430
+ MINDOODB_APP_LISTING_DESCRIPTION_MAX,
1431
+ MINDOODB_APP_LISTING_MAX_SCREENSHOTS,
1432
+ MINDOODB_APP_LISTING_SUMMARY_MAX,
1048
1433
  MINDOODB_APP_MAX_DRAG_PAYLOAD_BYTES,
1049
1434
  MINDOODB_APP_MAX_DRAG_PREVIEW_BYTES,
1050
1435
  MINDOODB_APP_MAX_DRAG_PREVIEW_EDGE,
1051
1436
  MINDOODB_APP_MAX_DRAG_TYPES,
1052
1437
  MINDOODB_APP_SEAL_SIGNING_DOMAIN,
1438
+ MINDOODB_APP_VALUE_TAG,
1053
1439
  MINDOODB_APP_WELL_KNOWN_DRAG_TYPES,
1054
1440
  MINIMAL_DRAG_PREVIEW_PNG,
1055
1441
  MindooDBAppDragInputError,
1442
+ MindooDBAppValue,
1056
1443
  MindooDBRichTextHandle,
1057
1444
  MindooDBTextBuffer,
1058
1445
  abbreviateCanonicalName,
1059
1446
  applyHostedDocumentOverscrollContain,
1060
1447
  bindDragSource,
1448
+ buildHavenAppInstallUrl,
1061
1449
  buildMindooDBAppBundleContentHashInput,
1062
1450
  canPreviewAttachment,
1063
1451
  createMindooDBAppBridge,
@@ -1065,10 +1453,12 @@ export {
1065
1453
  createMindooDBTextBuffer,
1066
1454
  createViewLanguage,
1067
1455
  expandAbbreviatedName,
1456
+ inheritDocumentEncryption,
1068
1457
  installHavenStorageShim,
1069
1458
  installHostShortcutCapture,
1070
1459
  isHostShortcutAction,
1071
1460
  isHostedBundleRuntime,
1461
+ isLaunchedByHaven,
1072
1462
  isMindooDBAppBundleHash,
1073
1463
  isMindooDBAppDragType,
1074
1464
  matchHostShortcut,
@@ -1078,10 +1468,16 @@ export {
1078
1468
  normalizeMindooDBAppBundlePath,
1079
1469
  queryDocuments,
1080
1470
  readMindooDBAppHostingMode,
1471
+ readMindooDBAppLaunchId,
1081
1472
  releaseMindooDBAppBridgeSessions,
1473
+ renderHavenAppLandingPage,
1474
+ resolveCurrentHavenAppUrl,
1082
1475
  resolveMindooDBAppDefinitionUrl,
1476
+ resolveMindooDBAppListingAssetUrl,
1477
+ resolveMindooDBAppLocalizedText,
1083
1478
  sanitizeHostShortcutBindings,
1084
1479
  snapshotElementToPng,
1085
1480
  validateMindooDBAppBundleManifest,
1086
- validateMindooDBAppDefinition
1481
+ validateMindooDBAppDefinition,
1482
+ watchWordChunks
1087
1483
  };