mindoodb-app-sdk 0.0.59 → 0.0.61
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 +68 -0
- package/dist/{chunk-SKPP4T6S.js → chunk-U4NXI7EH.js} +932 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +23 -510
- package/dist/testing/index.d.ts +44 -2
- package/dist/testing/index.js +307 -6
- package/dist/{types-DkzIJKBw.d.ts → types-BKN_Zh_l.d.ts} +223 -2
- package/package.json +3 -3
|
@@ -66,6 +66,679 @@ function assertValidMindooDBCreateIds(input, methodName = "createDocument") {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// src/appDefinition.ts
|
|
70
|
+
var MINDOODB_APP_DEFINITION_FORMAT = "mindoodb.haven.app";
|
|
71
|
+
var MINDOODB_APP_DEFINITION_VERSION = 1;
|
|
72
|
+
var MINDOODB_APP_DEFINITION_FILE_NAME = "haven-app.json";
|
|
73
|
+
var MINDOODB_APP_DEFINITION_PERMISSIONS = [
|
|
74
|
+
"write",
|
|
75
|
+
"delete",
|
|
76
|
+
"history",
|
|
77
|
+
"attachments",
|
|
78
|
+
"views",
|
|
79
|
+
"sign",
|
|
80
|
+
"timestamps",
|
|
81
|
+
"directory",
|
|
82
|
+
"sealedchannel",
|
|
83
|
+
"proposeapps"
|
|
84
|
+
];
|
|
85
|
+
var MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS = ["proposeapps"];
|
|
86
|
+
var MINDOODB_APP_LISTING_SUMMARY_MAX = 300;
|
|
87
|
+
var MINDOODB_APP_LISTING_DESCRIPTION_MAX = 4e3;
|
|
88
|
+
var MINDOODB_APP_LISTING_MAX_SCREENSHOTS = 8;
|
|
89
|
+
var LOGICAL_DATABASE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
90
|
+
function isPlainObject(value) {
|
|
91
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
92
|
+
}
|
|
93
|
+
function readOptionalString(raw, key, errors) {
|
|
94
|
+
const value = raw[key];
|
|
95
|
+
if (value === void 0 || value === null) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value !== "string") {
|
|
99
|
+
errors.push(`App definition ${key} must be a string.`);
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const trimmed = value.trim();
|
|
103
|
+
return trimmed || void 0;
|
|
104
|
+
}
|
|
105
|
+
function readOptionalBoolean(raw, key, errors) {
|
|
106
|
+
const value = raw[key];
|
|
107
|
+
if (value === void 0 || value === null) {
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
if (typeof value !== "boolean") {
|
|
111
|
+
errors.push(`App definition ${key} must be a boolean.`);
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
return value || void 0;
|
|
115
|
+
}
|
|
116
|
+
function readStringRecord(raw, key, errors) {
|
|
117
|
+
const value = raw[key];
|
|
118
|
+
if (value === void 0 || value === null) {
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
if (!isPlainObject(value)) {
|
|
122
|
+
errors.push(`App definition ${key} must be an object of string values.`);
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
const result = {};
|
|
126
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
127
|
+
if (typeof entryValue !== "string") {
|
|
128
|
+
errors.push(`App definition ${key}.${entryKey} must be a string.`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
result[entryKey] = entryValue;
|
|
132
|
+
}
|
|
133
|
+
return Object.keys(result).length ? result : void 0;
|
|
134
|
+
}
|
|
135
|
+
function readPermissions(value, label, allowed, errors) {
|
|
136
|
+
if (value === void 0 || value === null) {
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
if (!Array.isArray(value)) {
|
|
140
|
+
errors.push(`${label} must be an array of permission names.`);
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
const result = [];
|
|
144
|
+
for (const entry of value) {
|
|
145
|
+
if (typeof entry !== "string" || !allowed.includes(entry)) {
|
|
146
|
+
errors.push(`${label} contains the unknown permission ${JSON.stringify(entry)}.`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (!result.includes(entry)) {
|
|
150
|
+
result.push(entry);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return result.length ? result : void 0;
|
|
154
|
+
}
|
|
155
|
+
function readNetworkAllowlist(value, errors) {
|
|
156
|
+
if (value === void 0 || value === null) {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(value)) {
|
|
160
|
+
errors.push("App definition networkAllowlist must be an array of origins.");
|
|
161
|
+
return void 0;
|
|
162
|
+
}
|
|
163
|
+
const result = [];
|
|
164
|
+
for (const entry of value) {
|
|
165
|
+
if (typeof entry !== "string" || !entry.trim()) {
|
|
166
|
+
errors.push(`App definition networkAllowlist contains the invalid entry ${JSON.stringify(entry)}.`);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const trimmed = entry.trim();
|
|
170
|
+
if (!result.includes(trimmed)) {
|
|
171
|
+
result.push(trimmed);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result.length ? result : void 0;
|
|
175
|
+
}
|
|
176
|
+
function readDatabases(value, errors) {
|
|
177
|
+
if (value === void 0 || value === null) {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
if (!Array.isArray(value)) {
|
|
181
|
+
errors.push("App definition databases must be an array.");
|
|
182
|
+
return void 0;
|
|
183
|
+
}
|
|
184
|
+
const result = [];
|
|
185
|
+
const seen = /* @__PURE__ */ new Set();
|
|
186
|
+
value.forEach((entry, index) => {
|
|
187
|
+
if (!isPlainObject(entry)) {
|
|
188
|
+
errors.push(`App definition database #${index} must be an object.`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const logicalDatabaseId = typeof entry.logicalDatabaseId === "string" ? entry.logicalDatabaseId.trim() : "";
|
|
192
|
+
if (!logicalDatabaseId) {
|
|
193
|
+
errors.push(`App definition database #${index} is missing a non-empty logicalDatabaseId.`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!LOGICAL_DATABASE_ID_PATTERN.test(logicalDatabaseId)) {
|
|
197
|
+
errors.push(
|
|
198
|
+
`App definition database "${logicalDatabaseId}" must match ${String(LOGICAL_DATABASE_ID_PATTERN)}.`
|
|
199
|
+
);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (seen.has(logicalDatabaseId)) {
|
|
203
|
+
errors.push(`App definition lists the database "${logicalDatabaseId}" more than once.`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
seen.add(logicalDatabaseId);
|
|
207
|
+
const databaseId = typeof entry.databaseId === "string" ? entry.databaseId.trim() : "";
|
|
208
|
+
if (entry.databaseId !== void 0 && entry.databaseId !== null && !databaseId) {
|
|
209
|
+
errors.push(`App definition database "${logicalDatabaseId}" has an empty databaseId.`);
|
|
210
|
+
}
|
|
211
|
+
result.push({
|
|
212
|
+
logicalDatabaseId,
|
|
213
|
+
label: typeof entry.label === "string" && entry.label.trim() ? entry.label.trim() : void 0,
|
|
214
|
+
databaseId: databaseId || void 0,
|
|
215
|
+
permissions: readPermissions(
|
|
216
|
+
entry.permissions,
|
|
217
|
+
`App definition database "${logicalDatabaseId}" permissions`,
|
|
218
|
+
MINDOODB_APP_DEFINITION_PERMISSIONS.filter(
|
|
219
|
+
(permission) => !MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS.includes(permission)
|
|
220
|
+
),
|
|
221
|
+
errors
|
|
222
|
+
),
|
|
223
|
+
create: entry.create === false ? false : void 0
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
return result.length ? result : void 0;
|
|
227
|
+
}
|
|
228
|
+
function readLocalizedText(value, label, maxLength, errors) {
|
|
229
|
+
if (value === void 0 || value === null) {
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
if (typeof value === "string") {
|
|
233
|
+
const trimmed = value.trim();
|
|
234
|
+
if (trimmed.length > maxLength) {
|
|
235
|
+
errors.push(`${label} must not be longer than ${maxLength} characters.`);
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
return trimmed || void 0;
|
|
239
|
+
}
|
|
240
|
+
if (!isPlainObject(value)) {
|
|
241
|
+
errors.push(`${label} must be a string or an object of per-locale strings.`);
|
|
242
|
+
return void 0;
|
|
243
|
+
}
|
|
244
|
+
const result = {};
|
|
245
|
+
for (const [locale, text] of Object.entries(value)) {
|
|
246
|
+
if (typeof text !== "string") {
|
|
247
|
+
errors.push(`${label}.${locale} must be a string.`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const trimmed = text.trim();
|
|
251
|
+
if (trimmed.length > maxLength) {
|
|
252
|
+
errors.push(`${label}.${locale} must not be longer than ${maxLength} characters.`);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (trimmed) {
|
|
256
|
+
result[locale] = trimmed;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!Object.keys(result).length) {
|
|
260
|
+
return void 0;
|
|
261
|
+
}
|
|
262
|
+
if (!result.en) {
|
|
263
|
+
errors.push(`${label} must include an "en" entry as the fallback for other locales.`);
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
var MINDOODB_APP_ACCEPTS_MAX = 16;
|
|
269
|
+
var ACCEPT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,47}$/;
|
|
270
|
+
var ACCEPT_TYPE_PATTERN = /^(\*|[a-z0-9][a-z0-9.+-]*)\/(\*|[a-z0-9][a-z0-9.+-]*)$/i;
|
|
271
|
+
function readAccepts(value, errors) {
|
|
272
|
+
if (value === void 0 || value === null) {
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
if (!Array.isArray(value)) {
|
|
276
|
+
errors.push("App definition accepts must be an array.");
|
|
277
|
+
return void 0;
|
|
278
|
+
}
|
|
279
|
+
if (value.length > MINDOODB_APP_ACCEPTS_MAX) {
|
|
280
|
+
errors.push(`App definition accepts must not have more than ${MINDOODB_APP_ACCEPTS_MAX} entries.`);
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
const result = [];
|
|
284
|
+
const ids = /* @__PURE__ */ new Set();
|
|
285
|
+
value.forEach((entry, index) => {
|
|
286
|
+
const label = `App definition accepts[${index}]`;
|
|
287
|
+
if (!isPlainObject(entry)) {
|
|
288
|
+
errors.push(`${label} must be an object.`);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const id = typeof entry.id === "string" ? entry.id.trim() : "";
|
|
292
|
+
if (!ACCEPT_ID_PATTERN.test(id)) {
|
|
293
|
+
errors.push(`${label}.id must be 1-48 lowercase letters, digits, "-" or "_", received ${JSON.stringify(entry.id)}.`);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (ids.has(id)) {
|
|
297
|
+
errors.push(`${label}.id "${id}" is used twice.`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
ids.add(id);
|
|
301
|
+
const text = readLocalizedText(entry.label, `${label}.label`, 80, errors);
|
|
302
|
+
if (!text) {
|
|
303
|
+
errors.push(`${label}.label is required.`);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (!Array.isArray(entry.types) || !entry.types.length) {
|
|
307
|
+
errors.push(`${label}.types must be a non-empty array of MIME patterns.`);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const types = [];
|
|
311
|
+
for (const type of entry.types) {
|
|
312
|
+
if (typeof type !== "string" || !ACCEPT_TYPE_PATTERN.test(type.trim())) {
|
|
313
|
+
errors.push(`${label}.types contains an invalid MIME pattern ${JSON.stringify(type)}.`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
types.push(type.trim().toLowerCase());
|
|
317
|
+
}
|
|
318
|
+
const folders = readOptionalBoolean(entry, "folders", errors);
|
|
319
|
+
const multiple = entry.multiple === false ? false : readOptionalBoolean(entry, "multiple", errors);
|
|
320
|
+
let maxBytes;
|
|
321
|
+
if (entry.maxBytes !== void 0) {
|
|
322
|
+
if (typeof entry.maxBytes !== "number" || !Number.isSafeInteger(entry.maxBytes) || entry.maxBytes <= 0) {
|
|
323
|
+
errors.push(`${label}.maxBytes must be a positive integer.`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
maxBytes = entry.maxBytes;
|
|
327
|
+
}
|
|
328
|
+
result.push({ id, label: text, types, folders, multiple, maxBytes });
|
|
329
|
+
});
|
|
330
|
+
return result.length ? result : void 0;
|
|
331
|
+
}
|
|
332
|
+
function validateMindooDBAppAccepts(value) {
|
|
333
|
+
const errors = [];
|
|
334
|
+
const accepts = readAccepts(value, errors);
|
|
335
|
+
return { accepts: errors.length ? void 0 : accepts, errors };
|
|
336
|
+
}
|
|
337
|
+
function mindooDBAppAcceptMatchesType(pattern, type) {
|
|
338
|
+
const [patternMajor, patternMinor] = pattern.toLowerCase().split("/");
|
|
339
|
+
const [major, minor] = (type || "application/octet-stream").toLowerCase().split(";")[0].trim().split("/");
|
|
340
|
+
return (patternMajor === "*" || patternMajor === major) && (patternMinor === "*" || patternMinor === minor);
|
|
341
|
+
}
|
|
342
|
+
var MINDOODB_APP_COMPONENTS_MAX = 16;
|
|
343
|
+
var COMPONENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,47}$/;
|
|
344
|
+
var COMPONENT_FIELD_PATTERN = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
345
|
+
var COMPONENT_INTENTS = ["create", "edit", "view"];
|
|
346
|
+
var COMPONENT_CREATE_MAX_BYTES = 16 * 1024;
|
|
347
|
+
function readComponentFieldMatch(value, label, errors) {
|
|
348
|
+
if (!isPlainObject(value)) {
|
|
349
|
+
errors.push(`${label} must be an object of field values.`);
|
|
350
|
+
return void 0;
|
|
351
|
+
}
|
|
352
|
+
const result = {};
|
|
353
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
354
|
+
if (!COMPONENT_FIELD_PATTERN.test(field)) {
|
|
355
|
+
errors.push(`${label} has an invalid field name ${JSON.stringify(field)}.`);
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
if (typeof fieldValue !== "string" && typeof fieldValue !== "number" && typeof fieldValue !== "boolean") {
|
|
359
|
+
errors.push(`${label}.${field} must be a string, number or boolean.`);
|
|
360
|
+
return void 0;
|
|
361
|
+
}
|
|
362
|
+
result[field] = fieldValue;
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
function readComponents(value, errors) {
|
|
367
|
+
if (value === void 0 || value === null) {
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
if (!Array.isArray(value)) {
|
|
371
|
+
errors.push("App definition components must be an array.");
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
if (value.length > MINDOODB_APP_COMPONENTS_MAX) {
|
|
375
|
+
errors.push(`App definition components must not have more than ${MINDOODB_APP_COMPONENTS_MAX} entries.`);
|
|
376
|
+
return void 0;
|
|
377
|
+
}
|
|
378
|
+
const result = [];
|
|
379
|
+
const ids = /* @__PURE__ */ new Set();
|
|
380
|
+
value.forEach((entry, index) => {
|
|
381
|
+
const label = `App definition components[${index}]`;
|
|
382
|
+
const errorCount = errors.length;
|
|
383
|
+
if (!isPlainObject(entry)) {
|
|
384
|
+
errors.push(`${label} must be an object.`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const id = typeof entry.id === "string" ? entry.id.trim() : "";
|
|
388
|
+
if (!COMPONENT_ID_PATTERN.test(id)) {
|
|
389
|
+
errors.push(`${label}.id must be 1-48 lowercase letters, digits, "-" or "_", received ${JSON.stringify(entry.id)}.`);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (ids.has(id)) {
|
|
393
|
+
errors.push(`${label}.id "${id}" is used twice.`);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
ids.add(id);
|
|
397
|
+
const text = readLocalizedText(entry.label, `${label}.label`, 80, errors);
|
|
398
|
+
if (!text) {
|
|
399
|
+
if (errors.length === errorCount) {
|
|
400
|
+
errors.push(`${label}.label is required.`);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const description = readLocalizedText(entry.description, `${label}.description`, 300, errors);
|
|
405
|
+
const icon = readListingAssetPath(entry.icon, `${label}.icon`, errors);
|
|
406
|
+
if (!Array.isArray(entry.intents) || !entry.intents.length) {
|
|
407
|
+
errors.push(`${label}.intents must be a non-empty array of "create", "edit" or "view".`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const intents = [];
|
|
411
|
+
for (const intent of entry.intents) {
|
|
412
|
+
if (!COMPONENT_INTENTS.includes(intent)) {
|
|
413
|
+
errors.push(`${label}.intents contains an unknown intent ${JSON.stringify(intent)}.`);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (!intents.includes(intent)) {
|
|
417
|
+
intents.push(intent);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const match = readComponentFieldMatch(entry.match, `${label}.match`, errors);
|
|
421
|
+
if (!match) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (!Object.keys(match).length) {
|
|
425
|
+
errors.push(`${label}.match must name at least one field, or the component would claim every document.`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
let create;
|
|
429
|
+
if (entry.create !== void 0) {
|
|
430
|
+
if (!isPlainObject(entry.create)) {
|
|
431
|
+
errors.push(`${label}.create must be an object of initial field values.`);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
for (const field of Object.keys(entry.create)) {
|
|
435
|
+
if (!COMPONENT_FIELD_PATTERN.test(field)) {
|
|
436
|
+
errors.push(`${label}.create has an invalid field name ${JSON.stringify(field)}.`);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (field in match) {
|
|
440
|
+
errors.push(`${label}.create must not repeat the match field "${field}".`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
let serialized;
|
|
445
|
+
try {
|
|
446
|
+
serialized = JSON.stringify(entry.create);
|
|
447
|
+
} catch {
|
|
448
|
+
errors.push(`${label}.create must be plain JSON.`);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (serialized.length > COMPONENT_CREATE_MAX_BYTES) {
|
|
452
|
+
errors.push(`${label}.create must not be larger than ${COMPONENT_CREATE_MAX_BYTES} bytes.`);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
create = JSON.parse(serialized);
|
|
456
|
+
}
|
|
457
|
+
let children;
|
|
458
|
+
if (entry.children !== void 0) {
|
|
459
|
+
if (!isPlainObject(entry.children)) {
|
|
460
|
+
errors.push(`${label}.children must be an object.`);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const linkField = typeof entry.children.linkField === "string" ? entry.children.linkField.trim() : "";
|
|
464
|
+
if (!COMPONENT_FIELD_PATTERN.test(linkField)) {
|
|
465
|
+
errors.push(`${label}.children.linkField must be a top-level field name.`);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const childMatch = entry.children.match === void 0 ? void 0 : readComponentFieldMatch(entry.children.match, `${label}.children.match`, errors);
|
|
469
|
+
if (entry.children.match !== void 0 && !childMatch) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (childMatch && linkField in childMatch) {
|
|
473
|
+
errors.push(`${label}.children.match must not name the link field "${linkField}".`);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
children = { linkField, ...childMatch && Object.keys(childMatch).length ? { match: childMatch } : {} };
|
|
477
|
+
}
|
|
478
|
+
if (errors.length !== errorCount) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
result.push({
|
|
482
|
+
id,
|
|
483
|
+
label: text,
|
|
484
|
+
...description ? { description } : {},
|
|
485
|
+
...icon ? { icon } : {},
|
|
486
|
+
intents,
|
|
487
|
+
match,
|
|
488
|
+
...create ? { create } : {},
|
|
489
|
+
...children ? { children } : {}
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
return result.length ? result : void 0;
|
|
493
|
+
}
|
|
494
|
+
function validateMindooDBAppComponents(value) {
|
|
495
|
+
const errors = [];
|
|
496
|
+
const components = readComponents(value, errors);
|
|
497
|
+
return { components: errors.length ? void 0 : components, errors };
|
|
498
|
+
}
|
|
499
|
+
function mindooDBAppComponentFieldsMatch(match, data) {
|
|
500
|
+
if (!data) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
return Object.entries(match).every(([field, value]) => data[field] === value);
|
|
504
|
+
}
|
|
505
|
+
function readListingAssetPath(value, label, errors) {
|
|
506
|
+
if (value === void 0 || value === null) {
|
|
507
|
+
return void 0;
|
|
508
|
+
}
|
|
509
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
510
|
+
errors.push(`${label} must be a non-empty string.`);
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
const trimmed = value.trim();
|
|
514
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed)?.[1]?.toLowerCase();
|
|
515
|
+
if (scheme && scheme !== "https") {
|
|
516
|
+
errors.push(`${label} must be a path on the app origin or an https: URL.`);
|
|
517
|
+
return void 0;
|
|
518
|
+
}
|
|
519
|
+
if (trimmed.startsWith("//")) {
|
|
520
|
+
errors.push(`${label} must be a path on the app origin or an https: URL.`);
|
|
521
|
+
return void 0;
|
|
522
|
+
}
|
|
523
|
+
return trimmed;
|
|
524
|
+
}
|
|
525
|
+
function readListing(value, errors) {
|
|
526
|
+
if (value === void 0 || value === null) {
|
|
527
|
+
return void 0;
|
|
528
|
+
}
|
|
529
|
+
if (!isPlainObject(value)) {
|
|
530
|
+
errors.push("App definition listing must be an object.");
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
const listing = {};
|
|
534
|
+
const summary = readLocalizedText(
|
|
535
|
+
value.summary,
|
|
536
|
+
"App definition listing.summary",
|
|
537
|
+
MINDOODB_APP_LISTING_SUMMARY_MAX,
|
|
538
|
+
errors
|
|
539
|
+
);
|
|
540
|
+
if (summary) listing.summary = summary;
|
|
541
|
+
const description = readLocalizedText(
|
|
542
|
+
value.description,
|
|
543
|
+
"App definition listing.description",
|
|
544
|
+
MINDOODB_APP_LISTING_DESCRIPTION_MAX,
|
|
545
|
+
errors
|
|
546
|
+
);
|
|
547
|
+
if (description) listing.description = description;
|
|
548
|
+
const descriptionMarkdown = readLocalizedText(
|
|
549
|
+
value.descriptionMarkdown,
|
|
550
|
+
"App definition listing.descriptionMarkdown",
|
|
551
|
+
MINDOODB_APP_LISTING_DESCRIPTION_MAX,
|
|
552
|
+
errors
|
|
553
|
+
);
|
|
554
|
+
if (descriptionMarkdown) listing.descriptionMarkdown = descriptionMarkdown;
|
|
555
|
+
const icon = readListingAssetPath(value.icon, "App definition listing.icon", errors);
|
|
556
|
+
if (icon) listing.icon = icon;
|
|
557
|
+
if (value.screenshots !== void 0 && value.screenshots !== null) {
|
|
558
|
+
if (!Array.isArray(value.screenshots)) {
|
|
559
|
+
errors.push("App definition listing.screenshots must be an array.");
|
|
560
|
+
} else if (value.screenshots.length > MINDOODB_APP_LISTING_MAX_SCREENSHOTS) {
|
|
561
|
+
errors.push(
|
|
562
|
+
`App definition listing.screenshots must not list more than ${MINDOODB_APP_LISTING_MAX_SCREENSHOTS} images.`
|
|
563
|
+
);
|
|
564
|
+
} else {
|
|
565
|
+
const screenshots = [];
|
|
566
|
+
value.screenshots.forEach((entry, index) => {
|
|
567
|
+
const label = `App definition listing.screenshots[${index}]`;
|
|
568
|
+
const raw = typeof entry === "string" ? { file: entry } : entry;
|
|
569
|
+
if (!isPlainObject(raw)) {
|
|
570
|
+
errors.push(`${label} must be a path or an object with a file.`);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const file = readListingAssetPath(raw.file, `${label}.file`, errors);
|
|
574
|
+
if (!file) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const caption = readLocalizedText(raw.caption, `${label}.caption`, MINDOODB_APP_LISTING_SUMMARY_MAX, errors);
|
|
578
|
+
screenshots.push(caption ? { file, caption } : { file });
|
|
579
|
+
});
|
|
580
|
+
if (screenshots.length) listing.screenshots = screenshots;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (value.publisher !== void 0 && value.publisher !== null) {
|
|
584
|
+
const publisher = isPlainObject(value.publisher) ? value.publisher : {};
|
|
585
|
+
const name = typeof publisher.name === "string" ? publisher.name.trim() : "";
|
|
586
|
+
if (!name) {
|
|
587
|
+
errors.push("App definition listing.publisher must be an object with a non-empty name.");
|
|
588
|
+
} else {
|
|
589
|
+
const url = readListingAssetPath(publisher.url, "App definition listing.publisher.url", errors);
|
|
590
|
+
if (url && !/^https:\/\//i.test(url)) {
|
|
591
|
+
errors.push("App definition listing.publisher.url must be an absolute https: URL.");
|
|
592
|
+
} else {
|
|
593
|
+
listing.publisher = url ? { name, url } : { name };
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return Object.keys(listing).length ? listing : void 0;
|
|
598
|
+
}
|
|
599
|
+
function resolveMindooDBAppLocalizedText(text, locale) {
|
|
600
|
+
if (text === void 0) {
|
|
601
|
+
return "";
|
|
602
|
+
}
|
|
603
|
+
if (typeof text === "string") {
|
|
604
|
+
return text;
|
|
605
|
+
}
|
|
606
|
+
const normalized = locale.trim();
|
|
607
|
+
const language = normalized.split(/[-_]/)[0]?.toLowerCase() ?? "";
|
|
608
|
+
return text[normalized] ?? text[normalized.toLowerCase()] ?? (language ? text[language] : void 0) ?? text.en;
|
|
609
|
+
}
|
|
610
|
+
function resolveMindooDBAppListingAssetUrl(path, originUrl) {
|
|
611
|
+
try {
|
|
612
|
+
const base = new URL(originUrl.endsWith("/") ? originUrl : `${originUrl}/`);
|
|
613
|
+
const resolved = new URL(path, base);
|
|
614
|
+
if (resolved.protocol !== "https:" && resolved.protocol !== base.protocol) {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
return resolved.toString();
|
|
618
|
+
} catch {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function validateMindooDBAppDefinition(raw) {
|
|
623
|
+
const errors = [];
|
|
624
|
+
if (!isPlainObject(raw)) {
|
|
625
|
+
return { definition: null, errors: ["App definition must be a JSON object."] };
|
|
626
|
+
}
|
|
627
|
+
if (raw.format !== MINDOODB_APP_DEFINITION_FORMAT) {
|
|
628
|
+
errors.push(
|
|
629
|
+
`App definition format must be "${MINDOODB_APP_DEFINITION_FORMAT}", received ${JSON.stringify(raw.format)}.`
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
if (raw.formatVersion !== MINDOODB_APP_DEFINITION_VERSION) {
|
|
633
|
+
errors.push(
|
|
634
|
+
`Unsupported app definition version ${JSON.stringify(raw.formatVersion)}, expected ${MINDOODB_APP_DEFINITION_VERSION}.`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
const appId = typeof raw.appId === "string" ? raw.appId.trim() : "";
|
|
638
|
+
if (!appId) {
|
|
639
|
+
errors.push("App definition is missing a non-empty appId.");
|
|
640
|
+
}
|
|
641
|
+
const label = typeof raw.label === "string" ? raw.label.trim() : "";
|
|
642
|
+
if (!label) {
|
|
643
|
+
errors.push("App definition is missing a non-empty label.");
|
|
644
|
+
}
|
|
645
|
+
const runtime = raw.runtime;
|
|
646
|
+
if (runtime !== void 0 && runtime !== "iframe" && runtime !== "window") {
|
|
647
|
+
errors.push(`App definition runtime must be "iframe" or "window", received ${JSON.stringify(runtime)}.`);
|
|
648
|
+
}
|
|
649
|
+
const hosting = raw.hosting;
|
|
650
|
+
if (hosting !== void 0 && hosting !== "external" && hosting !== "hosted") {
|
|
651
|
+
errors.push(
|
|
652
|
+
`App definition hosting must be "external" or "hosted", received ${JSON.stringify(hosting)}.`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
const description = readOptionalString(raw, "description", errors);
|
|
656
|
+
const version = readOptionalString(raw, "version", errors);
|
|
657
|
+
const defaultLaunchDatabaseId = readOptionalString(raw, "defaultLaunchDatabaseId", errors);
|
|
658
|
+
const networkAllowlist = readNetworkAllowlist(raw.networkAllowlist, errors);
|
|
659
|
+
const launchParameters = readStringRecord(raw, "launchParameters", errors);
|
|
660
|
+
const databases = readDatabases(raw.databases, errors);
|
|
661
|
+
const permissions = readPermissions(
|
|
662
|
+
raw.permissions,
|
|
663
|
+
"App definition permissions",
|
|
664
|
+
MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS,
|
|
665
|
+
errors
|
|
666
|
+
);
|
|
667
|
+
const allowPopups = readOptionalBoolean(raw, "allowPopups", errors);
|
|
668
|
+
const allowCamera = readOptionalBoolean(raw, "allowCamera", errors);
|
|
669
|
+
const allowMicrophone = readOptionalBoolean(raw, "allowMicrophone", errors);
|
|
670
|
+
const allowGeolocation = readOptionalBoolean(raw, "allowGeolocation", errors);
|
|
671
|
+
const allowClipboardWrite = readOptionalBoolean(raw, "allowClipboardWrite", errors);
|
|
672
|
+
const allowMotion = readOptionalBoolean(raw, "allowMotion", errors);
|
|
673
|
+
const allowWebRtc = readOptionalBoolean(raw, "allowWebRtc", errors);
|
|
674
|
+
const allowWorkers = readOptionalBoolean(raw, "allowWorkers", errors);
|
|
675
|
+
const listing = readListing(raw.listing, errors);
|
|
676
|
+
const accepts = readAccepts(raw.accepts, errors);
|
|
677
|
+
const components = readComponents(raw.components, errors);
|
|
678
|
+
const agentToolPrefix = readOptionalString(raw, "agentToolPrefix", errors);
|
|
679
|
+
if (agentToolPrefix !== void 0 && (!/^[a-z][a-z0-9_]{0,15}$/.test(agentToolPrefix) || agentToolPrefix.endsWith("_") || /^haven(?:_|$)/.test(agentToolPrefix))) {
|
|
680
|
+
errors.push(
|
|
681
|
+
`App definition agentToolPrefix must be 1-16 lowercase letters, digits or "_", start with a letter and not be "haven", received ${JSON.stringify(agentToolPrefix)}.`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
if (defaultLaunchDatabaseId && databases && !databases.some((database) => database.logicalDatabaseId === defaultLaunchDatabaseId)) {
|
|
685
|
+
errors.push(
|
|
686
|
+
`App definition defaultLaunchDatabaseId "${defaultLaunchDatabaseId}" is not one of the declared databases.`
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
if (errors.length) {
|
|
690
|
+
return { definition: null, errors };
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
definition: {
|
|
694
|
+
format: MINDOODB_APP_DEFINITION_FORMAT,
|
|
695
|
+
formatVersion: MINDOODB_APP_DEFINITION_VERSION,
|
|
696
|
+
appId,
|
|
697
|
+
label,
|
|
698
|
+
description,
|
|
699
|
+
version,
|
|
700
|
+
runtime,
|
|
701
|
+
hosting,
|
|
702
|
+
networkAllowlist,
|
|
703
|
+
allowPopups,
|
|
704
|
+
allowCamera,
|
|
705
|
+
allowMicrophone,
|
|
706
|
+
allowGeolocation,
|
|
707
|
+
allowClipboardWrite,
|
|
708
|
+
allowMotion,
|
|
709
|
+
allowWebRtc,
|
|
710
|
+
allowWorkers,
|
|
711
|
+
agentToolPrefix,
|
|
712
|
+
permissions,
|
|
713
|
+
launchParameters,
|
|
714
|
+
defaultLaunchDatabaseId,
|
|
715
|
+
databases,
|
|
716
|
+
accepts,
|
|
717
|
+
components,
|
|
718
|
+
listing
|
|
719
|
+
},
|
|
720
|
+
errors: []
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function resolveMindooDBAppDefinitionUrl(baseUrl) {
|
|
724
|
+
const trimmed = baseUrl.trim();
|
|
725
|
+
if (!trimmed) {
|
|
726
|
+
return "";
|
|
727
|
+
}
|
|
728
|
+
const suffix = `/${MINDOODB_APP_DEFINITION_FILE_NAME}`;
|
|
729
|
+
try {
|
|
730
|
+
const url = new URL(trimmed);
|
|
731
|
+
const path = url.pathname.replace(/\/+$/, "") || "/";
|
|
732
|
+
url.search = "";
|
|
733
|
+
url.hash = "";
|
|
734
|
+
url.pathname = path.endsWith(suffix) || path === suffix ? path : `${path === "/" ? "" : path}${suffix}`;
|
|
735
|
+
return url.toString();
|
|
736
|
+
} catch {
|
|
737
|
+
const withoutSlash = trimmed.replace(/\/+$/, "");
|
|
738
|
+
return withoutSlash.toLowerCase().endsWith(suffix) ? withoutSlash : `${withoutSlash}${suffix}`;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
69
742
|
// src/drag.ts
|
|
70
743
|
var MINDOODB_APP_MAX_DRAG_TYPES = 8;
|
|
71
744
|
var MINDOODB_APP_MAX_DRAG_PAYLOAD_BYTES = 32 * 1024;
|
|
@@ -665,6 +1338,238 @@ function installHostShortcutCapture(options) {
|
|
|
665
1338
|
// src/client/createMindooDBAppBridge.ts
|
|
666
1339
|
import { parseMindooDBFormulaBooleanExpression } from "mindoodb-view-language";
|
|
667
1340
|
|
|
1341
|
+
// src/client/embeds.ts
|
|
1342
|
+
function isEmbedEventMessage(message) {
|
|
1343
|
+
return message.kind === "embed-event";
|
|
1344
|
+
}
|
|
1345
|
+
function toRect(rect) {
|
|
1346
|
+
return {
|
|
1347
|
+
left: Number(rect.left) || 0,
|
|
1348
|
+
top: Number(rect.top) || 0,
|
|
1349
|
+
width: Math.max(0, Number(rect.width) || 0),
|
|
1350
|
+
height: Math.max(0, Number(rect.height) || 0)
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
function containerRect(container) {
|
|
1354
|
+
const bounds = container.getBoundingClientRect();
|
|
1355
|
+
return toRect({ left: bounds.left, top: bounds.top, width: bounds.width, height: bounds.height });
|
|
1356
|
+
}
|
|
1357
|
+
var ContainerTracker = class {
|
|
1358
|
+
constructor(container, report) {
|
|
1359
|
+
this.container = container;
|
|
1360
|
+
this.report = report;
|
|
1361
|
+
this.observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(this.schedule);
|
|
1362
|
+
this.observer?.observe(container);
|
|
1363
|
+
window.addEventListener("resize", this.schedule);
|
|
1364
|
+
window.addEventListener("scroll", this.schedule, true);
|
|
1365
|
+
}
|
|
1366
|
+
container;
|
|
1367
|
+
report;
|
|
1368
|
+
frameRequest = null;
|
|
1369
|
+
observer;
|
|
1370
|
+
schedule = () => {
|
|
1371
|
+
if (this.frameRequest !== null) {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
this.frameRequest = window.requestAnimationFrame(() => {
|
|
1375
|
+
this.frameRequest = null;
|
|
1376
|
+
this.report(containerRect(this.container));
|
|
1377
|
+
});
|
|
1378
|
+
};
|
|
1379
|
+
dispose() {
|
|
1380
|
+
this.observer?.disconnect();
|
|
1381
|
+
window.removeEventListener("resize", this.schedule);
|
|
1382
|
+
window.removeEventListener("scroll", this.schedule, true);
|
|
1383
|
+
if (this.frameRequest !== null) {
|
|
1384
|
+
window.cancelAnimationFrame(this.frameRequest);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
function createComponentFrame(container, url, allow) {
|
|
1389
|
+
const frame = container.ownerDocument.createElement("iframe");
|
|
1390
|
+
frame.src = url;
|
|
1391
|
+
if (allow) {
|
|
1392
|
+
frame.setAttribute("allow", allow);
|
|
1393
|
+
}
|
|
1394
|
+
frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
|
|
1395
|
+
frame.dataset.mindoodbEmbed = "";
|
|
1396
|
+
Object.assign(frame.style, {
|
|
1397
|
+
position: "absolute",
|
|
1398
|
+
inset: "0",
|
|
1399
|
+
width: "100%",
|
|
1400
|
+
height: "100%",
|
|
1401
|
+
border: "0",
|
|
1402
|
+
display: "block"
|
|
1403
|
+
});
|
|
1404
|
+
container.appendChild(frame);
|
|
1405
|
+
return frame;
|
|
1406
|
+
}
|
|
1407
|
+
var MindooDBAppEmbedImpl = class {
|
|
1408
|
+
constructor(rpc, embedId, componentKey, databaseId, docId, frame, container) {
|
|
1409
|
+
this.rpc = rpc;
|
|
1410
|
+
this.embedId = embedId;
|
|
1411
|
+
this.componentKey = componentKey;
|
|
1412
|
+
this.databaseId = databaseId;
|
|
1413
|
+
this.docId = docId;
|
|
1414
|
+
this.frame = frame;
|
|
1415
|
+
this.closed = new Promise((resolve) => {
|
|
1416
|
+
this.resolveClosed = resolve;
|
|
1417
|
+
});
|
|
1418
|
+
if (container) {
|
|
1419
|
+
this.tracker = new ContainerTracker(container, (rect) => {
|
|
1420
|
+
void this.setRect(rect).catch(() => {
|
|
1421
|
+
});
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
rpc;
|
|
1426
|
+
embedId;
|
|
1427
|
+
componentKey;
|
|
1428
|
+
databaseId;
|
|
1429
|
+
docId;
|
|
1430
|
+
frame;
|
|
1431
|
+
closed;
|
|
1432
|
+
resolveClosed;
|
|
1433
|
+
closedEvent = null;
|
|
1434
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1435
|
+
tracker = null;
|
|
1436
|
+
get placement() {
|
|
1437
|
+
return this.frame ? "frame" : "overlay";
|
|
1438
|
+
}
|
|
1439
|
+
async setRect(rect) {
|
|
1440
|
+
if (this.closedEvent) {
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
await this.rpc.call("embeds.setRect", { embedId: this.embedId, rect: toRect(rect) });
|
|
1444
|
+
}
|
|
1445
|
+
async setVisible(visible) {
|
|
1446
|
+
if (this.closedEvent) {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
if (this.frame) {
|
|
1450
|
+
this.frame.style.visibility = visible ? "" : "hidden";
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
await this.rpc.call("embeds.setVisible", { embedId: this.embedId, visible: visible === true });
|
|
1454
|
+
}
|
|
1455
|
+
async close() {
|
|
1456
|
+
if (!this.closedEvent) {
|
|
1457
|
+
await this.rpc.call("embeds.close", { embedId: this.embedId });
|
|
1458
|
+
}
|
|
1459
|
+
return await this.closed;
|
|
1460
|
+
}
|
|
1461
|
+
onClosed(listener) {
|
|
1462
|
+
if (this.closedEvent) {
|
|
1463
|
+
const event = this.closedEvent;
|
|
1464
|
+
queueMicrotask(() => listener(event));
|
|
1465
|
+
return () => {
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
this.listeners.add(listener);
|
|
1469
|
+
return () => {
|
|
1470
|
+
this.listeners.delete(listener);
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
settle(event) {
|
|
1474
|
+
if (this.closedEvent) {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
this.closedEvent = event;
|
|
1478
|
+
this.tracker?.dispose();
|
|
1479
|
+
this.tracker = null;
|
|
1480
|
+
this.frame?.remove();
|
|
1481
|
+
this.resolveClosed(event);
|
|
1482
|
+
this.listeners.forEach((listener) => {
|
|
1483
|
+
try {
|
|
1484
|
+
listener(event);
|
|
1485
|
+
} catch (error) {
|
|
1486
|
+
console.error("MindooDB embed closed listener failed.", error);
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
this.listeners.clear();
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
var MindooDBAppEmbedsClient = class {
|
|
1493
|
+
constructor(rpc) {
|
|
1494
|
+
this.rpc = rpc;
|
|
1495
|
+
this.stopListening = this.rpc.addMessageListener((message) => {
|
|
1496
|
+
if (!isEmbedEventMessage(message) || message.event?.type !== "closed") {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const { type: _type, ...event } = message.event;
|
|
1500
|
+
const embed = this.open.get(event.embedId);
|
|
1501
|
+
if (embed) {
|
|
1502
|
+
this.open.delete(event.embedId);
|
|
1503
|
+
embed.settle(event);
|
|
1504
|
+
} else {
|
|
1505
|
+
this.early.set(event.embedId, event);
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
this.components = {
|
|
1509
|
+
list: async (query) => {
|
|
1510
|
+
const response = await this.rpc.call("components.list", {
|
|
1511
|
+
query: query ?? {}
|
|
1512
|
+
});
|
|
1513
|
+
return response?.components ?? [];
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
this.embeds = {
|
|
1517
|
+
open: async (input) => {
|
|
1518
|
+
const { container, rect, ...rest } = input;
|
|
1519
|
+
const initialRect = container ? containerRect(container) : toRect(rect ?? { left: 0, top: 0, width: 0, height: 0 });
|
|
1520
|
+
const response = await this.rpc.call("embeds.open", {
|
|
1521
|
+
// `frame`: Haven hands back a page for the container instead of laying
|
|
1522
|
+
// the component over this app. Havens that do not know it ignore it.
|
|
1523
|
+
input: { ...rest, rect: initialRect, ...container ? { placement: "frame" } : {} }
|
|
1524
|
+
});
|
|
1525
|
+
const frame = container && response.frame?.url ? createComponentFrame(container, response.frame.url, response.frame.allow) : null;
|
|
1526
|
+
const embed = new MindooDBAppEmbedImpl(
|
|
1527
|
+
this.rpc,
|
|
1528
|
+
response.embedId,
|
|
1529
|
+
input.componentKey,
|
|
1530
|
+
input.databaseId,
|
|
1531
|
+
input.docId,
|
|
1532
|
+
frame,
|
|
1533
|
+
container
|
|
1534
|
+
);
|
|
1535
|
+
const early = this.early.get(embed.embedId);
|
|
1536
|
+
if (early) {
|
|
1537
|
+
this.early.delete(embed.embedId);
|
|
1538
|
+
embed.settle(early);
|
|
1539
|
+
} else {
|
|
1540
|
+
this.open.set(embed.embedId, embed);
|
|
1541
|
+
}
|
|
1542
|
+
return embed;
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
this.embedding = {
|
|
1546
|
+
complete: async (result) => {
|
|
1547
|
+
await this.rpc.call("embedding.finish", {
|
|
1548
|
+
reason: "completed",
|
|
1549
|
+
...result === void 0 ? {} : { result: JSON.parse(JSON.stringify(result)) }
|
|
1550
|
+
});
|
|
1551
|
+
},
|
|
1552
|
+
cancel: async (message) => {
|
|
1553
|
+
await this.rpc.call("embedding.finish", {
|
|
1554
|
+
reason: "cancelled",
|
|
1555
|
+
...message ? { message: String(message) } : {}
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
rpc;
|
|
1561
|
+
components;
|
|
1562
|
+
embeds;
|
|
1563
|
+
embedding;
|
|
1564
|
+
open = /* @__PURE__ */ new Map();
|
|
1565
|
+
/** Events that arrived before `embeds.open` resolved with their id. */
|
|
1566
|
+
early = /* @__PURE__ */ new Map();
|
|
1567
|
+
stopListening;
|
|
1568
|
+
dispose() {
|
|
1569
|
+
this.stopListening();
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
|
|
668
1573
|
// src/client/incomingContent.ts
|
|
669
1574
|
var MINDOODB_APP_INCOMING_READ_CHUNK_BYTES = 1024 * 1024;
|
|
670
1575
|
function isIncomingContentMessage(message) {
|
|
@@ -2051,6 +2956,10 @@ var MindooDBAppSessionImpl = class {
|
|
|
2051
2956
|
this.drag = new MindooDBAppDragApiImpl(this.rpc);
|
|
2052
2957
|
this.agent = new MindooDBAppAgentApiImpl(this.rpc);
|
|
2053
2958
|
this.incoming = new MindooDBAppIncomingApiImpl(this.rpc);
|
|
2959
|
+
this.embedsClient = new MindooDBAppEmbedsClient(this.rpc);
|
|
2960
|
+
this.components = this.embedsClient.components;
|
|
2961
|
+
this.embeds = this.embedsClient.embeds;
|
|
2962
|
+
this.embedding = this.embedsClient.embedding;
|
|
2054
2963
|
this.storage = {
|
|
2055
2964
|
snapshot: async (options) => await this.rpc.call("appStorage.snapshot", {
|
|
2056
2965
|
prefixes: options?.prefixes
|
|
@@ -2115,6 +3024,10 @@ var MindooDBAppSessionImpl = class {
|
|
|
2115
3024
|
storage;
|
|
2116
3025
|
agent;
|
|
2117
3026
|
incoming;
|
|
3027
|
+
embedsClient;
|
|
3028
|
+
components;
|
|
3029
|
+
embeds;
|
|
3030
|
+
embedding;
|
|
2118
3031
|
beforeCloseListeners = /* @__PURE__ */ new Set();
|
|
2119
3032
|
hostShortcuts = DEFAULT_HAVEN_HOST_SHORTCUTS;
|
|
2120
3033
|
stopHostShortcuts;
|
|
@@ -2293,6 +3206,7 @@ var MindooDBAppSessionImpl = class {
|
|
|
2293
3206
|
this.drag.dispose();
|
|
2294
3207
|
this.agent.dispose();
|
|
2295
3208
|
this.incoming.dispose();
|
|
3209
|
+
this.embedsClient.dispose();
|
|
2296
3210
|
try {
|
|
2297
3211
|
await this.rpc.call("session.disconnect", {});
|
|
2298
3212
|
} finally {
|
|
@@ -2352,6 +3266,24 @@ export {
|
|
|
2352
3266
|
isValidMindooDBDocumentId,
|
|
2353
3267
|
mindooDBDocumentId,
|
|
2354
3268
|
assertValidMindooDBCreateIds,
|
|
3269
|
+
MINDOODB_APP_DEFINITION_FORMAT,
|
|
3270
|
+
MINDOODB_APP_DEFINITION_VERSION,
|
|
3271
|
+
MINDOODB_APP_DEFINITION_FILE_NAME,
|
|
3272
|
+
MINDOODB_APP_DEFINITION_PERMISSIONS,
|
|
3273
|
+
MINDOODB_APP_DEFINITION_REGISTRATION_PERMISSIONS,
|
|
3274
|
+
MINDOODB_APP_LISTING_SUMMARY_MAX,
|
|
3275
|
+
MINDOODB_APP_LISTING_DESCRIPTION_MAX,
|
|
3276
|
+
MINDOODB_APP_LISTING_MAX_SCREENSHOTS,
|
|
3277
|
+
MINDOODB_APP_ACCEPTS_MAX,
|
|
3278
|
+
validateMindooDBAppAccepts,
|
|
3279
|
+
mindooDBAppAcceptMatchesType,
|
|
3280
|
+
MINDOODB_APP_COMPONENTS_MAX,
|
|
3281
|
+
validateMindooDBAppComponents,
|
|
3282
|
+
mindooDBAppComponentFieldsMatch,
|
|
3283
|
+
resolveMindooDBAppLocalizedText,
|
|
3284
|
+
resolveMindooDBAppListingAssetUrl,
|
|
3285
|
+
validateMindooDBAppDefinition,
|
|
3286
|
+
resolveMindooDBAppDefinitionUrl,
|
|
2355
3287
|
MINDOODB_APP_MAX_DRAG_TYPES,
|
|
2356
3288
|
MINDOODB_APP_MAX_DRAG_PAYLOAD_BYTES,
|
|
2357
3289
|
MINDOODB_APP_MAX_DRAG_PREVIEW_BYTES,
|