mindoodb-app-sdk 0.0.19 → 0.0.26
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 +253 -12
- package/TESTING.md +37 -0
- package/dist/index.d.ts +18 -6
- package/dist/index.js +270 -8
- package/dist/testing/index.d.ts +23 -1
- package/dist/testing/index.js +501 -3
- package/dist/types-B6vEy1VC.d.ts +1565 -0
- package/package.json +13 -3
- package/dist/types-nFt-aryL.d.ts +0 -780
package/dist/testing/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// src/testing/index.ts
|
|
2
|
+
import * as Automerge from "@automerge/automerge";
|
|
3
|
+
import {
|
|
4
|
+
evaluateExpression,
|
|
5
|
+
parseMindooDBFormulaBooleanExpression
|
|
6
|
+
} from "mindoodb-view-language";
|
|
2
7
|
var PROTOCOL = "mindoodb-app-bridge";
|
|
3
8
|
function createBridgeErrorPayload(error, fallbackCode = "bridge-error") {
|
|
4
9
|
if (error && typeof error === "object" && "message" in error) {
|
|
@@ -27,7 +32,11 @@ function applyDocumentUpdatePatch(current, patch) {
|
|
|
27
32
|
applyMockTextPatch(next, textPatch.path, textPatch.edits);
|
|
28
33
|
}
|
|
29
34
|
for (const richTextPatch of patch.richText ?? []) {
|
|
30
|
-
|
|
35
|
+
const spans = richTextPatch.spansSequence?.at(-1) ?? richTextPatch.spans ?? [];
|
|
36
|
+
setValueAtPath(next, richTextPatch.path, structuredClone(spans));
|
|
37
|
+
}
|
|
38
|
+
for (const richTextStepPatch of patch.richTextSteps ?? []) {
|
|
39
|
+
applyMockRichTextSteps(next, richTextStepPatch.path, richTextStepPatch.steps);
|
|
31
40
|
}
|
|
32
41
|
return next;
|
|
33
42
|
}
|
|
@@ -49,6 +58,19 @@ function applyMockJsonPatch(target, patch) {
|
|
|
49
58
|
const list = readListAtPath(target, operation.path);
|
|
50
59
|
list.splice(operation.index, 0, ...structuredClone(operation.values));
|
|
51
60
|
}
|
|
61
|
+
for (const operation of patch.textSplice ?? []) {
|
|
62
|
+
applyMockTextPatch(target, operation.path, [{
|
|
63
|
+
index: operation.index,
|
|
64
|
+
deleteCount: operation.deleteCount,
|
|
65
|
+
...operation.insert !== void 0 ? { insert: operation.insert } : {}
|
|
66
|
+
}]);
|
|
67
|
+
}
|
|
68
|
+
for (const operation of patch.textMark ?? []) {
|
|
69
|
+
applyMockTextMarks(target, operation.path, operation.index, operation.length, operation.marks, false);
|
|
70
|
+
}
|
|
71
|
+
for (const operation of patch.textUnmark ?? []) {
|
|
72
|
+
applyMockTextMarks(target, operation.path, operation.index, operation.length, Object.fromEntries(operation.names.map((name) => [name, null])), true);
|
|
73
|
+
}
|
|
52
74
|
}
|
|
53
75
|
function setValueAtPath(target, path, value) {
|
|
54
76
|
if (path.length === 0) {
|
|
@@ -128,6 +150,21 @@ function applyMockTextPatch(target, path, edits) {
|
|
|
128
150
|
}
|
|
129
151
|
parent[leaf] = value;
|
|
130
152
|
}
|
|
153
|
+
function applyMockTextMarks(target, path, index, length, marks, remove) {
|
|
154
|
+
const parent = ensureParentAtPath(target, path);
|
|
155
|
+
const leaf = path[path.length - 1];
|
|
156
|
+
const marksKey = `${String(leaf)}__marks`;
|
|
157
|
+
const existing = Array.isArray(parent[marksKey]) ? parent[marksKey] : [];
|
|
158
|
+
parent[marksKey] = [
|
|
159
|
+
...existing,
|
|
160
|
+
{
|
|
161
|
+
index,
|
|
162
|
+
length,
|
|
163
|
+
marks: structuredClone(marks),
|
|
164
|
+
remove
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
}
|
|
131
168
|
function readRichTextSpansAtPath(target, path) {
|
|
132
169
|
let value = target;
|
|
133
170
|
for (const segment of path) {
|
|
@@ -136,8 +173,38 @@ function readRichTextSpansAtPath(target, path) {
|
|
|
136
173
|
}
|
|
137
174
|
value = value[segment];
|
|
138
175
|
}
|
|
176
|
+
if (typeof value === "string") {
|
|
177
|
+
return value.length > 0 ? [{ type: "text", value }] : [];
|
|
178
|
+
}
|
|
139
179
|
return Array.isArray(value) ? value : [];
|
|
140
180
|
}
|
|
181
|
+
function applyMockRichTextSteps(target, path, steps) {
|
|
182
|
+
if (path.length === 0) {
|
|
183
|
+
throw new Error("Rich-text steps path must contain at least one segment");
|
|
184
|
+
}
|
|
185
|
+
const parent = ensureParentAtPath(target, path);
|
|
186
|
+
const leaf = path[path.length - 1];
|
|
187
|
+
let text = materializeMockRichTextValue(parent[leaf]);
|
|
188
|
+
for (const step of steps) {
|
|
189
|
+
text = text.slice(0, step.index) + (step.insert ?? "") + text.slice(step.index + step.deleteCount);
|
|
190
|
+
}
|
|
191
|
+
parent[leaf] = text;
|
|
192
|
+
}
|
|
193
|
+
function materializeMockRichTextValue(value) {
|
|
194
|
+
if (typeof value === "string") {
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
if (!Array.isArray(value)) {
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
200
|
+
return value.map((span) => {
|
|
201
|
+
if (!span || typeof span !== "object") {
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
const record = span;
|
|
205
|
+
return record.type === "text" && typeof record.value === "string" ? record.value : "";
|
|
206
|
+
}).join("");
|
|
207
|
+
}
|
|
141
208
|
function mergeLaunchContext(current, patch) {
|
|
142
209
|
if (!patch) {
|
|
143
210
|
return {
|
|
@@ -244,10 +311,12 @@ function createDefaultLaunchContext(patch) {
|
|
|
244
311
|
uiPreferences: {
|
|
245
312
|
iosMultitaskingOptimized: false
|
|
246
313
|
},
|
|
314
|
+
locale: "en",
|
|
247
315
|
user: {
|
|
248
316
|
id: "user-1",
|
|
249
317
|
username: "Test User"
|
|
250
318
|
},
|
|
319
|
+
licensedProducts: [],
|
|
251
320
|
launchParameters: {},
|
|
252
321
|
databases: [],
|
|
253
322
|
views: []
|
|
@@ -291,6 +360,22 @@ function createDefaultWritableAttachmentStream() {
|
|
|
291
360
|
}
|
|
292
361
|
};
|
|
293
362
|
}
|
|
363
|
+
function loadMockAutomergeDocument(document) {
|
|
364
|
+
if (document.automergeBinary) {
|
|
365
|
+
return Automerge.load(document.automergeBinary);
|
|
366
|
+
}
|
|
367
|
+
const initialDoc = Automerge.from({
|
|
368
|
+
...structuredClone(document.data),
|
|
369
|
+
body: typeof document.data.body === "string" ? document.data.body : ""
|
|
370
|
+
});
|
|
371
|
+
document.automergeBinary = Automerge.save(initialDoc);
|
|
372
|
+
return initialDoc;
|
|
373
|
+
}
|
|
374
|
+
function persistMockAutomergeDocument(document, automergeDoc) {
|
|
375
|
+
document.automergeBinary = Automerge.save(automergeDoc);
|
|
376
|
+
document.data = structuredClone(automergeDoc);
|
|
377
|
+
document.heads = Automerge.getHeads(automergeDoc);
|
|
378
|
+
}
|
|
294
379
|
function getFieldValue(source, field) {
|
|
295
380
|
return field.split(".").reduce((current, part) => {
|
|
296
381
|
if (current && typeof current === "object" && part in current) {
|
|
@@ -314,6 +399,138 @@ function decodeMockListCursor(cursor) {
|
|
|
314
399
|
const offset = Number.parseInt(cursor, 10);
|
|
315
400
|
return Number.isFinite(offset) && offset >= 0 ? offset : 0;
|
|
316
401
|
}
|
|
402
|
+
function mockExpressionToBoolean(value) {
|
|
403
|
+
if (typeof value === "boolean") {
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
if (typeof value === "number") {
|
|
407
|
+
return value !== 0;
|
|
408
|
+
}
|
|
409
|
+
if (typeof value === "string") {
|
|
410
|
+
const normalized = value.trim().toLowerCase();
|
|
411
|
+
return normalized !== "" && normalized !== "false" && normalized !== "0" && normalized !== "no";
|
|
412
|
+
}
|
|
413
|
+
return Boolean(value);
|
|
414
|
+
}
|
|
415
|
+
function compareMockQueryValues(left, right) {
|
|
416
|
+
const leftNumber = typeof left === "number" ? left : Number(left);
|
|
417
|
+
const rightNumber = typeof right === "number" ? right : Number(right);
|
|
418
|
+
if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) {
|
|
419
|
+
return leftNumber - rightNumber;
|
|
420
|
+
}
|
|
421
|
+
return String(left ?? "").localeCompare(String(right ?? ""), void 0, {
|
|
422
|
+
sensitivity: "base"
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
function collectMockText(value, depth = 0) {
|
|
426
|
+
if (typeof value === "string") {
|
|
427
|
+
return value;
|
|
428
|
+
}
|
|
429
|
+
if (value === null || typeof value !== "object" || depth > 8) {
|
|
430
|
+
return "";
|
|
431
|
+
}
|
|
432
|
+
const parts = [];
|
|
433
|
+
for (const item of Array.isArray(value) ? value : Object.values(value)) {
|
|
434
|
+
const text = collectMockText(item, depth + 1);
|
|
435
|
+
if (text.length > 0) {
|
|
436
|
+
parts.push(text);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return parts.join(" ");
|
|
440
|
+
}
|
|
441
|
+
function mockTokenize(text) {
|
|
442
|
+
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
|
|
443
|
+
}
|
|
444
|
+
function computeMockTextScore(document, clause) {
|
|
445
|
+
const terms = mockTokenize(clause.query);
|
|
446
|
+
if (terms.length === 0) {
|
|
447
|
+
return void 0;
|
|
448
|
+
}
|
|
449
|
+
const haystack = clause.fields ? clause.fields.map((field) => collectMockText(getFieldValue(document.data, field))).join(" ") : collectMockText(
|
|
450
|
+
Object.fromEntries(
|
|
451
|
+
Object.entries(document.data).filter(([key]) => !key.startsWith("_"))
|
|
452
|
+
)
|
|
453
|
+
);
|
|
454
|
+
const tokens = mockTokenize(haystack);
|
|
455
|
+
const prefix = clause.prefix ?? true;
|
|
456
|
+
const combineWith = clause.combineWith ?? "AND";
|
|
457
|
+
let matchedTerms = 0;
|
|
458
|
+
let score = 0;
|
|
459
|
+
for (const term of terms) {
|
|
460
|
+
const occurrences = tokens.filter(
|
|
461
|
+
(token) => prefix ? token.startsWith(term) : token === term
|
|
462
|
+
).length;
|
|
463
|
+
if (occurrences > 0) {
|
|
464
|
+
matchedTerms++;
|
|
465
|
+
score += occurrences;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const matches = combineWith === "AND" ? matchedTerms === terms.length : matchedTerms > 0;
|
|
469
|
+
return matches ? score : void 0;
|
|
470
|
+
}
|
|
471
|
+
function runMockDocumentQuery(storedDocuments, databaseId, query) {
|
|
472
|
+
const filter = typeof query?.filter === "string" ? parseMindooDBFormulaBooleanExpression(query.filter) : query?.filter;
|
|
473
|
+
const sortBy = query?.sortBy && query.sortBy.length > 0 ? query.sortBy : query?.text ? [{ special: "textScore", direction: "descending" }] : [];
|
|
474
|
+
const matches = [];
|
|
475
|
+
for (const document of storedDocuments.values()) {
|
|
476
|
+
if (document.isDeleted) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
let textScore;
|
|
480
|
+
if (query?.text) {
|
|
481
|
+
textScore = computeMockTextScore(document, query.text);
|
|
482
|
+
if (textScore === void 0) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const context = {
|
|
487
|
+
doc: document.data,
|
|
488
|
+
values: {},
|
|
489
|
+
origin: databaseId,
|
|
490
|
+
variables: {}
|
|
491
|
+
};
|
|
492
|
+
if (filter && !mockExpressionToBoolean(evaluateExpression(filter, context))) {
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const sortValues = sortBy.map((key) => {
|
|
496
|
+
if (key.special === "textScore") {
|
|
497
|
+
return textScore ?? 0;
|
|
498
|
+
}
|
|
499
|
+
return key.expression ? evaluateExpression(key.expression, context) : getFieldValue(document.data, key.field ?? "");
|
|
500
|
+
});
|
|
501
|
+
matches.push({ document, sortValues, textScore });
|
|
502
|
+
}
|
|
503
|
+
if (sortBy.length > 0) {
|
|
504
|
+
matches.sort((left, right) => {
|
|
505
|
+
for (let i = 0; i < sortBy.length; i++) {
|
|
506
|
+
const result = compareMockQueryValues(
|
|
507
|
+
left.sortValues[i],
|
|
508
|
+
right.sortValues[i]
|
|
509
|
+
);
|
|
510
|
+
if (result !== 0) {
|
|
511
|
+
return sortBy[i].direction === "descending" ? -result : result;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return left.document.id.localeCompare(right.document.id);
|
|
515
|
+
});
|
|
516
|
+
} else {
|
|
517
|
+
matches.sort((left, right) => left.document.id.localeCompare(right.document.id));
|
|
518
|
+
}
|
|
519
|
+
const offset = Math.max(0, query?.offset ?? 0);
|
|
520
|
+
const limit = Math.max(1, query?.limit ?? 200);
|
|
521
|
+
const rows = matches.slice(offset, offset + limit).map(({ document, textScore }) => ({
|
|
522
|
+
docId: document.id,
|
|
523
|
+
fields: query?.fields ? Object.fromEntries(
|
|
524
|
+
query.fields.map((field) => [
|
|
525
|
+
field,
|
|
526
|
+
getFieldValue(document.data, field)
|
|
527
|
+
])
|
|
528
|
+
) : structuredClone(document.data),
|
|
529
|
+
lastModified: document.updatedAt ? Date.parse(document.updatedAt) || 0 : 0,
|
|
530
|
+
...textScore === void 0 ? {} : { textScore }
|
|
531
|
+
}));
|
|
532
|
+
return { rows, total: matches.length, coverage: "full" };
|
|
533
|
+
}
|
|
317
534
|
function createDefaultViewNavigator() {
|
|
318
535
|
const definition = {
|
|
319
536
|
title: "Mock View",
|
|
@@ -344,6 +561,10 @@ function createDefaultViewNavigator() {
|
|
|
344
561
|
async refresh() {
|
|
345
562
|
return viewCursor;
|
|
346
563
|
},
|
|
564
|
+
onDidUpdate(_listener) {
|
|
565
|
+
return () => {
|
|
566
|
+
};
|
|
567
|
+
},
|
|
347
568
|
async getCurrentEntry() {
|
|
348
569
|
return null;
|
|
349
570
|
},
|
|
@@ -523,7 +744,56 @@ function createDatabaseHandle(definition) {
|
|
|
523
744
|
let changeCounter = 0;
|
|
524
745
|
const defaultViewFactory = async () => createDefaultViewNavigator();
|
|
525
746
|
const storedDocuments = /* @__PURE__ */ new Map();
|
|
747
|
+
const liveQuerySubscriptions = /* @__PURE__ */ new Set();
|
|
748
|
+
const notifyLiveQueries = () => {
|
|
749
|
+
for (const subscription of liveQuerySubscriptions) {
|
|
750
|
+
const result = runMockDocumentQuery(
|
|
751
|
+
storedDocuments,
|
|
752
|
+
definition.info.id,
|
|
753
|
+
subscription.query
|
|
754
|
+
);
|
|
755
|
+
const resultJson = JSON.stringify(result);
|
|
756
|
+
if (resultJson !== subscription.lastResultJson) {
|
|
757
|
+
subscription.lastResultJson = resultJson;
|
|
758
|
+
subscription.onResult(result);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
};
|
|
526
762
|
const defaultDocuments = {
|
|
763
|
+
async query(query) {
|
|
764
|
+
return runMockDocumentQuery(storedDocuments, definition.info.id, query);
|
|
765
|
+
},
|
|
766
|
+
async liveQuery(query, onResult) {
|
|
767
|
+
const initial = runMockDocumentQuery(
|
|
768
|
+
storedDocuments,
|
|
769
|
+
definition.info.id,
|
|
770
|
+
query
|
|
771
|
+
);
|
|
772
|
+
const subscription = {
|
|
773
|
+
query,
|
|
774
|
+
onResult,
|
|
775
|
+
lastResultJson: JSON.stringify(initial)
|
|
776
|
+
};
|
|
777
|
+
liveQuerySubscriptions.add(subscription);
|
|
778
|
+
onResult(initial);
|
|
779
|
+
return {
|
|
780
|
+
refresh: async () => {
|
|
781
|
+
if (!liveQuerySubscriptions.has(subscription)) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const result = runMockDocumentQuery(
|
|
785
|
+
storedDocuments,
|
|
786
|
+
definition.info.id,
|
|
787
|
+
query
|
|
788
|
+
);
|
|
789
|
+
subscription.lastResultJson = JSON.stringify(result);
|
|
790
|
+
onResult(result);
|
|
791
|
+
},
|
|
792
|
+
dispose: async () => {
|
|
793
|
+
liveQuerySubscriptions.delete(subscription);
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
},
|
|
527
797
|
async list(query) {
|
|
528
798
|
const status = query?.status ?? "existing";
|
|
529
799
|
const skip = Math.max(0, query?.skip ?? 0);
|
|
@@ -591,6 +861,53 @@ function createDatabaseHandle(definition) {
|
|
|
591
861
|
spans: structuredClone(readRichTextSpansAtPath(document.data, path))
|
|
592
862
|
};
|
|
593
863
|
},
|
|
864
|
+
async getAutomergeSnapshot(docId, _options) {
|
|
865
|
+
const document = storedDocuments.get(docId);
|
|
866
|
+
if (!document || document.isDeleted) {
|
|
867
|
+
throw new Error(`Document ${docId} was not found.`);
|
|
868
|
+
}
|
|
869
|
+
const automergeDoc = loadMockAutomergeDocument(document);
|
|
870
|
+
return {
|
|
871
|
+
binary: Automerge.save(automergeDoc),
|
|
872
|
+
heads: Automerge.getHeads(automergeDoc)
|
|
873
|
+
};
|
|
874
|
+
},
|
|
875
|
+
async applyAutomergeChanges(docId, patch) {
|
|
876
|
+
const document = storedDocuments.get(docId);
|
|
877
|
+
if (!document || document.isDeleted) {
|
|
878
|
+
throw new Error(`Document ${docId} was not found.`);
|
|
879
|
+
}
|
|
880
|
+
const currentDoc = loadMockAutomergeDocument(document);
|
|
881
|
+
const [mergedDoc] = Automerge.applyChanges(
|
|
882
|
+
currentDoc,
|
|
883
|
+
patch.changes.map((change) => new Uint8Array(change))
|
|
884
|
+
);
|
|
885
|
+
persistMockAutomergeDocument(document, mergedDoc);
|
|
886
|
+
document.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
887
|
+
const mergedHeads = document.heads ? [...document.heads] : [];
|
|
888
|
+
let changesSince;
|
|
889
|
+
if (patch.replicaHeads?.length) {
|
|
890
|
+
const missingChanges = Automerge.getChangesSince(
|
|
891
|
+
mergedDoc,
|
|
892
|
+
patch.replicaHeads
|
|
893
|
+
);
|
|
894
|
+
changesSince = {
|
|
895
|
+
sinceHeads: [...patch.replicaHeads],
|
|
896
|
+
changes: missingChanges.map((change) => cloneBytes(new Uint8Array(change)))
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
return {
|
|
900
|
+
document: {
|
|
901
|
+
id: document.id,
|
|
902
|
+
data: structuredClone(document.data),
|
|
903
|
+
heads: mergedHeads,
|
|
904
|
+
attachments: document.attachments ? structuredClone(document.attachments) : [],
|
|
905
|
+
updatedAt: document.updatedAt
|
|
906
|
+
},
|
|
907
|
+
heads: mergedHeads,
|
|
908
|
+
changesSince
|
|
909
|
+
};
|
|
910
|
+
},
|
|
594
911
|
async create(input) {
|
|
595
912
|
const callerId = typeof input.id === "string" && input.id.length > 0 ? input.id : null;
|
|
596
913
|
if (callerId) {
|
|
@@ -600,6 +917,7 @@ function createDatabaseHandle(definition) {
|
|
|
600
917
|
existing.isDeleted = false;
|
|
601
918
|
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
602
919
|
existing.heads = [`mock-head-${++changeCounter}`];
|
|
920
|
+
notifyLiveQueries();
|
|
603
921
|
}
|
|
604
922
|
return {
|
|
605
923
|
id: existing.id,
|
|
@@ -631,10 +949,19 @@ function createDatabaseHandle(definition) {
|
|
|
631
949
|
attachments: [],
|
|
632
950
|
isDeleted: false
|
|
633
951
|
});
|
|
952
|
+
notifyLiveQueries();
|
|
634
953
|
return {
|
|
635
954
|
...created
|
|
636
955
|
};
|
|
637
956
|
},
|
|
957
|
+
async createMany(inputs) {
|
|
958
|
+
const ids = [];
|
|
959
|
+
for (const input of inputs) {
|
|
960
|
+
const created = await defaultDocuments.create(input);
|
|
961
|
+
ids.push(created.id);
|
|
962
|
+
}
|
|
963
|
+
return { ids };
|
|
964
|
+
},
|
|
638
965
|
async update(docId, patch) {
|
|
639
966
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
640
967
|
const existing = storedDocuments.get(docId);
|
|
@@ -649,8 +976,10 @@ function createDatabaseHandle(definition) {
|
|
|
649
976
|
...updated,
|
|
650
977
|
data: structuredClone(updated.data),
|
|
651
978
|
attachments: updated.attachments ? structuredClone(updated.attachments) : [],
|
|
652
|
-
isDeleted: false
|
|
979
|
+
isDeleted: false,
|
|
980
|
+
automergeBinary: void 0
|
|
653
981
|
});
|
|
982
|
+
notifyLiveQueries();
|
|
654
983
|
return updated;
|
|
655
984
|
},
|
|
656
985
|
async delete(_docId) {
|
|
@@ -661,6 +990,13 @@ function createDatabaseHandle(definition) {
|
|
|
661
990
|
isDeleted: true,
|
|
662
991
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
663
992
|
});
|
|
993
|
+
notifyLiveQueries();
|
|
994
|
+
}
|
|
995
|
+
return { ok: true };
|
|
996
|
+
},
|
|
997
|
+
async deleteMany(docIds) {
|
|
998
|
+
for (const docId of docIds) {
|
|
999
|
+
await defaultDocuments.delete(docId);
|
|
664
1000
|
}
|
|
665
1001
|
return { ok: true };
|
|
666
1002
|
},
|
|
@@ -673,9 +1009,25 @@ function createDatabaseHandle(definition) {
|
|
|
673
1009
|
heads: [`mock-head-${++changeCounter}`],
|
|
674
1010
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
675
1011
|
});
|
|
1012
|
+
notifyLiveQueries();
|
|
676
1013
|
}
|
|
677
1014
|
return { ok: true };
|
|
678
1015
|
},
|
|
1016
|
+
async canCreate() {
|
|
1017
|
+
return { allowed: true, reason: "allowed (test host)", tier: "tier1" };
|
|
1018
|
+
},
|
|
1019
|
+
async canChange(_docId) {
|
|
1020
|
+
return { allowed: true, reason: "allowed (test host)", tier: "tier1" };
|
|
1021
|
+
},
|
|
1022
|
+
async canDelete(_docId) {
|
|
1023
|
+
return { allowed: true, reason: "allowed (test host)", tier: "tier1" };
|
|
1024
|
+
},
|
|
1025
|
+
async canUndelete(_docId) {
|
|
1026
|
+
return { allowed: true, reason: "allowed (test host)", tier: "tier1" };
|
|
1027
|
+
},
|
|
1028
|
+
async getDefaultCreateKeyId() {
|
|
1029
|
+
return "default";
|
|
1030
|
+
},
|
|
679
1031
|
async listHistory(_docId) {
|
|
680
1032
|
return [];
|
|
681
1033
|
},
|
|
@@ -743,6 +1095,8 @@ function createDatabaseHandle(definition) {
|
|
|
743
1095
|
}
|
|
744
1096
|
};
|
|
745
1097
|
const methods = definition.methods ?? {};
|
|
1098
|
+
let fulltextSetup = definition.fulltextSetup ?? null;
|
|
1099
|
+
let extractionSetup = definition.extractionSetup ?? null;
|
|
746
1100
|
return {
|
|
747
1101
|
async info() {
|
|
748
1102
|
return {
|
|
@@ -757,6 +1111,18 @@ function createDatabaseHandle(definition) {
|
|
|
757
1111
|
attachments: {
|
|
758
1112
|
...defaultAttachments,
|
|
759
1113
|
...methods.attachments
|
|
1114
|
+
},
|
|
1115
|
+
async getFulltextSetup() {
|
|
1116
|
+
return fulltextSetup === null ? null : { ...fulltextSetup };
|
|
1117
|
+
},
|
|
1118
|
+
async setFulltextSetup(config) {
|
|
1119
|
+
fulltextSetup = config === null ? null : { ...config };
|
|
1120
|
+
},
|
|
1121
|
+
async getExtractionSetup() {
|
|
1122
|
+
return extractionSetup === null ? null : { ...extractionSetup };
|
|
1123
|
+
},
|
|
1124
|
+
async setExtractionSetup(config) {
|
|
1125
|
+
extractionSetup = config === null ? null : { ...config };
|
|
760
1126
|
}
|
|
761
1127
|
};
|
|
762
1128
|
}
|
|
@@ -765,6 +1131,7 @@ function createMockSessionState(options = {}) {
|
|
|
765
1131
|
const themeListeners = /* @__PURE__ */ new Set();
|
|
766
1132
|
const viewportListeners = /* @__PURE__ */ new Set();
|
|
767
1133
|
const uiPreferencesListeners = /* @__PURE__ */ new Set();
|
|
1134
|
+
const localeListeners = /* @__PURE__ */ new Set();
|
|
768
1135
|
const databaseHandles = /* @__PURE__ */ new Map();
|
|
769
1136
|
const databaseViewApis = /* @__PURE__ */ new Map();
|
|
770
1137
|
const sessionViews = /* @__PURE__ */ new Map();
|
|
@@ -818,6 +1185,9 @@ function createMockSessionState(options = {}) {
|
|
|
818
1185
|
async getLaunchContext() {
|
|
819
1186
|
return mergeLaunchContext(launchContext);
|
|
820
1187
|
},
|
|
1188
|
+
async getLicensedProducts() {
|
|
1189
|
+
return [...launchContext.licensedProducts ?? []];
|
|
1190
|
+
},
|
|
821
1191
|
async listDatabases() {
|
|
822
1192
|
return databaseInfos.map((entry) => ({
|
|
823
1193
|
...entry,
|
|
@@ -895,6 +1265,12 @@ function createMockSessionState(options = {}) {
|
|
|
895
1265
|
uiPreferencesListeners.delete(listener);
|
|
896
1266
|
};
|
|
897
1267
|
},
|
|
1268
|
+
onLocaleChange(listener) {
|
|
1269
|
+
localeListeners.add(listener);
|
|
1270
|
+
return () => {
|
|
1271
|
+
localeListeners.delete(listener);
|
|
1272
|
+
};
|
|
1273
|
+
},
|
|
898
1274
|
async disconnect() {
|
|
899
1275
|
await options.onDisconnect?.();
|
|
900
1276
|
}
|
|
@@ -926,6 +1302,9 @@ function createMockSessionState(options = {}) {
|
|
|
926
1302
|
}
|
|
927
1303
|
return database;
|
|
928
1304
|
},
|
|
1305
|
+
getLicensedProducts() {
|
|
1306
|
+
return [...launchContext.licensedProducts ?? []];
|
|
1307
|
+
},
|
|
929
1308
|
createViewNavigator: session.createViewNavigator,
|
|
930
1309
|
openViewNavigator: session.openViewNavigator,
|
|
931
1310
|
bridge,
|
|
@@ -945,6 +1324,10 @@ function createMockSessionState(options = {}) {
|
|
|
945
1324
|
uiPreferencesListeners.forEach(
|
|
946
1325
|
(listener) => listener({ ...launchContext.uiPreferences })
|
|
947
1326
|
);
|
|
1327
|
+
},
|
|
1328
|
+
emitLocaleChange(locale) {
|
|
1329
|
+
launchContext = mergeLaunchContext(launchContext, { locale });
|
|
1330
|
+
localeListeners.forEach((listener) => listener(launchContext.locale));
|
|
948
1331
|
}
|
|
949
1332
|
};
|
|
950
1333
|
}
|
|
@@ -955,11 +1338,13 @@ function createMockMindooDBAppSession(options = {}) {
|
|
|
955
1338
|
session: state.session,
|
|
956
1339
|
getLaunchContext: state.getLaunchContext,
|
|
957
1340
|
setLaunchContext: state.setLaunchContext,
|
|
1341
|
+
getLicensedProducts: state.getLicensedProducts,
|
|
958
1342
|
listDatabases: state.listDatabaseInfos,
|
|
959
1343
|
setDatabases: state.setDatabases,
|
|
960
1344
|
emitThemeChange: state.emitThemeChange,
|
|
961
1345
|
emitViewportChange: state.emitViewportChange,
|
|
962
|
-
emitUiPreferencesChange: state.emitUiPreferencesChange
|
|
1346
|
+
emitUiPreferencesChange: state.emitUiPreferencesChange,
|
|
1347
|
+
emitLocaleChange: state.emitLocaleChange
|
|
963
1348
|
};
|
|
964
1349
|
}
|
|
965
1350
|
function createMockMindooDBAppBridge(options = {}) {
|
|
@@ -975,8 +1360,10 @@ function createFakeBridgeHost(options = {}) {
|
|
|
975
1360
|
const viewSessions = /* @__PURE__ */ new Map();
|
|
976
1361
|
const readStreams = /* @__PURE__ */ new Map();
|
|
977
1362
|
const writeStreams = /* @__PURE__ */ new Map();
|
|
1363
|
+
const liveQueries = /* @__PURE__ */ new Map();
|
|
978
1364
|
let viewCounter = 0;
|
|
979
1365
|
let streamCounter = 0;
|
|
1366
|
+
let liveQueryCounter = 0;
|
|
980
1367
|
let previousWindowDescriptor = Object.getOwnPropertyDescriptor(
|
|
981
1368
|
globalThis,
|
|
982
1369
|
"window"
|
|
@@ -1121,6 +1508,8 @@ function createFakeBridgeHost(options = {}) {
|
|
|
1121
1508
|
switch (request.method) {
|
|
1122
1509
|
case "session.getLaunchContext":
|
|
1123
1510
|
return state.getLaunchContext();
|
|
1511
|
+
case "session.getLicensedProducts":
|
|
1512
|
+
return state.getLicensedProducts();
|
|
1124
1513
|
case "session.listDatabases":
|
|
1125
1514
|
return state.listDatabaseInfos();
|
|
1126
1515
|
case "session.openDatabase":
|
|
@@ -1162,6 +1551,62 @@ function createFakeBridgeHost(options = {}) {
|
|
|
1162
1551
|
);
|
|
1163
1552
|
case "documents.getHeadCursor":
|
|
1164
1553
|
return await state.getDatabase(String(params.databaseId)).documents.getHeadCursor();
|
|
1554
|
+
case "database.getFulltextSetup":
|
|
1555
|
+
return {
|
|
1556
|
+
config: await state.getDatabase(String(params.databaseId)).getFulltextSetup()
|
|
1557
|
+
};
|
|
1558
|
+
case "database.setFulltextSetup":
|
|
1559
|
+
await state.getDatabase(String(params.databaseId)).setFulltextSetup(
|
|
1560
|
+
params.config ?? null
|
|
1561
|
+
);
|
|
1562
|
+
return { ok: true };
|
|
1563
|
+
case "database.getExtractionSetup":
|
|
1564
|
+
return {
|
|
1565
|
+
config: await state.getDatabase(String(params.databaseId)).getExtractionSetup()
|
|
1566
|
+
};
|
|
1567
|
+
case "database.setExtractionSetup":
|
|
1568
|
+
await state.getDatabase(String(params.databaseId)).setExtractionSetup(
|
|
1569
|
+
params.config ?? null
|
|
1570
|
+
);
|
|
1571
|
+
return { ok: true };
|
|
1572
|
+
case "documents.query":
|
|
1573
|
+
return await state.getDatabase(String(params.databaseId)).documents.query(
|
|
1574
|
+
params.query
|
|
1575
|
+
);
|
|
1576
|
+
case "documents.liveQuery.subscribe": {
|
|
1577
|
+
const subscriptionId = `live-query-${liveQueryCounter += 1}`;
|
|
1578
|
+
let initialResult = null;
|
|
1579
|
+
let initialDelivered = false;
|
|
1580
|
+
const handle = await state.getDatabase(String(params.databaseId)).documents.liveQuery(
|
|
1581
|
+
params.query,
|
|
1582
|
+
(result) => {
|
|
1583
|
+
if (!initialDelivered) {
|
|
1584
|
+
initialDelivered = true;
|
|
1585
|
+
initialResult = result;
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
const payload = {
|
|
1589
|
+
protocol: PROTOCOL,
|
|
1590
|
+
kind: "query-result",
|
|
1591
|
+
subscriptionId,
|
|
1592
|
+
result
|
|
1593
|
+
};
|
|
1594
|
+
connectedPorts.forEach((port) => port.postMessage(payload));
|
|
1595
|
+
}
|
|
1596
|
+
);
|
|
1597
|
+
liveQueries.set(subscriptionId, handle);
|
|
1598
|
+
return { subscriptionId, result: initialResult };
|
|
1599
|
+
}
|
|
1600
|
+
case "documents.liveQuery.refresh": {
|
|
1601
|
+
await liveQueries.get(String(params.subscriptionId))?.refresh();
|
|
1602
|
+
return { ok: true };
|
|
1603
|
+
}
|
|
1604
|
+
case "documents.liveQuery.unsubscribe": {
|
|
1605
|
+
const key = String(params.subscriptionId);
|
|
1606
|
+
await liveQueries.get(key)?.dispose();
|
|
1607
|
+
liveQueries.delete(key);
|
|
1608
|
+
return { ok: true };
|
|
1609
|
+
}
|
|
1165
1610
|
case "documents.get":
|
|
1166
1611
|
return await state.getDatabase(String(params.databaseId)).documents.get(String(params.docId));
|
|
1167
1612
|
case "documents.richText.get":
|
|
@@ -1169,6 +1614,16 @@ function createFakeBridgeHost(options = {}) {
|
|
|
1169
1614
|
String(params.docId),
|
|
1170
1615
|
params.path
|
|
1171
1616
|
);
|
|
1617
|
+
case "documents.automerge.getSnapshot":
|
|
1618
|
+
return await state.getDatabase(String(params.databaseId)).documents.getAutomergeSnapshot(
|
|
1619
|
+
String(params.docId),
|
|
1620
|
+
{ revisionId: params.revisionId }
|
|
1621
|
+
);
|
|
1622
|
+
case "documents.automerge.applyChanges":
|
|
1623
|
+
return await state.getDatabase(String(params.databaseId)).documents.applyAutomergeChanges(
|
|
1624
|
+
String(params.docId),
|
|
1625
|
+
params.patch
|
|
1626
|
+
);
|
|
1172
1627
|
case "documents.create":
|
|
1173
1628
|
return await state.getDatabase(String(params.databaseId)).documents.create(
|
|
1174
1629
|
params.input
|
|
@@ -1182,6 +1637,18 @@ function createFakeBridgeHost(options = {}) {
|
|
|
1182
1637
|
return await state.getDatabase(String(params.databaseId)).documents.delete(String(params.docId));
|
|
1183
1638
|
case "documents.undelete":
|
|
1184
1639
|
return await state.getDatabase(String(params.databaseId)).documents.undelete(String(params.docId));
|
|
1640
|
+
case "documents.canCreate":
|
|
1641
|
+
return await state.getDatabase(String(params.databaseId)).documents.canCreate(
|
|
1642
|
+
params.input
|
|
1643
|
+
);
|
|
1644
|
+
case "documents.canChange":
|
|
1645
|
+
return await state.getDatabase(String(params.databaseId)).documents.canChange(String(params.docId));
|
|
1646
|
+
case "documents.canDelete":
|
|
1647
|
+
return await state.getDatabase(String(params.databaseId)).documents.canDelete(String(params.docId));
|
|
1648
|
+
case "documents.canUndelete":
|
|
1649
|
+
return await state.getDatabase(String(params.databaseId)).documents.canUndelete(String(params.docId));
|
|
1650
|
+
case "documents.getDefaultCreateKeyId":
|
|
1651
|
+
return await state.getDatabase(String(params.databaseId)).documents.getDefaultCreateKeyId();
|
|
1185
1652
|
case "documents.history.list":
|
|
1186
1653
|
return await state.getDatabase(String(params.databaseId)).documents.listHistory(String(params.docId));
|
|
1187
1654
|
case "documents.history.getAtTimestamp":
|
|
@@ -1513,6 +1980,10 @@ function createFakeBridgeHost(options = {}) {
|
|
|
1513
1980
|
viewSessions.clear();
|
|
1514
1981
|
readStreams.clear();
|
|
1515
1982
|
writeStreams.clear();
|
|
1983
|
+
liveQueries.forEach((handle) => {
|
|
1984
|
+
void handle.dispose();
|
|
1985
|
+
});
|
|
1986
|
+
liveQueries.clear();
|
|
1516
1987
|
},
|
|
1517
1988
|
emitThemeChange(theme) {
|
|
1518
1989
|
state.emitThemeChange(theme);
|
|
@@ -1541,6 +2012,33 @@ function createFakeBridgeHost(options = {}) {
|
|
|
1541
2012
|
};
|
|
1542
2013
|
connectedPorts.forEach((port) => port.postMessage(payload));
|
|
1543
2014
|
},
|
|
2015
|
+
emitLocaleChange(locale) {
|
|
2016
|
+
state.emitLocaleChange(locale);
|
|
2017
|
+
const payload = {
|
|
2018
|
+
protocol: PROTOCOL,
|
|
2019
|
+
kind: "locale-changed",
|
|
2020
|
+
locale
|
|
2021
|
+
};
|
|
2022
|
+
connectedPorts.forEach((port) => port.postMessage(payload));
|
|
2023
|
+
},
|
|
2024
|
+
emitQueryResult(subscriptionId, result) {
|
|
2025
|
+
const payload = {
|
|
2026
|
+
protocol: PROTOCOL,
|
|
2027
|
+
kind: "query-result",
|
|
2028
|
+
subscriptionId,
|
|
2029
|
+
result
|
|
2030
|
+
};
|
|
2031
|
+
connectedPorts.forEach((port) => port.postMessage(payload));
|
|
2032
|
+
},
|
|
2033
|
+
emitViewChanged(navigatorId, stats) {
|
|
2034
|
+
const payload = {
|
|
2035
|
+
protocol: PROTOCOL,
|
|
2036
|
+
kind: "view-changed",
|
|
2037
|
+
navigatorId,
|
|
2038
|
+
stats
|
|
2039
|
+
};
|
|
2040
|
+
connectedPorts.forEach((port) => port.postMessage(payload));
|
|
2041
|
+
},
|
|
1544
2042
|
postPortMessage(message, transfer = []) {
|
|
1545
2043
|
connectedPorts.forEach((port) => port.postMessage(message, transfer));
|
|
1546
2044
|
},
|