mindoodb-app-sdk 0.0.51 → 0.0.54
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 +63 -55
- package/TESTING.md +57 -0
- package/dist/{chunk-R76XXRJ6.js → chunk-6NTVTL6G.js} +1 -1
- package/dist/index.d.ts +132 -3
- package/dist/index.js +655 -17
- package/dist/testing/index.d.ts +97 -2
- package/dist/testing/index.js +412 -141
- package/dist/{types-BLyV6Ejy.d.ts → types-CX-o1Xyq.d.ts} +53 -1
- package/dist/vite/index.d.ts +18 -5
- package/dist/vite/index.js +156 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
releaseMindooDBAppBridgeSessions,
|
|
24
24
|
sanitizeHostShortcutBindings,
|
|
25
25
|
snapshotElementToPng
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-6NTVTL6G.js";
|
|
27
27
|
import {
|
|
28
28
|
MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
|
|
29
29
|
MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
|
|
@@ -61,6 +61,9 @@ var MINDOODB_APP_DEFINITION_PERMISSIONS = [
|
|
|
61
61
|
"proposeapps"
|
|
62
62
|
];
|
|
63
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;
|
|
64
67
|
var LOGICAL_DATABASE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
65
68
|
function isPlainObject(value) {
|
|
66
69
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -200,6 +203,163 @@ function readDatabases(value, errors) {
|
|
|
200
203
|
});
|
|
201
204
|
return result.length ? result : void 0;
|
|
202
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 descriptionMarkdown = readLocalizedText(
|
|
290
|
+
value.descriptionMarkdown,
|
|
291
|
+
"App definition listing.descriptionMarkdown",
|
|
292
|
+
MINDOODB_APP_LISTING_DESCRIPTION_MAX,
|
|
293
|
+
errors
|
|
294
|
+
);
|
|
295
|
+
if (descriptionMarkdown) listing.descriptionMarkdown = descriptionMarkdown;
|
|
296
|
+
const icon = readListingAssetPath(value.icon, "App definition listing.icon", errors);
|
|
297
|
+
if (icon) listing.icon = icon;
|
|
298
|
+
if (value.screenshots !== void 0 && value.screenshots !== null) {
|
|
299
|
+
if (!Array.isArray(value.screenshots)) {
|
|
300
|
+
errors.push("App definition listing.screenshots must be an array.");
|
|
301
|
+
} else if (value.screenshots.length > MINDOODB_APP_LISTING_MAX_SCREENSHOTS) {
|
|
302
|
+
errors.push(
|
|
303
|
+
`App definition listing.screenshots must not list more than ${MINDOODB_APP_LISTING_MAX_SCREENSHOTS} images.`
|
|
304
|
+
);
|
|
305
|
+
} else {
|
|
306
|
+
const screenshots = [];
|
|
307
|
+
value.screenshots.forEach((entry, index) => {
|
|
308
|
+
const label = `App definition listing.screenshots[${index}]`;
|
|
309
|
+
const raw = typeof entry === "string" ? { file: entry } : entry;
|
|
310
|
+
if (!isPlainObject(raw)) {
|
|
311
|
+
errors.push(`${label} must be a path or an object with a file.`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const file = readListingAssetPath(raw.file, `${label}.file`, errors);
|
|
315
|
+
if (!file) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const caption = readLocalizedText(raw.caption, `${label}.caption`, MINDOODB_APP_LISTING_SUMMARY_MAX, errors);
|
|
319
|
+
screenshots.push(caption ? { file, caption } : { file });
|
|
320
|
+
});
|
|
321
|
+
if (screenshots.length) listing.screenshots = screenshots;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (value.publisher !== void 0 && value.publisher !== null) {
|
|
325
|
+
const publisher = isPlainObject(value.publisher) ? value.publisher : {};
|
|
326
|
+
const name = typeof publisher.name === "string" ? publisher.name.trim() : "";
|
|
327
|
+
if (!name) {
|
|
328
|
+
errors.push("App definition listing.publisher must be an object with a non-empty name.");
|
|
329
|
+
} else {
|
|
330
|
+
const url = readListingAssetPath(publisher.url, "App definition listing.publisher.url", errors);
|
|
331
|
+
if (url && !/^https:\/\//i.test(url)) {
|
|
332
|
+
errors.push("App definition listing.publisher.url must be an absolute https: URL.");
|
|
333
|
+
} else {
|
|
334
|
+
listing.publisher = url ? { name, url } : { name };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return Object.keys(listing).length ? listing : void 0;
|
|
339
|
+
}
|
|
340
|
+
function resolveMindooDBAppLocalizedText(text, locale) {
|
|
341
|
+
if (text === void 0) {
|
|
342
|
+
return "";
|
|
343
|
+
}
|
|
344
|
+
if (typeof text === "string") {
|
|
345
|
+
return text;
|
|
346
|
+
}
|
|
347
|
+
const normalized = locale.trim();
|
|
348
|
+
const language = normalized.split(/[-_]/)[0]?.toLowerCase() ?? "";
|
|
349
|
+
return text[normalized] ?? text[normalized.toLowerCase()] ?? (language ? text[language] : void 0) ?? text.en;
|
|
350
|
+
}
|
|
351
|
+
function resolveMindooDBAppListingAssetUrl(path, originUrl) {
|
|
352
|
+
try {
|
|
353
|
+
const base = new URL(originUrl.endsWith("/") ? originUrl : `${originUrl}/`);
|
|
354
|
+
const resolved = new URL(path, base);
|
|
355
|
+
if (resolved.protocol !== "https:" && resolved.protocol !== base.protocol) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
return resolved.toString();
|
|
359
|
+
} catch {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
203
363
|
function validateMindooDBAppDefinition(raw) {
|
|
204
364
|
const errors = [];
|
|
205
365
|
if (!isPlainObject(raw)) {
|
|
@@ -251,6 +411,7 @@ function validateMindooDBAppDefinition(raw) {
|
|
|
251
411
|
const allowGeolocation = readOptionalBoolean(raw, "allowGeolocation", errors);
|
|
252
412
|
const allowWebRtc = readOptionalBoolean(raw, "allowWebRtc", errors);
|
|
253
413
|
const allowWorkers = readOptionalBoolean(raw, "allowWorkers", errors);
|
|
414
|
+
const listing = readListing(raw.listing, errors);
|
|
254
415
|
if (defaultLaunchDatabaseId && databases && !databases.some((database) => database.logicalDatabaseId === defaultLaunchDatabaseId)) {
|
|
255
416
|
errors.push(
|
|
256
417
|
`App definition defaultLaunchDatabaseId "${defaultLaunchDatabaseId}" is not one of the declared databases.`
|
|
@@ -279,7 +440,8 @@ function validateMindooDBAppDefinition(raw) {
|
|
|
279
440
|
permissions,
|
|
280
441
|
launchParameters,
|
|
281
442
|
defaultLaunchDatabaseId,
|
|
282
|
-
databases
|
|
443
|
+
databases,
|
|
444
|
+
listing
|
|
283
445
|
},
|
|
284
446
|
errors: []
|
|
285
447
|
};
|
|
@@ -389,6 +551,22 @@ function expandAbbreviatedName(value) {
|
|
|
389
551
|
|
|
390
552
|
// src/client/hosting.ts
|
|
391
553
|
var MINDOODB_APP_HOSTING_QUERY_PARAM = "mindoodbAppHosting";
|
|
554
|
+
var MINDOODB_APP_LAUNCH_ID_QUERY_PARAM = "mindoodbAppLaunchId";
|
|
555
|
+
function readMindooDBAppLaunchId(search) {
|
|
556
|
+
const raw = search ?? (typeof window === "undefined" ? "" : window.location.search);
|
|
557
|
+
if (!raw) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
const launchId = new URLSearchParams(raw).get(MINDOODB_APP_LAUNCH_ID_QUERY_PARAM)?.trim();
|
|
562
|
+
return launchId || null;
|
|
563
|
+
} catch {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function isLaunchedByHaven(search) {
|
|
568
|
+
return readMindooDBAppLaunchId(search) !== null;
|
|
569
|
+
}
|
|
392
570
|
function readMindooDBAppHostingMode(search) {
|
|
393
571
|
const raw = search ?? (typeof window === "undefined" ? "" : window.location.search);
|
|
394
572
|
if (!raw) {
|
|
@@ -404,6 +582,451 @@ function isHostedBundleRuntime(search) {
|
|
|
404
582
|
return readMindooDBAppHostingMode(search) === "hosted";
|
|
405
583
|
}
|
|
406
584
|
|
|
585
|
+
// src/listingMarkdown.ts
|
|
586
|
+
var MAX_INLINE_DEPTH = 4;
|
|
587
|
+
var HEADING = /^#{1,6}\s+(.*?)\s*#*\s*$/;
|
|
588
|
+
var BULLET = /^[-*+]\s+(.*)$/;
|
|
589
|
+
var ORDERED = /^\d{1,9}[.)]\s+(.*)$/;
|
|
590
|
+
var ESCAPABLE = new Set("\\`*_[]()#+-.!<>".split(""));
|
|
591
|
+
var BARE_URL_TRAILING_PUNCTUATION = /[.,;:!?)\]'"]+$/;
|
|
592
|
+
function sanitizeListingHref(raw) {
|
|
593
|
+
const cleaned = raw.replace(/[\t\n\r]/g, "").trim();
|
|
594
|
+
if (!cleaned) {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
try {
|
|
598
|
+
const url = new URL(cleaned);
|
|
599
|
+
if (url.protocol === "https:") {
|
|
600
|
+
return url.toString();
|
|
601
|
+
}
|
|
602
|
+
if (url.protocol === "mailto:" && url.pathname) {
|
|
603
|
+
return url.toString();
|
|
604
|
+
}
|
|
605
|
+
} catch {
|
|
606
|
+
}
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
function isWordCharacter(char) {
|
|
610
|
+
return char !== void 0 && /[\p{L}\p{N}]/u.test(char);
|
|
611
|
+
}
|
|
612
|
+
function pushText(nodes, text) {
|
|
613
|
+
if (!text) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const last = nodes[nodes.length - 1];
|
|
617
|
+
if (last?.type === "text") {
|
|
618
|
+
last.text += text;
|
|
619
|
+
} else {
|
|
620
|
+
nodes.push({ type: "text", text });
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function findClosingDelimiter(text, from, delimiter) {
|
|
624
|
+
let index = text.indexOf(delimiter, from);
|
|
625
|
+
while (index !== -1) {
|
|
626
|
+
const before = text[index - 1];
|
|
627
|
+
const after = text[index + delimiter.length];
|
|
628
|
+
const closesAfterContent = index > from && before !== " " && before !== "\\";
|
|
629
|
+
const underscoreBoundary = delimiter[0] !== "_" || !isWordCharacter(after);
|
|
630
|
+
if (closesAfterContent && underscoreBoundary) {
|
|
631
|
+
return index;
|
|
632
|
+
}
|
|
633
|
+
index = text.indexOf(delimiter, index + 1);
|
|
634
|
+
}
|
|
635
|
+
return -1;
|
|
636
|
+
}
|
|
637
|
+
function findLinkTargetEnd(text, from) {
|
|
638
|
+
let open = 0;
|
|
639
|
+
for (let index = from; index < text.length; index += 1) {
|
|
640
|
+
const char = text[index];
|
|
641
|
+
if (char === "(") {
|
|
642
|
+
open += 1;
|
|
643
|
+
} else if (char === ")") {
|
|
644
|
+
if (open === 0) return index;
|
|
645
|
+
open -= 1;
|
|
646
|
+
} else if (char === " ") {
|
|
647
|
+
return -1;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return -1;
|
|
651
|
+
}
|
|
652
|
+
function parseInline(text, depth = 0) {
|
|
653
|
+
const nodes = [];
|
|
654
|
+
let index = 0;
|
|
655
|
+
while (index < text.length) {
|
|
656
|
+
const char = text[index];
|
|
657
|
+
if (char === "\\" && ESCAPABLE.has(text[index + 1] ?? "")) {
|
|
658
|
+
pushText(nodes, text[index + 1]);
|
|
659
|
+
index += 2;
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (char === "`") {
|
|
663
|
+
const end = text.indexOf("`", index + 1);
|
|
664
|
+
if (end > index + 1) {
|
|
665
|
+
nodes.push({ type: "code", text: text.slice(index + 1, end) });
|
|
666
|
+
index = end + 1;
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (depth < MAX_INLINE_DEPTH && (char === "*" || char === "_")) {
|
|
671
|
+
const strong = text[index + 1] === char;
|
|
672
|
+
const delimiter = strong ? char + char : char;
|
|
673
|
+
const next = text[index + delimiter.length];
|
|
674
|
+
const opens = next !== void 0 && next !== " " && (char !== "_" || !isWordCharacter(text[index - 1]));
|
|
675
|
+
if (opens) {
|
|
676
|
+
const end = findClosingDelimiter(text, index + delimiter.length, delimiter);
|
|
677
|
+
if (end !== -1) {
|
|
678
|
+
const children = parseInline(text.slice(index + delimiter.length, end), depth + 1);
|
|
679
|
+
nodes.push(strong ? { type: "strong", children } : { type: "em", children });
|
|
680
|
+
index = end + delimiter.length;
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (char === "[" && depth < MAX_INLINE_DEPTH) {
|
|
686
|
+
const labelEnd = text.indexOf("](", index + 1);
|
|
687
|
+
const urlEnd = labelEnd === -1 ? -1 : findLinkTargetEnd(text, labelEnd + 2);
|
|
688
|
+
if (labelEnd > index + 1 && urlEnd !== -1) {
|
|
689
|
+
const label = parseInline(text.slice(index + 1, labelEnd), depth + 1);
|
|
690
|
+
const href = sanitizeListingHref(text.slice(labelEnd + 2, urlEnd));
|
|
691
|
+
if (href) {
|
|
692
|
+
nodes.push({ type: "link", href, children: label });
|
|
693
|
+
} else {
|
|
694
|
+
nodes.push(...label);
|
|
695
|
+
}
|
|
696
|
+
index = urlEnd + 1;
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (char === "h" && text.startsWith("https://", index) && !isWordCharacter(text[index - 1])) {
|
|
701
|
+
let end = index;
|
|
702
|
+
while (end < text.length && !/\s/.test(text[end])) end += 1;
|
|
703
|
+
const candidate = text.slice(index, end).replace(BARE_URL_TRAILING_PUNCTUATION, "");
|
|
704
|
+
const href = sanitizeListingHref(candidate);
|
|
705
|
+
if (href) {
|
|
706
|
+
nodes.push({ type: "link", href, children: [{ type: "text", text: candidate }] });
|
|
707
|
+
index += candidate.length;
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
pushText(nodes, char);
|
|
712
|
+
index += 1;
|
|
713
|
+
}
|
|
714
|
+
return nodes;
|
|
715
|
+
}
|
|
716
|
+
function parseListingMarkdown(markdown) {
|
|
717
|
+
const blocks = [];
|
|
718
|
+
let paragraph = [];
|
|
719
|
+
let list = null;
|
|
720
|
+
const flushParagraph = () => {
|
|
721
|
+
if (paragraph.length) {
|
|
722
|
+
blocks.push({ type: "paragraph", children: parseInline(paragraph.join(" ")) });
|
|
723
|
+
paragraph = [];
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
const flushList = () => {
|
|
727
|
+
if (list) {
|
|
728
|
+
blocks.push({ type: "list", ordered: list.ordered, items: list.items.map((item) => parseInline(item)) });
|
|
729
|
+
list = null;
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
for (const rawLine of markdown.split(/\r?\n/)) {
|
|
733
|
+
const line = rawLine.trim();
|
|
734
|
+
if (!line) {
|
|
735
|
+
flushParagraph();
|
|
736
|
+
flushList();
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
const heading = HEADING.exec(line);
|
|
740
|
+
const bullet = BULLET.exec(line);
|
|
741
|
+
const ordered = bullet ? null : ORDERED.exec(line);
|
|
742
|
+
if (heading) {
|
|
743
|
+
flushParagraph();
|
|
744
|
+
flushList();
|
|
745
|
+
blocks.push({ type: "heading", children: parseInline(heading[1]) });
|
|
746
|
+
} else if (bullet || ordered) {
|
|
747
|
+
flushParagraph();
|
|
748
|
+
const isOrdered = Boolean(ordered);
|
|
749
|
+
if (list && list.ordered !== isOrdered) {
|
|
750
|
+
flushList();
|
|
751
|
+
}
|
|
752
|
+
list ??= { ordered: isOrdered, items: [] };
|
|
753
|
+
list.items.push((bullet ?? ordered)[1]);
|
|
754
|
+
} else if (list) {
|
|
755
|
+
list.items[list.items.length - 1] += ` ${line}`;
|
|
756
|
+
} else {
|
|
757
|
+
paragraph.push(line);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
flushParagraph();
|
|
761
|
+
flushList();
|
|
762
|
+
return blocks;
|
|
763
|
+
}
|
|
764
|
+
function inlineToPlainText(nodes) {
|
|
765
|
+
return nodes.map((node) => {
|
|
766
|
+
if (node.type === "text" || node.type === "code") {
|
|
767
|
+
return node.text;
|
|
768
|
+
}
|
|
769
|
+
const label = inlineToPlainText(node.children);
|
|
770
|
+
if (node.type !== "link") {
|
|
771
|
+
return label;
|
|
772
|
+
}
|
|
773
|
+
const target = node.href.replace(/^mailto:/, "").replace(/^https:\/\//, "").replace(/\/$/, "");
|
|
774
|
+
return label.includes(target) ? label : `${label} (${node.href.replace(/^mailto:/, "")})`;
|
|
775
|
+
}).join("");
|
|
776
|
+
}
|
|
777
|
+
function listingMarkdownToPlainText(markdown) {
|
|
778
|
+
const paragraphs = [];
|
|
779
|
+
for (const block of parseListingMarkdown(markdown)) {
|
|
780
|
+
if (block.type === "list") {
|
|
781
|
+
block.items.forEach((item, index) => {
|
|
782
|
+
paragraphs.push(`${block.ordered ? `${index + 1}.` : "\u2022"} ${inlineToPlainText(item)}`);
|
|
783
|
+
});
|
|
784
|
+
} else {
|
|
785
|
+
paragraphs.push(inlineToPlainText(block.children));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return paragraphs.filter((paragraph) => paragraph.trim()).join("\n\n");
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/landing.ts
|
|
792
|
+
var DEFAULT_HAVEN_URL = "https://haven.mindoodb.com";
|
|
793
|
+
var APP_ICON_FALLBACKS = ["appicon.png", "appicon.webp", "appicon.svg"];
|
|
794
|
+
var STRINGS = {
|
|
795
|
+
en: {
|
|
796
|
+
eyebrow: "App for MindooDB Haven",
|
|
797
|
+
cta: "Open in MindooDB Haven",
|
|
798
|
+
ctaHint: "Haven sets up your encrypted workspace and installs {label}. Already using Haven? Then the app is simply added.",
|
|
799
|
+
from: "from {origin}",
|
|
800
|
+
by: "by {publisher}",
|
|
801
|
+
screenshots: "Screenshots",
|
|
802
|
+
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.",
|
|
803
|
+
learnMore: "What is MindooDB?",
|
|
804
|
+
fallbackSummary: "This app runs inside MindooDB Haven."
|
|
805
|
+
},
|
|
806
|
+
de: {
|
|
807
|
+
eyebrow: "App f\xFCr MindooDB Haven",
|
|
808
|
+
cta: "In MindooDB Haven \xF6ffnen",
|
|
809
|
+
ctaHint: "Haven richtet deine verschl\xFCsselte Umgebung ein und installiert {label}. Du nutzt Haven schon? Dann wird die App einfach hinzugef\xFCgt.",
|
|
810
|
+
from: "von {origin}",
|
|
811
|
+
by: "von {publisher}",
|
|
812
|
+
screenshots: "Screenshots",
|
|
813
|
+
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.",
|
|
814
|
+
learnMore: "Was ist MindooDB?",
|
|
815
|
+
fallbackSummary: "Diese App l\xE4uft in MindooDB Haven."
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
function stringsFor(locale) {
|
|
819
|
+
return locale.toLowerCase().startsWith("de") ? STRINGS.de : STRINGS.en;
|
|
820
|
+
}
|
|
821
|
+
function format(template, values) {
|
|
822
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
|
|
823
|
+
}
|
|
824
|
+
function resolveCurrentHavenAppUrl(href = window.location.href) {
|
|
825
|
+
const url = new URL(href);
|
|
826
|
+
url.search = "";
|
|
827
|
+
url.hash = "";
|
|
828
|
+
url.pathname = url.pathname.replace(/\/index\.html?$/i, "/");
|
|
829
|
+
return url.toString();
|
|
830
|
+
}
|
|
831
|
+
function buildHavenAppInstallUrl(appUrl, havenUrl = DEFAULT_HAVEN_URL) {
|
|
832
|
+
const url = new URL(havenUrl);
|
|
833
|
+
url.searchParams.set("app", appUrl);
|
|
834
|
+
return url.toString();
|
|
835
|
+
}
|
|
836
|
+
async function loadDefinition(appUrl) {
|
|
837
|
+
try {
|
|
838
|
+
const definitionUrl = new URL(MINDOODB_APP_DEFINITION_FILE_NAME, appUrl.endsWith("/") ? appUrl : `${appUrl}/`);
|
|
839
|
+
const response = await fetch(definitionUrl, { headers: { Accept: "application/json" } });
|
|
840
|
+
if (!response.ok) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
return validateMindooDBAppDefinition(await response.json()).definition;
|
|
844
|
+
} catch {
|
|
845
|
+
return null;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
function el(tag, className, text) {
|
|
849
|
+
const node = document.createElement(tag);
|
|
850
|
+
if (className) node.className = className;
|
|
851
|
+
if (text !== void 0) node.textContent = text;
|
|
852
|
+
return node;
|
|
853
|
+
}
|
|
854
|
+
function externalLink(href) {
|
|
855
|
+
const link = el("a");
|
|
856
|
+
link.href = href;
|
|
857
|
+
link.rel = "noopener noreferrer";
|
|
858
|
+
link.target = "_blank";
|
|
859
|
+
return link;
|
|
860
|
+
}
|
|
861
|
+
function appendInline(parent, nodes) {
|
|
862
|
+
for (const node of nodes) {
|
|
863
|
+
if (node.type === "text") {
|
|
864
|
+
parent.append(node.text);
|
|
865
|
+
} else if (node.type === "code") {
|
|
866
|
+
parent.append(el("code", void 0, node.text));
|
|
867
|
+
} else {
|
|
868
|
+
const child = node.type === "link" ? externalLink(node.href) : el(node.type);
|
|
869
|
+
appendInline(child, node.children);
|
|
870
|
+
parent.append(child);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function renderMarkdownDescription(block, markdown) {
|
|
875
|
+
for (const entry of parseListingMarkdown(markdown)) {
|
|
876
|
+
if (entry.type === "list") {
|
|
877
|
+
const list = el(entry.ordered ? "ol" : "ul");
|
|
878
|
+
for (const item of entry.items) {
|
|
879
|
+
const li = el("li");
|
|
880
|
+
appendInline(li, item);
|
|
881
|
+
list.append(li);
|
|
882
|
+
}
|
|
883
|
+
block.append(list);
|
|
884
|
+
} else {
|
|
885
|
+
const node = el(entry.type === "heading" ? "h2" : "p");
|
|
886
|
+
appendInline(node, entry.children);
|
|
887
|
+
block.append(node);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
var STYLE_ID = "mdb-landing-style";
|
|
892
|
+
var STYLE = `
|
|
893
|
+
.mdb-landing{--bg:#f4f6fb;--card:#fff;--text:#172033;--muted:#5b6478;--line:#e2e6ef;--accent:#1f3a8a;--accent-text:#fff;
|
|
894
|
+
min-height:100vh;box-sizing:border-box;margin:0;padding:48px 16px;background:var(--bg);color:var(--text);
|
|
895
|
+
font:16px/1.55 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
896
|
+
@media (prefers-color-scheme:dark){.mdb-landing{--bg:#0e1320;--card:#161d2e;--text:#e8ecf5;--muted:#9aa3b8;--line:#263049;--accent:#8fb0ff;--accent-text:#0e1320}}
|
|
897
|
+
.mdb-landing *{box-sizing:border-box}
|
|
898
|
+
.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)}
|
|
899
|
+
.mdb-landing__head{display:flex;gap:24px;align-items:center}
|
|
900
|
+
.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)}
|
|
901
|
+
.mdb-landing__eyebrow{margin:0 0 4px;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
|
|
902
|
+
.mdb-landing__title{margin:0;font-size:clamp(26px,4vw,36px);line-height:1.15}
|
|
903
|
+
.mdb-landing__meta{margin:6px 0 0;color:var(--muted);font-size:14px}
|
|
904
|
+
.mdb-landing__summary{margin:28px 0 0;font-size:19px}
|
|
905
|
+
.mdb-landing__cta{display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin:28px 0 0}
|
|
906
|
+
.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}
|
|
907
|
+
.mdb-landing__button:focus-visible{outline:3px solid var(--accent);outline-offset:3px}
|
|
908
|
+
.mdb-landing__hint{margin:0;max-width:420px;color:var(--muted);font-size:14px}
|
|
909
|
+
.mdb-landing__description{margin:32px 0 0}
|
|
910
|
+
.mdb-landing__description p,.mdb-landing__description ul,.mdb-landing__description ol{margin:0 0 12px}
|
|
911
|
+
.mdb-landing__description ul,.mdb-landing__description ol{padding-left:22px}
|
|
912
|
+
.mdb-landing__description li+li{margin-top:4px}
|
|
913
|
+
.mdb-landing__description h2{margin:20px 0 8px;font-size:17px}
|
|
914
|
+
.mdb-landing__description code{font:.92em ui-monospace,SFMono-Regular,Menlo,monospace;padding:1px 5px;border-radius:5px;background:var(--line)}
|
|
915
|
+
.mdb-landing__description a,.mdb-landing__meta a{color:var(--accent)}
|
|
916
|
+
.mdb-landing__shots-title{margin:32px 0 12px;font-size:15px;color:var(--muted);font-weight:600}
|
|
917
|
+
.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}
|
|
918
|
+
.mdb-landing__shot{margin:0;scroll-snap-align:start}
|
|
919
|
+
.mdb-landing__shot img{width:100%;border-radius:14px;border:1px solid var(--line);display:block}
|
|
920
|
+
.mdb-landing__shot figcaption{margin-top:8px;font-size:13px;color:var(--muted)}
|
|
921
|
+
.mdb-landing__footer{margin:36px 0 0;padding-top:20px;border-top:1px solid var(--line);color:var(--muted);font-size:13px}
|
|
922
|
+
.mdb-landing__footer a{color:inherit}
|
|
923
|
+
@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}}
|
|
924
|
+
`;
|
|
925
|
+
async function renderHavenAppLandingPage(options = {}) {
|
|
926
|
+
const appUrl = options.appUrl ?? resolveCurrentHavenAppUrl();
|
|
927
|
+
const locale = options.locale ?? (typeof navigator === "undefined" ? "en" : navigator.language || "en");
|
|
928
|
+
const strings = stringsFor(locale);
|
|
929
|
+
const definition = options.definition ?? await loadDefinition(appUrl);
|
|
930
|
+
const installUrl = buildHavenAppInstallUrl(appUrl, options.havenUrl);
|
|
931
|
+
const listing = definition?.listing;
|
|
932
|
+
const label = definition?.label ?? (document.title || new URL(appUrl).host);
|
|
933
|
+
const origin = new URL(appUrl).host;
|
|
934
|
+
if (!document.getElementById(STYLE_ID)) {
|
|
935
|
+
const style = el("style");
|
|
936
|
+
style.id = STYLE_ID;
|
|
937
|
+
style.textContent = STYLE;
|
|
938
|
+
document.head.append(style);
|
|
939
|
+
}
|
|
940
|
+
document.title = label;
|
|
941
|
+
const root = el("main", "mdb-landing");
|
|
942
|
+
root.dataset.testid = "haven-app-landing";
|
|
943
|
+
const card = el("article", "mdb-landing__card");
|
|
944
|
+
root.append(card);
|
|
945
|
+
const head = el("header", "mdb-landing__head");
|
|
946
|
+
const iconCandidates = (listing?.icon ? [listing.icon] : [...APP_ICON_FALLBACKS]).map((path) => resolveMindooDBAppListingAssetUrl(path, appUrl)).filter((url) => Boolean(url));
|
|
947
|
+
if (iconCandidates.length) {
|
|
948
|
+
const icon = el("img", "mdb-landing__icon");
|
|
949
|
+
let next = 0;
|
|
950
|
+
icon.alt = "";
|
|
951
|
+
icon.addEventListener("error", () => {
|
|
952
|
+
next += 1;
|
|
953
|
+
if (next < iconCandidates.length) {
|
|
954
|
+
icon.src = iconCandidates[next];
|
|
955
|
+
} else {
|
|
956
|
+
icon.remove();
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
icon.src = iconCandidates[0];
|
|
960
|
+
head.append(icon);
|
|
961
|
+
}
|
|
962
|
+
const titles = el("div");
|
|
963
|
+
titles.append(el("p", "mdb-landing__eyebrow", strings.eyebrow), el("h1", "mdb-landing__title", label));
|
|
964
|
+
const meta = el("p", "mdb-landing__meta");
|
|
965
|
+
if (listing?.publisher) {
|
|
966
|
+
const [before, after = ""] = strings.by.split("{publisher}");
|
|
967
|
+
const name = listing.publisher.url ? Object.assign(externalLink(listing.publisher.url), { textContent: listing.publisher.name }) : listing.publisher.name;
|
|
968
|
+
meta.append(before ?? "", name, after, " \xB7 ");
|
|
969
|
+
}
|
|
970
|
+
meta.append(format(strings.from, { origin }));
|
|
971
|
+
titles.append(meta);
|
|
972
|
+
head.append(titles);
|
|
973
|
+
card.append(head);
|
|
974
|
+
const summary = resolveMindooDBAppLocalizedText(listing?.summary, locale) || definition?.description || strings.fallbackSummary;
|
|
975
|
+
card.append(el("p", "mdb-landing__summary", summary));
|
|
976
|
+
const cta = el("div", "mdb-landing__cta");
|
|
977
|
+
const button = el("a", "mdb-landing__button", strings.cta);
|
|
978
|
+
button.href = installUrl;
|
|
979
|
+
button.rel = "noopener";
|
|
980
|
+
button.dataset.testid = "haven-app-landing-install";
|
|
981
|
+
cta.append(button, el("p", "mdb-landing__hint", format(strings.ctaHint, { label })));
|
|
982
|
+
card.append(cta);
|
|
983
|
+
const descriptionMarkdown = resolveMindooDBAppLocalizedText(listing?.descriptionMarkdown, locale);
|
|
984
|
+
const description = resolveMindooDBAppLocalizedText(listing?.description, locale);
|
|
985
|
+
if (descriptionMarkdown || description) {
|
|
986
|
+
const block = el("section", "mdb-landing__description");
|
|
987
|
+
if (descriptionMarkdown) {
|
|
988
|
+
renderMarkdownDescription(block, descriptionMarkdown);
|
|
989
|
+
} else {
|
|
990
|
+
for (const paragraph of description.split(/\n\s*\n/)) {
|
|
991
|
+
if (paragraph.trim()) block.append(el("p", void 0, paragraph.trim()));
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
card.append(block);
|
|
995
|
+
}
|
|
996
|
+
const screenshots = (listing?.screenshots ?? []).map((shot) => ({
|
|
997
|
+
url: resolveMindooDBAppListingAssetUrl(shot.file, appUrl),
|
|
998
|
+
caption: resolveMindooDBAppLocalizedText(shot.caption, locale)
|
|
999
|
+
})).filter((shot) => Boolean(shot.url));
|
|
1000
|
+
if (screenshots.length) {
|
|
1001
|
+
card.append(el("h2", "mdb-landing__shots-title", strings.screenshots));
|
|
1002
|
+
const strip = el("div", "mdb-landing__shots");
|
|
1003
|
+
for (const shot of screenshots) {
|
|
1004
|
+
const figure = el("figure", "mdb-landing__shot");
|
|
1005
|
+
const image = el("img");
|
|
1006
|
+
image.src = shot.url;
|
|
1007
|
+
image.alt = shot.caption;
|
|
1008
|
+
image.loading = "lazy";
|
|
1009
|
+
figure.append(image);
|
|
1010
|
+
if (shot.caption) figure.append(el("figcaption", void 0, shot.caption));
|
|
1011
|
+
strip.append(figure);
|
|
1012
|
+
}
|
|
1013
|
+
card.append(strip);
|
|
1014
|
+
}
|
|
1015
|
+
const footer = el("footer", "mdb-landing__footer");
|
|
1016
|
+
footer.append(format(strings.footer, { label }), " ");
|
|
1017
|
+
const learnMore = el("a", void 0, strings.learnMore);
|
|
1018
|
+
learnMore.href = "https://mindoodb.com";
|
|
1019
|
+
learnMore.rel = "noopener";
|
|
1020
|
+
footer.append(learnMore);
|
|
1021
|
+
card.append(footer);
|
|
1022
|
+
const container = options.container ?? document.body;
|
|
1023
|
+
container.replaceChildren(root);
|
|
1024
|
+
if (container === document.body) {
|
|
1025
|
+
document.body.style.margin = "0";
|
|
1026
|
+
}
|
|
1027
|
+
return { definition, installUrl };
|
|
1028
|
+
}
|
|
1029
|
+
|
|
407
1030
|
// src/client/havenStorageShim.ts
|
|
408
1031
|
var DEFAULT_QUOTA_BYTES = 256 * 1024;
|
|
409
1032
|
var DEFAULT_MAX_VALUE_BYTES = 64 * 1024;
|
|
@@ -731,11 +1354,11 @@ var MindooDBRichTextHandle = class {
|
|
|
731
1354
|
this.pollTimer = null;
|
|
732
1355
|
}
|
|
733
1356
|
}
|
|
734
|
-
reconcile(
|
|
1357
|
+
reconcile(document2, snapshot) {
|
|
735
1358
|
const previous = JSON.stringify(this.spansInternal);
|
|
736
1359
|
this.spansInternal = cloneSpans(snapshot.spans);
|
|
737
1360
|
this.baseMaterializedText = spansToPlainText(snapshot.spans);
|
|
738
|
-
this.heads = snapshot.heads ??
|
|
1361
|
+
this.heads = snapshot.heads ?? document2.heads ?? [];
|
|
739
1362
|
this.dirtyInternal = false;
|
|
740
1363
|
const changed = JSON.stringify(this.spansInternal) !== previous;
|
|
741
1364
|
if (changed) {
|
|
@@ -754,14 +1377,14 @@ var MindooDBRichTextHandle = class {
|
|
|
754
1377
|
if (this.dirtyInternal) {
|
|
755
1378
|
return false;
|
|
756
1379
|
}
|
|
757
|
-
const
|
|
1380
|
+
const document2 = await this.options.database.documents.get(
|
|
758
1381
|
this.options.document.id
|
|
759
1382
|
);
|
|
760
|
-
if (!
|
|
1383
|
+
if (!document2) {
|
|
761
1384
|
return false;
|
|
762
1385
|
}
|
|
763
1386
|
const snapshot = await this.refresh();
|
|
764
|
-
return this.reconcile(
|
|
1387
|
+
return this.reconcile(document2, snapshot);
|
|
765
1388
|
}
|
|
766
1389
|
async flush(updateSpansConfig) {
|
|
767
1390
|
if (!this.dirtyInternal) {
|
|
@@ -778,7 +1401,7 @@ var MindooDBRichTextHandle = class {
|
|
|
778
1401
|
const localSpansAtFlush = cloneSpans(this.spansInternal);
|
|
779
1402
|
const localTextAtFlush = spansToPlainText(localSpansAtFlush);
|
|
780
1403
|
const textChanged = localTextAtFlush !== this.baseMaterializedText;
|
|
781
|
-
const
|
|
1404
|
+
const document2 = textChanged ? await this.flushTextSteps(localTextAtFlush) : await this.options.database.documents.update(
|
|
782
1405
|
this.options.document.id,
|
|
783
1406
|
{
|
|
784
1407
|
richText: [{
|
|
@@ -796,13 +1419,13 @@ var MindooDBRichTextHandle = class {
|
|
|
796
1419
|
const reconciled = JSON.stringify(snapshot.spans) !== JSON.stringify(localSpansAtFlush);
|
|
797
1420
|
this.spansInternal = cloneSpans(snapshot.spans);
|
|
798
1421
|
this.baseMaterializedText = spansToPlainText(snapshot.spans);
|
|
799
|
-
this.heads = snapshot.heads ??
|
|
1422
|
+
this.heads = snapshot.heads ?? document2.heads ?? [];
|
|
800
1423
|
this.dirtyInternal = false;
|
|
801
1424
|
if (reconciled) {
|
|
802
1425
|
this.emitChange();
|
|
803
1426
|
}
|
|
804
1427
|
return {
|
|
805
|
-
document,
|
|
1428
|
+
document: document2,
|
|
806
1429
|
snapshot,
|
|
807
1430
|
reconciled
|
|
808
1431
|
};
|
|
@@ -940,11 +1563,11 @@ var MindooDBTextBuffer = class {
|
|
|
940
1563
|
* Returns whether the text value changed. Use this after an external refresh
|
|
941
1564
|
* or after receiving a document update through another app-level mechanism.
|
|
942
1565
|
*/
|
|
943
|
-
reconcile(
|
|
944
|
-
const nextValue = readTextAtPath(
|
|
1566
|
+
reconcile(document2) {
|
|
1567
|
+
const nextValue = readTextAtPath(document2.data, this.options.path);
|
|
945
1568
|
const changed = nextValue !== this.valueInternal;
|
|
946
1569
|
this.valueInternal = nextValue;
|
|
947
|
-
this.heads =
|
|
1570
|
+
this.heads = document2.heads ? [...document2.heads] : [];
|
|
948
1571
|
this.pendingEdits = [];
|
|
949
1572
|
return changed;
|
|
950
1573
|
}
|
|
@@ -966,20 +1589,20 @@ var MindooDBTextBuffer = class {
|
|
|
966
1589
|
};
|
|
967
1590
|
}
|
|
968
1591
|
const localValueAtFlush = this.valueInternal;
|
|
969
|
-
const
|
|
1592
|
+
const document2 = await this.options.database.documents.update(this.options.document.id, {
|
|
970
1593
|
text: [{
|
|
971
1594
|
path: this.options.path,
|
|
972
1595
|
baseHeads: this.heads,
|
|
973
1596
|
edits: this.pendingEdits.map((edit) => ({ ...edit }))
|
|
974
1597
|
}]
|
|
975
1598
|
});
|
|
976
|
-
const nextValue = readTextAtPath(
|
|
1599
|
+
const nextValue = readTextAtPath(document2.data, this.options.path);
|
|
977
1600
|
const reconciled = nextValue !== localValueAtFlush;
|
|
978
1601
|
this.valueInternal = nextValue;
|
|
979
|
-
this.heads =
|
|
1602
|
+
this.heads = document2.heads ? [...document2.heads] : [];
|
|
980
1603
|
this.pendingEdits = [];
|
|
981
1604
|
return {
|
|
982
|
-
document,
|
|
1605
|
+
document: document2,
|
|
983
1606
|
value: this.valueInternal,
|
|
984
1607
|
reconciled
|
|
985
1608
|
};
|
|
@@ -1055,6 +1678,7 @@ export {
|
|
|
1055
1678
|
DEFAULT_DRAG_LONG_PRESS_MS,
|
|
1056
1679
|
DEFAULT_DRAG_POINTER_THRESHOLD_PX,
|
|
1057
1680
|
DEFAULT_HAVEN_HOST_SHORTCUTS,
|
|
1681
|
+
DEFAULT_HAVEN_URL,
|
|
1058
1682
|
MINDOODB_APP_BUNDLE_ARCHIVE_FILE_NAME,
|
|
1059
1683
|
MINDOODB_APP_BUNDLE_MANIFEST_FILE_NAME,
|
|
1060
1684
|
MINDOODB_APP_BUNDLE_MANIFEST_FORMAT,
|
|
@@ -1065,6 +1689,10 @@ export {
|
|
|
1065
1689
|
MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS,
|
|
1066
1690
|
MINDOODB_APP_DEFINITION_VERSION,
|
|
1067
1691
|
MINDOODB_APP_HOSTING_QUERY_PARAM,
|
|
1692
|
+
MINDOODB_APP_LAUNCH_ID_QUERY_PARAM,
|
|
1693
|
+
MINDOODB_APP_LISTING_DESCRIPTION_MAX,
|
|
1694
|
+
MINDOODB_APP_LISTING_MAX_SCREENSHOTS,
|
|
1695
|
+
MINDOODB_APP_LISTING_SUMMARY_MAX,
|
|
1068
1696
|
MINDOODB_APP_MAX_DRAG_PAYLOAD_BYTES,
|
|
1069
1697
|
MINDOODB_APP_MAX_DRAG_PREVIEW_BYTES,
|
|
1070
1698
|
MINDOODB_APP_MAX_DRAG_PREVIEW_EDGE,
|
|
@@ -1080,6 +1708,7 @@ export {
|
|
|
1080
1708
|
abbreviateCanonicalName,
|
|
1081
1709
|
applyHostedDocumentOverscrollContain,
|
|
1082
1710
|
bindDragSource,
|
|
1711
|
+
buildHavenAppInstallUrl,
|
|
1083
1712
|
buildMindooDBAppBundleContentHashInput,
|
|
1084
1713
|
canPreviewAttachment,
|
|
1085
1714
|
createMindooDBAppBridge,
|
|
@@ -1092,18 +1721,27 @@ export {
|
|
|
1092
1721
|
installHostShortcutCapture,
|
|
1093
1722
|
isHostShortcutAction,
|
|
1094
1723
|
isHostedBundleRuntime,
|
|
1724
|
+
isLaunchedByHaven,
|
|
1095
1725
|
isMindooDBAppBundleHash,
|
|
1096
1726
|
isMindooDBAppDragType,
|
|
1727
|
+
listingMarkdownToPlainText,
|
|
1097
1728
|
matchHostShortcut,
|
|
1098
1729
|
normalizeDragAccepts,
|
|
1099
1730
|
normalizeDragPointerInput,
|
|
1100
1731
|
normalizeDragStartInput,
|
|
1101
1732
|
normalizeMindooDBAppBundlePath,
|
|
1733
|
+
parseListingMarkdown,
|
|
1102
1734
|
queryDocuments,
|
|
1103
1735
|
readMindooDBAppHostingMode,
|
|
1736
|
+
readMindooDBAppLaunchId,
|
|
1104
1737
|
releaseMindooDBAppBridgeSessions,
|
|
1738
|
+
renderHavenAppLandingPage,
|
|
1739
|
+
resolveCurrentHavenAppUrl,
|
|
1105
1740
|
resolveMindooDBAppDefinitionUrl,
|
|
1741
|
+
resolveMindooDBAppListingAssetUrl,
|
|
1742
|
+
resolveMindooDBAppLocalizedText,
|
|
1106
1743
|
sanitizeHostShortcutBindings,
|
|
1744
|
+
sanitizeListingHref,
|
|
1107
1745
|
snapshotElementToPng,
|
|
1108
1746
|
validateMindooDBAppBundleManifest,
|
|
1109
1747
|
validateMindooDBAppDefinition,
|