@mongoosejs/studio 0.5.7 → 0.5.9
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/backend/integrations/streamLLM.js +1 -1
- package/eslint.config.js +3 -1
- package/frontend/public/app.js +1454 -667
- package/frontend/public/tw.css +113 -0
- package/frontend/src/_util/calendar.js +155 -0
- package/frontend/src/_util/document-search-autocomplete.js +105 -23
- package/frontend/src/custom-date-picker/custom-date-picker.html +183 -0
- package/frontend/src/custom-date-picker/custom-date-picker.js +407 -0
- package/frontend/src/document/document.css +62 -0
- package/frontend/src/document/document.html +113 -2
- package/frontend/src/document/document.js +171 -9
- package/frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.html +1 -0
- package/frontend/src/document-details/date-view-mode-picker/date-view-mode-picker.js +1 -1
- package/frontend/src/document-details/document-details.css +1 -63
- package/frontend/src/document-details/document-details.html +0 -122
- package/frontend/src/document-details/document-details.js +2 -138
- package/frontend/src/document-details/document-property/document-property.html +3 -2
- package/frontend/src/edit-date/edit-date.html +8 -6
- package/frontend/src/edit-date/edit-date.js +0 -20
- package/frontend/src/index.js +1 -1
- package/frontend/src/models/document-search/document-search.html +78 -22
- package/frontend/src/models/document-search/document-search.js +64 -2
- package/frontend/src/models/models.js +46 -10
- package/frontend/src/routes.js +2 -1
- package/package.json +1 -1
package/frontend/public/app.js
CHANGED
|
@@ -60,6 +60,172 @@ module.exports = {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
/***/ },
|
|
64
|
+
|
|
65
|
+
/***/ "./frontend/src/_util/calendar.js"
|
|
66
|
+
/*!****************************************!*\
|
|
67
|
+
!*** ./frontend/src/_util/calendar.js ***!
|
|
68
|
+
\****************************************/
|
|
69
|
+
(module) {
|
|
70
|
+
|
|
71
|
+
"use strict";
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
function pad2(n) {
|
|
75
|
+
return String(n).padStart(2, '0');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function pad3(n) {
|
|
79
|
+
return String(n).padStart(3, '0');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {number} value
|
|
84
|
+
* @param {number} min
|
|
85
|
+
* @param {number} max
|
|
86
|
+
*/
|
|
87
|
+
function clampInt(value, min, max) {
|
|
88
|
+
const n = Math.floor(Number(value));
|
|
89
|
+
if (Number.isNaN(n)) {
|
|
90
|
+
return min;
|
|
91
|
+
}
|
|
92
|
+
return Math.min(max, Math.max(min, n));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {Date} date
|
|
97
|
+
* @returns {string} YYYY-MM-DDTHH:mm:ss.SSS
|
|
98
|
+
*/
|
|
99
|
+
function dateToDatetimeLocal(date) {
|
|
100
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
return [
|
|
104
|
+
date.getFullYear(),
|
|
105
|
+
'-',
|
|
106
|
+
pad2(date.getMonth() + 1),
|
|
107
|
+
'-',
|
|
108
|
+
pad2(date.getDate()),
|
|
109
|
+
'T',
|
|
110
|
+
pad2(date.getHours()),
|
|
111
|
+
':',
|
|
112
|
+
pad2(date.getMinutes()),
|
|
113
|
+
':',
|
|
114
|
+
pad2(date.getSeconds()),
|
|
115
|
+
'.',
|
|
116
|
+
pad3(date.getMilliseconds())
|
|
117
|
+
].join('');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {Date} date
|
|
122
|
+
* @returns {string} YYYY-MM-DD
|
|
123
|
+
*/
|
|
124
|
+
function dateToDateOnly(date) {
|
|
125
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
126
|
+
return '';
|
|
127
|
+
}
|
|
128
|
+
return [
|
|
129
|
+
date.getFullYear(),
|
|
130
|
+
'-',
|
|
131
|
+
pad2(date.getMonth() + 1),
|
|
132
|
+
'-',
|
|
133
|
+
pad2(date.getDate())
|
|
134
|
+
].join('');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {unknown} value
|
|
139
|
+
* @returns {Date|null}
|
|
140
|
+
*/
|
|
141
|
+
function parseValueToDate(value) {
|
|
142
|
+
if (value == null || value === '') {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const date = new Date(value);
|
|
146
|
+
if (Number.isNaN(date.getTime())) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return date;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @param {string} localValue - YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss.SSS
|
|
154
|
+
* @returns {Date|null}
|
|
155
|
+
*/
|
|
156
|
+
function localStringToDate(localValue) {
|
|
157
|
+
if (!localValue) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const date = new Date(localValue);
|
|
161
|
+
if (Number.isNaN(date.getTime())) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return date;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isSameDay(a, b) {
|
|
168
|
+
if (!a || !b) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return (
|
|
172
|
+
a.getFullYear() === b.getFullYear()
|
|
173
|
+
&& a.getMonth() === b.getMonth()
|
|
174
|
+
&& a.getDate() === b.getDate()
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isSameMonth(date, year, month) {
|
|
179
|
+
return date.getFullYear() === year && date.getMonth() === month;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Build a 6-row calendar grid for the given month (local timezone).
|
|
184
|
+
* @param {number} year
|
|
185
|
+
* @param {number} month - 0-indexed
|
|
186
|
+
* @returns {Array<{ key: string, label: number, date: Date, inMonth: boolean }>}
|
|
187
|
+
*/
|
|
188
|
+
function buildCalendarDays(year, month) {
|
|
189
|
+
const firstOfMonth = new Date(year, month, 1);
|
|
190
|
+
const startOffset = firstOfMonth.getDay();
|
|
191
|
+
const gridStart = new Date(year, month, 1 - startOffset);
|
|
192
|
+
|
|
193
|
+
const days = [];
|
|
194
|
+
for (let i = 0; i < 42; i++) {
|
|
195
|
+
const date = new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i);
|
|
196
|
+
days.push({
|
|
197
|
+
key: `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`,
|
|
198
|
+
label: date.getDate(),
|
|
199
|
+
date,
|
|
200
|
+
inMonth: date.getMonth() === month
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return days;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const WEEKDAY_LABELS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
207
|
+
|
|
208
|
+
const MONTH_LABELS = [
|
|
209
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
210
|
+
'July', 'August', 'September', 'October', 'November', 'December'
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
module.exports = {
|
|
214
|
+
pad2,
|
|
215
|
+
pad3,
|
|
216
|
+
clampInt,
|
|
217
|
+
dateToDatetimeLocal,
|
|
218
|
+
dateToDateOnly,
|
|
219
|
+
parseValueToDate,
|
|
220
|
+
localStringToDate,
|
|
221
|
+
isSameDay,
|
|
222
|
+
isSameMonth,
|
|
223
|
+
buildCalendarDays,
|
|
224
|
+
WEEKDAY_LABELS,
|
|
225
|
+
MONTH_LABELS
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
|
|
63
229
|
/***/ },
|
|
64
230
|
|
|
65
231
|
/***/ "./frontend/src/_util/dateRange.js"
|
|
@@ -228,6 +394,8 @@ module.exports = deepEqual;
|
|
|
228
394
|
"use strict";
|
|
229
395
|
|
|
230
396
|
|
|
397
|
+
const { dateToDatetimeLocal } = __webpack_require__(/*! ./calendar */ "./frontend/src/_util/calendar.js");
|
|
398
|
+
|
|
231
399
|
const { Trie } = __webpack_require__(/*! ../models/trie */ "./frontend/src/models/trie.js");
|
|
232
400
|
|
|
233
401
|
const QUERY_SELECTORS = [
|
|
@@ -277,7 +445,7 @@ function buildAutocompleteTrie(schemaPaths) {
|
|
|
277
445
|
trie.bulkInsert(QUERY_SELECTORS, 5, 'operator');
|
|
278
446
|
trie.bulkInsert(QUERY_SELECTORS, 5, 'fieldName');
|
|
279
447
|
trie.bulkInsert(VALUE_HELPERS, 5, 'value');
|
|
280
|
-
|
|
448
|
+
|
|
281
449
|
if (Array.isArray(schemaPaths) && schemaPaths.length > 0) {
|
|
282
450
|
const paths = schemaPaths
|
|
283
451
|
.map(path => path?.path)
|
|
@@ -289,13 +457,13 @@ function buildAutocompleteTrie(schemaPaths) {
|
|
|
289
457
|
}
|
|
290
458
|
trie.bulkInsert(paths, 10, 'fieldName');
|
|
291
459
|
}
|
|
292
|
-
|
|
460
|
+
|
|
293
461
|
return trie;
|
|
294
462
|
}
|
|
295
463
|
|
|
296
464
|
function getAutocompleteContext(searchText, cursorPos) {
|
|
297
465
|
const before = searchText.slice(0, cursorPos);
|
|
298
|
-
|
|
466
|
+
|
|
299
467
|
// Check if we're in a field name context (after { or ,)
|
|
300
468
|
// This takes precedence over value context to handle cases like { _id: { $gt
|
|
301
469
|
const fieldMatch = before.match(/(?:\{|,)\s*([^:\s]*)$/);
|
|
@@ -307,7 +475,7 @@ function getAutocompleteContext(searchText, cursorPos) {
|
|
|
307
475
|
startPos: cursorPos - token.length
|
|
308
476
|
};
|
|
309
477
|
}
|
|
310
|
-
|
|
478
|
+
|
|
311
479
|
// Check if we're in a value context (after a colon)
|
|
312
480
|
// Match the last colon followed by optional whitespace and capture everything after
|
|
313
481
|
const valueMatch = before.match(/:\s*(\{?\s*)([^\s,\}\]:]*)$/);
|
|
@@ -319,19 +487,19 @@ function getAutocompleteContext(searchText, cursorPos) {
|
|
|
319
487
|
startPos: cursorPos - token.length
|
|
320
488
|
};
|
|
321
489
|
}
|
|
322
|
-
|
|
490
|
+
|
|
323
491
|
return null;
|
|
324
492
|
}
|
|
325
493
|
|
|
326
494
|
function getAutocompleteSuggestions(trie, searchText, cursorPos, schemaPaths) {
|
|
327
495
|
const context = getAutocompleteContext(searchText, cursorPos);
|
|
328
|
-
|
|
496
|
+
|
|
329
497
|
if (!context) {
|
|
330
498
|
return [];
|
|
331
499
|
}
|
|
332
|
-
|
|
500
|
+
|
|
333
501
|
const { token, role } = context;
|
|
334
|
-
|
|
502
|
+
|
|
335
503
|
// Extract the actual term without quotes
|
|
336
504
|
const leadingQuoteMatch = token.match(/^["']/);
|
|
337
505
|
const trailingQuoteMatch = token.length > 1 && /["']$/.test(token)
|
|
@@ -341,14 +509,14 @@ function getAutocompleteSuggestions(trie, searchText, cursorPos, schemaPaths) {
|
|
|
341
509
|
.replace(/^["']/, '')
|
|
342
510
|
.replace(trailingQuoteMatch ? new RegExp(`[${trailingQuoteMatch}]$`) : '', '')
|
|
343
511
|
.trim();
|
|
344
|
-
|
|
512
|
+
|
|
345
513
|
if (!term) {
|
|
346
514
|
return [];
|
|
347
515
|
}
|
|
348
|
-
|
|
516
|
+
|
|
349
517
|
const primarySuggestions = trie.getSuggestions(term, 10, role);
|
|
350
518
|
const suggestionsSet = new Set(primarySuggestions);
|
|
351
|
-
|
|
519
|
+
|
|
352
520
|
// Add schema path suggestions for field names
|
|
353
521
|
if (role === 'fieldName' && Array.isArray(schemaPaths) && schemaPaths.length > 0) {
|
|
354
522
|
for (const schemaPath of schemaPaths) {
|
|
@@ -365,9 +533,9 @@ function getAutocompleteSuggestions(trie, searchText, cursorPos, schemaPaths) {
|
|
|
365
533
|
}
|
|
366
534
|
}
|
|
367
535
|
}
|
|
368
|
-
|
|
536
|
+
|
|
369
537
|
let suggestions = Array.from(suggestionsSet);
|
|
370
|
-
|
|
538
|
+
|
|
371
539
|
// Preserve quotes if present
|
|
372
540
|
if (leadingQuoteMatch) {
|
|
373
541
|
const leadingQuote = leadingQuoteMatch[0];
|
|
@@ -378,14 +546,14 @@ function getAutocompleteSuggestions(trie, searchText, cursorPos, schemaPaths) {
|
|
|
378
546
|
suggestion.endsWith(trailingQuoteMatch) ? suggestion : `${suggestion}${trailingQuoteMatch}`
|
|
379
547
|
);
|
|
380
548
|
}
|
|
381
|
-
|
|
549
|
+
|
|
382
550
|
return suggestions;
|
|
383
551
|
}
|
|
384
552
|
|
|
385
553
|
function applySuggestion(searchText, cursorPos, suggestion) {
|
|
386
554
|
const before = searchText.slice(0, cursorPos);
|
|
387
555
|
const after = searchText.slice(cursorPos);
|
|
388
|
-
|
|
556
|
+
|
|
389
557
|
// Check if we're in a value context
|
|
390
558
|
const valueMatch = before.match(/:\s*(\{?\s*)([^\s,\}\]:]*)$/);
|
|
391
559
|
if (valueMatch) {
|
|
@@ -393,30 +561,30 @@ function applySuggestion(searchText, cursorPos, suggestion) {
|
|
|
393
561
|
const start = cursorPos - token.length;
|
|
394
562
|
let replacement = suggestion;
|
|
395
563
|
let cursorOffset = replacement.length;
|
|
396
|
-
|
|
564
|
+
|
|
397
565
|
// Add parentheses for function helpers and position cursor inside
|
|
398
566
|
if (FUNCTION_HELPERS.has(suggestion)) {
|
|
399
567
|
replacement = `${suggestion}()`;
|
|
400
568
|
cursorOffset = suggestion.length + 1; // Position cursor between ()
|
|
401
569
|
}
|
|
402
|
-
|
|
570
|
+
|
|
403
571
|
return {
|
|
404
572
|
text: searchText.slice(0, start) + replacement + after,
|
|
405
573
|
newCursorPos: start + cursorOffset
|
|
406
574
|
};
|
|
407
575
|
}
|
|
408
|
-
|
|
576
|
+
|
|
409
577
|
// Check if we're in a field name context
|
|
410
578
|
const fieldMatch = before.match(/(?:\{|,)\s*([^:\s]*)$/);
|
|
411
579
|
if (fieldMatch) {
|
|
412
580
|
const token = fieldMatch[1];
|
|
413
581
|
const start = cursorPos - token.length;
|
|
414
582
|
let replacement = suggestion;
|
|
415
|
-
|
|
583
|
+
|
|
416
584
|
const leadingQuote = token.startsWith('"') || token.startsWith('\'') ? token[0] : '';
|
|
417
585
|
const trailingQuote = token.length > 1 && (token.endsWith('"') || token.endsWith('\'')) ? token[token.length - 1] : '';
|
|
418
586
|
const colonNeeded = !/^\s*:/.test(after);
|
|
419
|
-
|
|
587
|
+
|
|
420
588
|
// If suggestion already has quotes, use it as-is
|
|
421
589
|
const suggestionHasQuotes = (suggestion.startsWith('"') || suggestion.startsWith('\'')) &&
|
|
422
590
|
(suggestion.endsWith('"') || suggestion.endsWith('\''));
|
|
@@ -430,27 +598,107 @@ function applySuggestion(searchText, cursorPos, suggestion) {
|
|
|
430
598
|
replacement = `${replacement}${trailingQuote}`;
|
|
431
599
|
}
|
|
432
600
|
}
|
|
433
|
-
|
|
601
|
+
|
|
434
602
|
// Only insert : if we know the user isn't entering in a nested path
|
|
435
603
|
// If suggestion has full quotes or user typed both quotes, add colon
|
|
436
604
|
if (colonNeeded && (suggestionHasQuotes || !leadingQuote || trailingQuote)) {
|
|
437
605
|
replacement = `${replacement}:`;
|
|
438
606
|
}
|
|
439
|
-
|
|
607
|
+
|
|
440
608
|
return {
|
|
441
609
|
text: searchText.slice(0, start) + replacement + after,
|
|
442
610
|
newCursorPos: start + replacement.length
|
|
443
611
|
};
|
|
444
612
|
}
|
|
445
|
-
|
|
613
|
+
|
|
446
614
|
return null;
|
|
447
615
|
}
|
|
448
616
|
|
|
617
|
+
/**
|
|
618
|
+
* When the cursor is inside a Date(…) or new Date(…) argument, returns the
|
|
619
|
+
* slice indices of that argument so a picker can replace it with a quoted ISO string.
|
|
620
|
+
*/
|
|
621
|
+
function getDatePickerInsertionRange(searchText, cursorPos) {
|
|
622
|
+
const before = searchText.slice(0, cursorPos);
|
|
623
|
+
const re = /((?:new\s+)?Date\s*\(\s*)([^)]*)$/i;
|
|
624
|
+
const m = before.match(re);
|
|
625
|
+
if (!m) {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
const innerStart = m.index + m[1].length;
|
|
629
|
+
const after = searchText.slice(cursorPos);
|
|
630
|
+
let closeIdx = -1;
|
|
631
|
+
let parenDepth = 0;
|
|
632
|
+
for (let k = 0; k < after.length; k++) {
|
|
633
|
+
const ch = after[k];
|
|
634
|
+
if (ch === '(') {
|
|
635
|
+
parenDepth++;
|
|
636
|
+
} else if (ch === ')') {
|
|
637
|
+
if (parenDepth === 0) {
|
|
638
|
+
closeIdx = cursorPos + k;
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
parenDepth--;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
const innerEnd = closeIdx >= 0 ? closeIdx : cursorPos;
|
|
645
|
+
const needsClosingParen = closeIdx < 0;
|
|
646
|
+
return { innerStart, innerEnd, needsClosingParen };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function dateArgumentSliceToDatetimeLocal(slice) {
|
|
650
|
+
const t = slice.trim();
|
|
651
|
+
if (!t) {
|
|
652
|
+
return '';
|
|
653
|
+
}
|
|
654
|
+
const unquoted = (t.startsWith('"') && t.endsWith('"')) || (t.startsWith('\'') && t.endsWith('\''))
|
|
655
|
+
? t.slice(1, -1)
|
|
656
|
+
: t;
|
|
657
|
+
const d = new Date(unquoted);
|
|
658
|
+
if (Number.isNaN(d.getTime())) {
|
|
659
|
+
return '';
|
|
660
|
+
}
|
|
661
|
+
return dateToDatetimeLocal(d);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* @param {Date} date
|
|
666
|
+
* @param {'quoted'|'timestamp'} format
|
|
667
|
+
*/
|
|
668
|
+
function formatDateArgumentValue(date, format) {
|
|
669
|
+
if (format === 'quoted') {
|
|
670
|
+
return JSON.stringify(date.toISOString());
|
|
671
|
+
}
|
|
672
|
+
return String(date.getTime());
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Inserts a Date argument value as timestamp or quoted ISO string.
|
|
677
|
+
* @param {string} searchText
|
|
678
|
+
* @param {{ innerStart: number, innerEnd: number, needsClosingParen?: boolean }} range
|
|
679
|
+
* @param {Date} date
|
|
680
|
+
* @param {'quoted'|'timestamp'} [format] - when omitted, inferred from the existing argument
|
|
681
|
+
*/
|
|
682
|
+
function insertDateInDateArgument(searchText, range, date, format) {
|
|
683
|
+
const slice = searchText.slice(range.innerStart, range.innerEnd);
|
|
684
|
+
const insertion = formatDateArgumentValue(date, format);
|
|
685
|
+
const { innerStart, innerEnd } = range;
|
|
686
|
+
const closing = range.needsClosingParen === true ? ')' : '';
|
|
687
|
+
return {
|
|
688
|
+
text: searchText.slice(0, innerStart) + insertion + closing + searchText.slice(innerEnd),
|
|
689
|
+
newCursorPos: innerStart + insertion.length + closing.length
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
449
693
|
module.exports = {
|
|
450
694
|
buildAutocompleteTrie,
|
|
451
695
|
getAutocompleteContext,
|
|
452
696
|
getAutocompleteSuggestions,
|
|
453
697
|
applySuggestion,
|
|
698
|
+
getDatePickerInsertionRange,
|
|
699
|
+
dateArgumentSliceToDatetimeLocal,
|
|
700
|
+
formatDateArgumentValue,
|
|
701
|
+
insertDateInDateArgument,
|
|
454
702
|
QUERY_SELECTORS,
|
|
455
703
|
VALUE_HELPERS,
|
|
456
704
|
FUNCTION_HELPERS
|
|
@@ -2464,6 +2712,424 @@ module.exports = app => app.component('create-document', {
|
|
|
2464
2712
|
});
|
|
2465
2713
|
|
|
2466
2714
|
|
|
2715
|
+
/***/ },
|
|
2716
|
+
|
|
2717
|
+
/***/ "./frontend/src/custom-date-picker/custom-date-picker.js"
|
|
2718
|
+
/*!***************************************************************!*\
|
|
2719
|
+
!*** ./frontend/src/custom-date-picker/custom-date-picker.js ***!
|
|
2720
|
+
\***************************************************************/
|
|
2721
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
2722
|
+
|
|
2723
|
+
"use strict";
|
|
2724
|
+
|
|
2725
|
+
|
|
2726
|
+
const template = __webpack_require__(/*! ./custom-date-picker.html */ "./frontend/src/custom-date-picker/custom-date-picker.html");
|
|
2727
|
+
const {
|
|
2728
|
+
dateToDatetimeLocal,
|
|
2729
|
+
dateToDateOnly,
|
|
2730
|
+
parseValueToDate,
|
|
2731
|
+
buildCalendarDays,
|
|
2732
|
+
clampInt,
|
|
2733
|
+
isSameDay,
|
|
2734
|
+
pad2,
|
|
2735
|
+
WEEKDAY_LABELS,
|
|
2736
|
+
MONTH_LABELS
|
|
2737
|
+
} = __webpack_require__(/*! ../_util/calendar */ "./frontend/src/_util/calendar.js");
|
|
2738
|
+
|
|
2739
|
+
const WHEEL_PAD = 2;
|
|
2740
|
+
const HOUR_OPTIONS = Array.from({ length: 24 }, (_, i) => i);
|
|
2741
|
+
const MINUTE_OPTIONS = Array.from({ length: 60 }, (_, i) => i);
|
|
2742
|
+
const SECOND_OPTIONS = Array.from({ length: 60 }, (_, i) => i);
|
|
2743
|
+
|
|
2744
|
+
function timePartsFromDate(date) {
|
|
2745
|
+
if (!date) {
|
|
2746
|
+
return { hour: 0, minute: 0, second: 0, millisecond: 0 };
|
|
2747
|
+
}
|
|
2748
|
+
return {
|
|
2749
|
+
hour: date.getHours(),
|
|
2750
|
+
minute: date.getMinutes(),
|
|
2751
|
+
second: date.getSeconds(),
|
|
2752
|
+
millisecond: date.getMilliseconds()
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
module.exports = app => app.component('custom-date-picker', {
|
|
2757
|
+
template,
|
|
2758
|
+
props: {
|
|
2759
|
+
value: {
|
|
2760
|
+
default: null
|
|
2761
|
+
},
|
|
2762
|
+
mode: {
|
|
2763
|
+
type: String,
|
|
2764
|
+
default: 'datetime' // 'date' | 'datetime'
|
|
2765
|
+
},
|
|
2766
|
+
compact: {
|
|
2767
|
+
type: Boolean,
|
|
2768
|
+
default: false
|
|
2769
|
+
},
|
|
2770
|
+
mini: {
|
|
2771
|
+
type: Boolean,
|
|
2772
|
+
default: false
|
|
2773
|
+
},
|
|
2774
|
+
immediate: {
|
|
2775
|
+
type: Boolean,
|
|
2776
|
+
default: true
|
|
2777
|
+
},
|
|
2778
|
+
horizontal: {
|
|
2779
|
+
type: Boolean,
|
|
2780
|
+
default: false
|
|
2781
|
+
},
|
|
2782
|
+
embedded: {
|
|
2783
|
+
type: Boolean,
|
|
2784
|
+
default: false
|
|
2785
|
+
}
|
|
2786
|
+
},
|
|
2787
|
+
emits: ['input', 'commit'],
|
|
2788
|
+
data() {
|
|
2789
|
+
const selected = parseValueToDate(this.value);
|
|
2790
|
+
const anchor = selected || new Date();
|
|
2791
|
+
const time = timePartsFromDate(anchor);
|
|
2792
|
+
return {
|
|
2793
|
+
viewYear: anchor.getFullYear(),
|
|
2794
|
+
viewMonth: anchor.getMonth(),
|
|
2795
|
+
selectedDate: anchor,
|
|
2796
|
+
hour: time.hour,
|
|
2797
|
+
minute: time.minute,
|
|
2798
|
+
second: time.second,
|
|
2799
|
+
millisecond: time.millisecond,
|
|
2800
|
+
pendingValue: null,
|
|
2801
|
+
wheelScrollLock: false,
|
|
2802
|
+
timeApplyTimer: null,
|
|
2803
|
+
hourOptions: HOUR_OPTIONS,
|
|
2804
|
+
minuteOptions: MINUTE_OPTIONS,
|
|
2805
|
+
secondOptions: SECOND_OPTIONS
|
|
2806
|
+
};
|
|
2807
|
+
},
|
|
2808
|
+
watch: {
|
|
2809
|
+
value() {
|
|
2810
|
+
this.syncFromValue();
|
|
2811
|
+
},
|
|
2812
|
+
horizontal() {
|
|
2813
|
+
this.$nextTick(() => this.scrollWheelsToValues());
|
|
2814
|
+
}
|
|
2815
|
+
},
|
|
2816
|
+
computed: {
|
|
2817
|
+
rootClasses() {
|
|
2818
|
+
const sizeClass = this.mini
|
|
2819
|
+
? 'p-0.5 text-[10px]'
|
|
2820
|
+
: (this.compact ? 'p-2 text-xs' : 'p-3 text-sm');
|
|
2821
|
+
const chromeClass = this.embedded
|
|
2822
|
+
? ''
|
|
2823
|
+
: (this.mini ? '' : 'bg-surface border border-edge-strong rounded-md shadow-sm');
|
|
2824
|
+
return [sizeClass, chromeClass];
|
|
2825
|
+
},
|
|
2826
|
+
timeSectionClasses() {
|
|
2827
|
+
if (this.horizontal) {
|
|
2828
|
+
return 'flex-1 min-w-0 border-l border-edge-strong pl-2';
|
|
2829
|
+
}
|
|
2830
|
+
return [
|
|
2831
|
+
'border-t border-edge-strong',
|
|
2832
|
+
this.mini ? 'mt-1 pt-1' : 'mt-3 pt-3'
|
|
2833
|
+
];
|
|
2834
|
+
},
|
|
2835
|
+
wheelPadCount() {
|
|
2836
|
+
return 2;
|
|
2837
|
+
},
|
|
2838
|
+
wheelVisibleRows() {
|
|
2839
|
+
return 5;
|
|
2840
|
+
},
|
|
2841
|
+
weekdayLabels() {
|
|
2842
|
+
return WEEKDAY_LABELS;
|
|
2843
|
+
},
|
|
2844
|
+
monthLabel() {
|
|
2845
|
+
return `${MONTH_LABELS[this.viewMonth]} ${this.viewYear}`;
|
|
2846
|
+
},
|
|
2847
|
+
calendarDays() {
|
|
2848
|
+
return buildCalendarDays(this.viewYear, this.viewMonth);
|
|
2849
|
+
},
|
|
2850
|
+
today() {
|
|
2851
|
+
const now = new Date();
|
|
2852
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
2853
|
+
},
|
|
2854
|
+
showTime() {
|
|
2855
|
+
return this.mode === 'datetime';
|
|
2856
|
+
},
|
|
2857
|
+
dayCellClass() {
|
|
2858
|
+
if (this.mini) {
|
|
2859
|
+
return 'h-6';
|
|
2860
|
+
}
|
|
2861
|
+
if (this.compact) {
|
|
2862
|
+
return 'h-7';
|
|
2863
|
+
}
|
|
2864
|
+
return 'aspect-square';
|
|
2865
|
+
},
|
|
2866
|
+
calendarSectionClass() {
|
|
2867
|
+
if (this.mini) return 'w-36';
|
|
2868
|
+
if (this.compact) return 'w-44';
|
|
2869
|
+
return 'w-48';
|
|
2870
|
+
},
|
|
2871
|
+
wheelItemPx() {
|
|
2872
|
+
if (this.mini) {
|
|
2873
|
+
return 32;
|
|
2874
|
+
}
|
|
2875
|
+
if (this.compact) {
|
|
2876
|
+
return 36;
|
|
2877
|
+
}
|
|
2878
|
+
return 40;
|
|
2879
|
+
},
|
|
2880
|
+
wheelListClass() {
|
|
2881
|
+
return [
|
|
2882
|
+
'overflow-y-auto overscroll-y-contain snap-y snap-mandatory',
|
|
2883
|
+
'[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'
|
|
2884
|
+
];
|
|
2885
|
+
},
|
|
2886
|
+
wheelListStyle() {
|
|
2887
|
+
return {
|
|
2888
|
+
height: `${this.wheelItemPx * this.wheelVisibleRows}px`
|
|
2889
|
+
};
|
|
2890
|
+
},
|
|
2891
|
+
wheelItemClass() {
|
|
2892
|
+
return [
|
|
2893
|
+
'snap-center shrink-0 w-full flex items-center justify-center font-mono transition-colors',
|
|
2894
|
+
this.mini ? 'h-8 text-sm' : (this.compact ? 'h-9 text-sm' : 'h-10 text-base')
|
|
2895
|
+
];
|
|
2896
|
+
},
|
|
2897
|
+
wheelHighlightClass() {
|
|
2898
|
+
return this.mini ? 'h-8' : (this.compact ? 'h-9' : 'h-10');
|
|
2899
|
+
},
|
|
2900
|
+
wheelColumnClass() {
|
|
2901
|
+
return this.mini ? 'w-11' : (this.compact ? 'w-12' : 'w-14');
|
|
2902
|
+
}
|
|
2903
|
+
},
|
|
2904
|
+
mounted() {
|
|
2905
|
+
this.$nextTick(() => this.scrollWheelsToValues());
|
|
2906
|
+
},
|
|
2907
|
+
beforeUnmount() {
|
|
2908
|
+
if (this.timeApplyTimer) {
|
|
2909
|
+
clearTimeout(this.timeApplyTimer);
|
|
2910
|
+
}
|
|
2911
|
+
},
|
|
2912
|
+
methods: {
|
|
2913
|
+
syncFromValue() {
|
|
2914
|
+
const selected = parseValueToDate(this.value);
|
|
2915
|
+
const anchor = selected || new Date();
|
|
2916
|
+
this.selectedDate = anchor;
|
|
2917
|
+
this.viewYear = anchor.getFullYear();
|
|
2918
|
+
this.viewMonth = anchor.getMonth();
|
|
2919
|
+
const time = timePartsFromDate(anchor);
|
|
2920
|
+
this.hour = time.hour;
|
|
2921
|
+
this.minute = time.minute;
|
|
2922
|
+
this.second = time.second;
|
|
2923
|
+
this.millisecond = time.millisecond;
|
|
2924
|
+
this.$nextTick(() => this.scrollWheelsToValues());
|
|
2925
|
+
},
|
|
2926
|
+
clampedTimeParts() {
|
|
2927
|
+
return {
|
|
2928
|
+
hour: clampInt(this.hour, 0, 23),
|
|
2929
|
+
minute: clampInt(this.minute, 0, 59),
|
|
2930
|
+
second: clampInt(this.second, 0, 59),
|
|
2931
|
+
millisecond: clampInt(this.millisecond, 0, 999)
|
|
2932
|
+
};
|
|
2933
|
+
},
|
|
2934
|
+
formatWheelValue(value) {
|
|
2935
|
+
return pad2(value);
|
|
2936
|
+
},
|
|
2937
|
+
wheelItemClasses(value, selectedValue) {
|
|
2938
|
+
return value === selectedValue
|
|
2939
|
+
? 'text-content-primary font-semibold'
|
|
2940
|
+
: 'text-content-tertiary hover:text-content-secondary';
|
|
2941
|
+
},
|
|
2942
|
+
wheelIndexFromScroll(el, max) {
|
|
2943
|
+
return clampInt(Math.round(el.scrollTop / this.wheelItemPx), 0, max);
|
|
2944
|
+
},
|
|
2945
|
+
scrollWheelTo(el, index) {
|
|
2946
|
+
if (!el) {
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
el.scrollTop = index * this.wheelItemPx;
|
|
2950
|
+
},
|
|
2951
|
+
scrollWheelsToValues() {
|
|
2952
|
+
this.wheelScrollLock = true;
|
|
2953
|
+
this.scrollWheelTo(this.$refs.hourWheel, this.hour);
|
|
2954
|
+
this.scrollWheelTo(this.$refs.minuteWheel, this.minute);
|
|
2955
|
+
this.scrollWheelTo(this.$refs.secondWheel, this.second);
|
|
2956
|
+
requestAnimationFrame(() => {
|
|
2957
|
+
this.wheelScrollLock = false;
|
|
2958
|
+
});
|
|
2959
|
+
},
|
|
2960
|
+
scheduleTimeApply() {
|
|
2961
|
+
if (this.timeApplyTimer) {
|
|
2962
|
+
clearTimeout(this.timeApplyTimer);
|
|
2963
|
+
}
|
|
2964
|
+
this.timeApplyTimer = setTimeout(() => {
|
|
2965
|
+
this.timeApplyTimer = null;
|
|
2966
|
+
this.applyTimeToSelection();
|
|
2967
|
+
}, 120);
|
|
2968
|
+
},
|
|
2969
|
+
onHourWheelScroll(event) {
|
|
2970
|
+
if (this.wheelScrollLock) {
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
const hour = this.wheelIndexFromScroll(event.target, 23);
|
|
2974
|
+
if (hour === this.hour) {
|
|
2975
|
+
return;
|
|
2976
|
+
}
|
|
2977
|
+
this.hour = hour;
|
|
2978
|
+
this.scheduleTimeApply();
|
|
2979
|
+
},
|
|
2980
|
+
onMinuteWheelScroll(event) {
|
|
2981
|
+
if (this.wheelScrollLock) {
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
const minute = this.wheelIndexFromScroll(event.target, 59);
|
|
2985
|
+
if (minute === this.minute) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
this.minute = minute;
|
|
2989
|
+
this.scheduleTimeApply();
|
|
2990
|
+
},
|
|
2991
|
+
onSecondWheelScroll(event) {
|
|
2992
|
+
if (this.wheelScrollLock) {
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
const second = this.wheelIndexFromScroll(event.target, 59);
|
|
2996
|
+
if (second === this.second) {
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
this.second = second;
|
|
3000
|
+
this.scheduleTimeApply();
|
|
3001
|
+
},
|
|
3002
|
+
selectHour(hour) {
|
|
3003
|
+
this.hour = hour;
|
|
3004
|
+
this.scrollWheelTo(this.$refs.hourWheel, hour);
|
|
3005
|
+
this.applyTimeToSelection();
|
|
3006
|
+
},
|
|
3007
|
+
selectMinute(minute) {
|
|
3008
|
+
this.minute = minute;
|
|
3009
|
+
this.scrollWheelTo(this.$refs.minuteWheel, minute);
|
|
3010
|
+
this.applyTimeToSelection();
|
|
3011
|
+
},
|
|
3012
|
+
selectSecond(second) {
|
|
3013
|
+
this.second = second;
|
|
3014
|
+
this.scrollWheelTo(this.$refs.secondWheel, second);
|
|
3015
|
+
this.applyTimeToSelection();
|
|
3016
|
+
},
|
|
3017
|
+
dayClasses(day) {
|
|
3018
|
+
const selected = this.selectedDate;
|
|
3019
|
+
const isSelected = selected && isSameDay(day.date, selected);
|
|
3020
|
+
const isToday = isSameDay(day.date, this.today);
|
|
3021
|
+
return {
|
|
3022
|
+
'text-content-tertiary': !day.inMonth,
|
|
3023
|
+
'bg-merlot-700 text-white hover:bg-merlot-800': isSelected,
|
|
3024
|
+
'hover:bg-surface-hover': day.inMonth && !isSelected,
|
|
3025
|
+
'ring-1 ring-merlot-700 ring-inset': isToday && !isSelected && day.inMonth,
|
|
3026
|
+
'font-medium': day.inMonth
|
|
3027
|
+
};
|
|
3028
|
+
},
|
|
3029
|
+
prevMonth() {
|
|
3030
|
+
if (this.viewMonth === 0) {
|
|
3031
|
+
this.viewMonth = 11;
|
|
3032
|
+
this.viewYear -= 1;
|
|
3033
|
+
} else {
|
|
3034
|
+
this.viewMonth -= 1;
|
|
3035
|
+
}
|
|
3036
|
+
},
|
|
3037
|
+
nextMonth() {
|
|
3038
|
+
if (this.viewMonth === 11) {
|
|
3039
|
+
this.viewMonth = 0;
|
|
3040
|
+
this.viewYear += 1;
|
|
3041
|
+
} else {
|
|
3042
|
+
this.viewMonth += 1;
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3045
|
+
goToToday() {
|
|
3046
|
+
const now = new Date();
|
|
3047
|
+
this.viewYear = now.getFullYear();
|
|
3048
|
+
this.viewMonth = now.getMonth();
|
|
3049
|
+
this.selectDay({
|
|
3050
|
+
date: new Date(now.getFullYear(), now.getMonth(), now.getDate()),
|
|
3051
|
+
inMonth: true
|
|
3052
|
+
});
|
|
3053
|
+
},
|
|
3054
|
+
selectDay(day) {
|
|
3055
|
+
const { hour, minute, second, millisecond } = this.clampedTimeParts();
|
|
3056
|
+
const next = new Date(day.date);
|
|
3057
|
+
if (this.showTime) {
|
|
3058
|
+
next.setHours(hour, minute, second, millisecond);
|
|
3059
|
+
} else {
|
|
3060
|
+
next.setHours(0, 0, 0, 0);
|
|
3061
|
+
}
|
|
3062
|
+
this.selectedDate = next;
|
|
3063
|
+
if (!day.inMonth) {
|
|
3064
|
+
this.viewYear = next.getFullYear();
|
|
3065
|
+
this.viewMonth = next.getMonth();
|
|
3066
|
+
}
|
|
3067
|
+
this.emitValue(next);
|
|
3068
|
+
},
|
|
3069
|
+
applyTimeToSelection() {
|
|
3070
|
+
const { hour, minute, second, millisecond } = this.clampedTimeParts();
|
|
3071
|
+
this.hour = hour;
|
|
3072
|
+
this.minute = minute;
|
|
3073
|
+
this.second = second;
|
|
3074
|
+
this.millisecond = millisecond;
|
|
3075
|
+
|
|
3076
|
+
if (!this.selectedDate) {
|
|
3077
|
+
const now = new Date();
|
|
3078
|
+
this.selectedDate = new Date(
|
|
3079
|
+
now.getFullYear(),
|
|
3080
|
+
now.getMonth(),
|
|
3081
|
+
now.getDate(),
|
|
3082
|
+
hour,
|
|
3083
|
+
minute,
|
|
3084
|
+
second,
|
|
3085
|
+
millisecond
|
|
3086
|
+
);
|
|
3087
|
+
this.viewYear = this.selectedDate.getFullYear();
|
|
3088
|
+
this.viewMonth = this.selectedDate.getMonth();
|
|
3089
|
+
} else {
|
|
3090
|
+
this.selectedDate = new Date(
|
|
3091
|
+
this.selectedDate.getFullYear(),
|
|
3092
|
+
this.selectedDate.getMonth(),
|
|
3093
|
+
this.selectedDate.getDate(),
|
|
3094
|
+
hour,
|
|
3095
|
+
minute,
|
|
3096
|
+
second,
|
|
3097
|
+
millisecond
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3100
|
+
this.emitValue(this.selectedDate);
|
|
3101
|
+
},
|
|
3102
|
+
valuePayload(date) {
|
|
3103
|
+
if (this.mode === 'date') {
|
|
3104
|
+
return dateToDateOnly(date);
|
|
3105
|
+
}
|
|
3106
|
+
return dateToDatetimeLocal(date);
|
|
3107
|
+
},
|
|
3108
|
+
emitValue(date) {
|
|
3109
|
+
const payload = this.valuePayload(date);
|
|
3110
|
+
if (!this.immediate) {
|
|
3111
|
+
this.pendingValue = payload;
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
this.$emit('input', payload);
|
|
3115
|
+
},
|
|
3116
|
+
commit() {
|
|
3117
|
+
const payload = this.pendingValue != null
|
|
3118
|
+
? this.pendingValue
|
|
3119
|
+
: (this.selectedDate ? this.valuePayload(this.selectedDate) : null);
|
|
3120
|
+
if (payload == null) {
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
this.$emit('commit', payload);
|
|
3124
|
+
this.pendingValue = null;
|
|
3125
|
+
},
|
|
3126
|
+
isSelectedDay(day) {
|
|
3127
|
+
return this.selectedDate && isSameDay(day.date, this.selectedDate);
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
});
|
|
3131
|
+
|
|
3132
|
+
|
|
2467
3133
|
/***/ },
|
|
2468
3134
|
|
|
2469
3135
|
/***/ "./frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js"
|
|
@@ -4880,7 +5546,7 @@ const template = __webpack_require__(/*! ./date-view-mode-picker.html */ "./fron
|
|
|
4880
5546
|
|
|
4881
5547
|
module.exports = app => app.component('date-view-mode-picker', {
|
|
4882
5548
|
template,
|
|
4883
|
-
props: ['viewMode', 'path'],
|
|
5549
|
+
props: ['viewMode', 'path', 'editting'],
|
|
4884
5550
|
emits: ['update:viewMode'],
|
|
4885
5551
|
computed: {
|
|
4886
5552
|
format() {
|
|
@@ -4937,20 +5603,12 @@ appendCSS(__webpack_require__(/*! ./document-details.css */ "./frontend/src/docu
|
|
|
4937
5603
|
|
|
4938
5604
|
module.exports = app => app.component('document-details', {
|
|
4939
5605
|
template,
|
|
4940
|
-
props: ['document', 'schemaPaths', 'virtualPaths', 'editting', 'changes', 'invalid', 'viewMode'
|
|
5606
|
+
props: ['document', 'schemaPaths', 'virtualPaths', 'editting', 'changes', 'invalid', 'viewMode'],
|
|
4941
5607
|
data() {
|
|
4942
5608
|
return {
|
|
4943
5609
|
searchQuery: '',
|
|
4944
5610
|
selectedType: '',
|
|
4945
|
-
collapsedVirtuals: new Set()
|
|
4946
|
-
showAddFieldModal: false,
|
|
4947
|
-
fieldData: {
|
|
4948
|
-
name: '',
|
|
4949
|
-
type: '',
|
|
4950
|
-
value: ''
|
|
4951
|
-
},
|
|
4952
|
-
fieldErrors: {},
|
|
4953
|
-
isSubmittingField: false
|
|
5611
|
+
collapsedVirtuals: new Set()
|
|
4954
5612
|
};
|
|
4955
5613
|
},
|
|
4956
5614
|
mounted() {
|
|
@@ -5020,20 +5678,6 @@ module.exports = app => app.component('document-details', {
|
|
|
5020
5678
|
|
|
5021
5679
|
return Array.from(types).sort();
|
|
5022
5680
|
},
|
|
5023
|
-
allFieldTypes() {
|
|
5024
|
-
const schemaTypes = this.availableTypes;
|
|
5025
|
-
const commonTypes = ['String', 'Number', 'Boolean', 'Date', 'Array', 'Object'];
|
|
5026
|
-
|
|
5027
|
-
// Combine schema types with common types, avoiding duplicates
|
|
5028
|
-
const allTypes = new Set([...schemaTypes, ...commonTypes]);
|
|
5029
|
-
return Array.from(allTypes).sort();
|
|
5030
|
-
},
|
|
5031
|
-
shouldUseAce() {
|
|
5032
|
-
return ['Array', 'Object', 'Embedded'].includes(this.fieldData.type);
|
|
5033
|
-
},
|
|
5034
|
-
shouldUseDatePicker() {
|
|
5035
|
-
return this.fieldData.type === 'Date';
|
|
5036
|
-
},
|
|
5037
5681
|
typeFilteredSchemaPaths() {
|
|
5038
5682
|
let paths = this.schemaPaths || [];
|
|
5039
5683
|
|
|
@@ -5245,120 +5889,6 @@ module.exports = app => app.component('document-details', {
|
|
|
5245
5889
|
isVirtualFieldCollapsed(fieldName) {
|
|
5246
5890
|
return this.collapsedVirtuals.has(fieldName);
|
|
5247
5891
|
},
|
|
5248
|
-
openAddFieldModal() {
|
|
5249
|
-
this.showAddFieldModal = true;
|
|
5250
|
-
},
|
|
5251
|
-
closeAddFieldModal() {
|
|
5252
|
-
this.showAddFieldModal = false;
|
|
5253
|
-
this.resetFieldForm();
|
|
5254
|
-
},
|
|
5255
|
-
async addNewField(fieldData) {
|
|
5256
|
-
// Emit event to parent component to handle the field addition
|
|
5257
|
-
this.$emit('add-field', fieldData);
|
|
5258
|
-
this.closeAddFieldModal();
|
|
5259
|
-
},
|
|
5260
|
-
validateFieldForm() {
|
|
5261
|
-
this.fieldErrors = {};
|
|
5262
|
-
|
|
5263
|
-
// Validate field name
|
|
5264
|
-
const trimmedName = this.fieldData.name.trim();
|
|
5265
|
-
if (!trimmedName) {
|
|
5266
|
-
this.fieldErrors.name = 'Field name is required';
|
|
5267
|
-
} else {
|
|
5268
|
-
const transformedName = this.getTransformedFieldName();
|
|
5269
|
-
if (!transformedName) {
|
|
5270
|
-
this.fieldErrors.name = 'Field name contains only invalid characters';
|
|
5271
|
-
} else if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(transformedName)) {
|
|
5272
|
-
this.fieldErrors.name = 'Field name must start with a letter, underscore, or $ and contain only letters, numbers, underscores, and $';
|
|
5273
|
-
}
|
|
5274
|
-
}
|
|
5275
|
-
|
|
5276
|
-
// Validate field type
|
|
5277
|
-
if (!this.fieldData.type) {
|
|
5278
|
-
this.fieldErrors.type = 'Field type is required';
|
|
5279
|
-
}
|
|
5280
|
-
|
|
5281
|
-
// Validate field value if provided
|
|
5282
|
-
if (this.fieldData.value && this.fieldData.value.trim()) {
|
|
5283
|
-
if (['Object', 'Array'].includes(this.fieldData.type)) {
|
|
5284
|
-
try {
|
|
5285
|
-
JSON.parse(this.fieldData.value);
|
|
5286
|
-
} catch (e) {
|
|
5287
|
-
this.fieldErrors.value = 'Invalid JSON format for object/array type';
|
|
5288
|
-
}
|
|
5289
|
-
} else if (this.fieldData.type === 'Number') {
|
|
5290
|
-
if (isNaN(Number(this.fieldData.value))) {
|
|
5291
|
-
this.fieldErrors.value = 'Invalid number format';
|
|
5292
|
-
}
|
|
5293
|
-
} else if (this.fieldData.type === 'Boolean') {
|
|
5294
|
-
const lowerValue = this.fieldData.value.toLowerCase();
|
|
5295
|
-
if (!['true', 'false', '1', '0', 'yes', 'no'].includes(lowerValue)) {
|
|
5296
|
-
this.fieldErrors.value = 'Invalid boolean value (use true/false, 1/0, yes/no)';
|
|
5297
|
-
}
|
|
5298
|
-
} else if (this.fieldData.type === 'Date') {
|
|
5299
|
-
// Date picker provides YYYY-MM-DD format, validate it
|
|
5300
|
-
const dateValue = new Date(this.fieldData.value);
|
|
5301
|
-
if (isNaN(dateValue.getTime())) {
|
|
5302
|
-
this.fieldErrors.value = 'Invalid date format';
|
|
5303
|
-
}
|
|
5304
|
-
}
|
|
5305
|
-
}
|
|
5306
|
-
|
|
5307
|
-
return Object.keys(this.fieldErrors).length === 0;
|
|
5308
|
-
},
|
|
5309
|
-
parseFieldValue(value, type) {
|
|
5310
|
-
if (!value || !value.trim()) {
|
|
5311
|
-
return null;
|
|
5312
|
-
}
|
|
5313
|
-
|
|
5314
|
-
switch (type) {
|
|
5315
|
-
case 'Number':
|
|
5316
|
-
return Number(value);
|
|
5317
|
-
case 'Boolean':
|
|
5318
|
-
const lowerValue = value.toLowerCase();
|
|
5319
|
-
return ['true', '1', 'yes'].includes(lowerValue);
|
|
5320
|
-
case 'Date':
|
|
5321
|
-
// For date picker, value is already in YYYY-MM-DD format
|
|
5322
|
-
return new Date(value);
|
|
5323
|
-
case 'Object':
|
|
5324
|
-
case 'Array':
|
|
5325
|
-
return JSON.parse(value);
|
|
5326
|
-
default:
|
|
5327
|
-
return value;
|
|
5328
|
-
}
|
|
5329
|
-
},
|
|
5330
|
-
async handleAddFieldSubmit() {
|
|
5331
|
-
if (!this.validateFieldForm()) {
|
|
5332
|
-
return;
|
|
5333
|
-
}
|
|
5334
|
-
|
|
5335
|
-
this.isSubmittingField = true;
|
|
5336
|
-
|
|
5337
|
-
try {
|
|
5338
|
-
const fieldData = {
|
|
5339
|
-
name: this.fieldData.name,
|
|
5340
|
-
type: this.fieldData.type,
|
|
5341
|
-
value: this.parseFieldValue(this.fieldData.value, this.fieldData.type)
|
|
5342
|
-
};
|
|
5343
|
-
|
|
5344
|
-
this.$emit('add-field', fieldData);
|
|
5345
|
-
this.closeAddFieldModal();
|
|
5346
|
-
} catch (error) {
|
|
5347
|
-
console.error('Error adding field:', error);
|
|
5348
|
-
this.fieldErrors.value = 'Error processing field value';
|
|
5349
|
-
} finally {
|
|
5350
|
-
this.isSubmittingField = false;
|
|
5351
|
-
}
|
|
5352
|
-
},
|
|
5353
|
-
resetFieldForm() {
|
|
5354
|
-
this.fieldData = {
|
|
5355
|
-
name: '',
|
|
5356
|
-
type: '',
|
|
5357
|
-
value: ''
|
|
5358
|
-
};
|
|
5359
|
-
this.fieldErrors = {};
|
|
5360
|
-
this.isSubmittingField = false;
|
|
5361
|
-
},
|
|
5362
5892
|
getVirtualFieldType(virtual) {
|
|
5363
5893
|
const value = virtual.value;
|
|
5364
5894
|
if (value === null || value === undefined) {
|
|
@@ -5941,7 +6471,15 @@ module.exports = app => app.component('document', {
|
|
|
5941
6471
|
shouldShowDeleteModal: false,
|
|
5942
6472
|
shouldShowCloneModal: false,
|
|
5943
6473
|
shouldShowValidationModal: false,
|
|
6474
|
+
showAddFieldModal: false,
|
|
5944
6475
|
validationResult: null,
|
|
6476
|
+
fieldData: {
|
|
6477
|
+
name: '',
|
|
6478
|
+
type: '',
|
|
6479
|
+
value: ''
|
|
6480
|
+
},
|
|
6481
|
+
fieldErrors: {},
|
|
6482
|
+
isSubmittingField: false,
|
|
5945
6483
|
previousQuery: null,
|
|
5946
6484
|
lastUpdatedAt: null,
|
|
5947
6485
|
isRefreshing: false,
|
|
@@ -6003,6 +6541,33 @@ module.exports = app => app.component('document', {
|
|
|
6003
6541
|
}
|
|
6004
6542
|
return shortcuts;
|
|
6005
6543
|
},
|
|
6544
|
+
allFieldTypes() {
|
|
6545
|
+
const types = new Set(['String', 'Number', 'Boolean', 'Date', 'Array', 'Object']);
|
|
6546
|
+
|
|
6547
|
+
(this.schemaPaths || []).forEach(path => {
|
|
6548
|
+
if (path.instance) {
|
|
6549
|
+
types.add(path.instance);
|
|
6550
|
+
}
|
|
6551
|
+
});
|
|
6552
|
+
|
|
6553
|
+
Object.keys(this.document || {}).forEach(key => {
|
|
6554
|
+
if ((this.schemaPaths || []).some(path => path.path === key)) {
|
|
6555
|
+
return;
|
|
6556
|
+
}
|
|
6557
|
+
const fieldType = this.getFieldType(this.document[key]);
|
|
6558
|
+
if (fieldType && fieldType !== 'unknown' && fieldType !== 'null') {
|
|
6559
|
+
types.add(fieldType);
|
|
6560
|
+
}
|
|
6561
|
+
});
|
|
6562
|
+
|
|
6563
|
+
return Array.from(types).sort();
|
|
6564
|
+
},
|
|
6565
|
+
shouldUseAce() {
|
|
6566
|
+
return ['Array', 'Object', 'Embedded'].includes(this.fieldData.type);
|
|
6567
|
+
},
|
|
6568
|
+
shouldUseDatePicker() {
|
|
6569
|
+
return this.fieldData.type === 'Date';
|
|
6570
|
+
},
|
|
6006
6571
|
isLambda() {
|
|
6007
6572
|
return !!window?.MONGOOSE_STUDIO_CONFIG?.isLambda;
|
|
6008
6573
|
}
|
|
@@ -6199,16 +6764,143 @@ module.exports = app => app.component('document', {
|
|
|
6199
6764
|
showClonedDocument(doc) {
|
|
6200
6765
|
this.$router.push({ path: `/model/${this.model}/document/${doc._id}` });
|
|
6201
6766
|
},
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6767
|
+
addField() {
|
|
6768
|
+
this.showAddFieldModal = true;
|
|
6769
|
+
},
|
|
6770
|
+
closeAddFieldModal() {
|
|
6771
|
+
this.showAddFieldModal = false;
|
|
6772
|
+
this.resetFieldForm();
|
|
6773
|
+
},
|
|
6774
|
+
resetFieldForm() {
|
|
6775
|
+
this.fieldData = {
|
|
6776
|
+
name: '',
|
|
6777
|
+
type: '',
|
|
6778
|
+
value: ''
|
|
6779
|
+
};
|
|
6780
|
+
this.fieldErrors = {};
|
|
6781
|
+
this.isSubmittingField = false;
|
|
6782
|
+
},
|
|
6783
|
+
getTransformedFieldName() {
|
|
6784
|
+
const trimmedName = this.fieldData.name.trim();
|
|
6785
|
+
return trimmedName
|
|
6786
|
+
.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
6787
|
+
.replace(/^[^a-zA-Z_$]+/, '');
|
|
6788
|
+
},
|
|
6789
|
+
validateFieldForm() {
|
|
6790
|
+
this.fieldErrors = {};
|
|
6210
6791
|
|
|
6211
|
-
|
|
6792
|
+
const trimmedName = this.fieldData.name.trim();
|
|
6793
|
+
if (!trimmedName) {
|
|
6794
|
+
this.fieldErrors.name = 'Field name is required';
|
|
6795
|
+
} else {
|
|
6796
|
+
const transformedName = this.getTransformedFieldName();
|
|
6797
|
+
if (!transformedName) {
|
|
6798
|
+
this.fieldErrors.name = 'Field name contains only invalid characters';
|
|
6799
|
+
} else if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(transformedName)) {
|
|
6800
|
+
this.fieldErrors.name = 'Field name must start with a letter, underscore, or $ and contain only letters, numbers, underscores, and $';
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
|
|
6804
|
+
if (!this.fieldData.type) {
|
|
6805
|
+
this.fieldErrors.type = 'Field type is required';
|
|
6806
|
+
}
|
|
6807
|
+
|
|
6808
|
+
if (this.fieldData.value && this.fieldData.value.trim()) {
|
|
6809
|
+
if (['Object', 'Array'].includes(this.fieldData.type)) {
|
|
6810
|
+
try {
|
|
6811
|
+
JSON.parse(this.fieldData.value);
|
|
6812
|
+
} catch (err) {
|
|
6813
|
+
this.fieldErrors.value = 'Invalid JSON format for object/array type';
|
|
6814
|
+
}
|
|
6815
|
+
} else if (this.fieldData.type === 'Number') {
|
|
6816
|
+
if (isNaN(Number(this.fieldData.value))) {
|
|
6817
|
+
this.fieldErrors.value = 'Invalid number format';
|
|
6818
|
+
}
|
|
6819
|
+
} else if (this.fieldData.type === 'Boolean') {
|
|
6820
|
+
const lowerValue = this.fieldData.value.toLowerCase();
|
|
6821
|
+
if (!['true', 'false', '1', '0', 'yes', 'no'].includes(lowerValue)) {
|
|
6822
|
+
this.fieldErrors.value = 'Invalid boolean value (use true/false, 1/0, yes/no)';
|
|
6823
|
+
}
|
|
6824
|
+
} else if (this.fieldData.type === 'Date') {
|
|
6825
|
+
const dateValue = new Date(this.fieldData.value);
|
|
6826
|
+
if (isNaN(dateValue.getTime())) {
|
|
6827
|
+
this.fieldErrors.value = 'Invalid date format';
|
|
6828
|
+
}
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
|
|
6832
|
+
return Object.keys(this.fieldErrors).length === 0;
|
|
6833
|
+
},
|
|
6834
|
+
parseFieldValue(value, type) {
|
|
6835
|
+
if (!value || !value.trim()) {
|
|
6836
|
+
return null;
|
|
6837
|
+
}
|
|
6838
|
+
|
|
6839
|
+
switch (type) {
|
|
6840
|
+
case 'Number':
|
|
6841
|
+
return Number(value);
|
|
6842
|
+
case 'Boolean':
|
|
6843
|
+
return ['true', '1', 'yes'].includes(value.toLowerCase());
|
|
6844
|
+
case 'Date':
|
|
6845
|
+
return new Date(value);
|
|
6846
|
+
case 'Object':
|
|
6847
|
+
case 'Array':
|
|
6848
|
+
return JSON.parse(value);
|
|
6849
|
+
default:
|
|
6850
|
+
return value;
|
|
6851
|
+
}
|
|
6852
|
+
},
|
|
6853
|
+
async handleAddFieldSubmit() {
|
|
6854
|
+
if (!this.validateFieldForm()) {
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
|
|
6858
|
+
this.isSubmittingField = true;
|
|
6859
|
+
|
|
6860
|
+
try {
|
|
6861
|
+
const fieldName = this.getTransformedFieldName();
|
|
6862
|
+
const fieldValue = this.parseFieldValue(this.fieldData.value, this.fieldData.type);
|
|
6863
|
+
|
|
6864
|
+
const { doc } = await api.Model.addField({
|
|
6865
|
+
model: this.model,
|
|
6866
|
+
_id: this.document._id,
|
|
6867
|
+
fieldName,
|
|
6868
|
+
fieldValue
|
|
6869
|
+
});
|
|
6870
|
+
this.document = doc;
|
|
6871
|
+
this.closeAddFieldModal();
|
|
6872
|
+
|
|
6873
|
+
this.$toast.success(`Field added! Field "${fieldName}" has been added to the document`);
|
|
6874
|
+
} catch (error) {
|
|
6875
|
+
console.error('Error adding field:', error);
|
|
6876
|
+
this.fieldErrors.value = 'Error adding field';
|
|
6877
|
+
} finally {
|
|
6878
|
+
this.isSubmittingField = false;
|
|
6879
|
+
}
|
|
6880
|
+
},
|
|
6881
|
+
getFieldType(value) {
|
|
6882
|
+
if (value === null || value === undefined) {
|
|
6883
|
+
return 'null';
|
|
6884
|
+
}
|
|
6885
|
+
if (Array.isArray(value)) {
|
|
6886
|
+
return 'Array';
|
|
6887
|
+
}
|
|
6888
|
+
if (value instanceof Date) {
|
|
6889
|
+
return 'Date';
|
|
6890
|
+
}
|
|
6891
|
+
if (typeof value === 'object') {
|
|
6892
|
+
return 'Object';
|
|
6893
|
+
}
|
|
6894
|
+
if (typeof value === 'number') {
|
|
6895
|
+
return 'Number';
|
|
6896
|
+
}
|
|
6897
|
+
if (typeof value === 'boolean') {
|
|
6898
|
+
return 'Boolean';
|
|
6899
|
+
}
|
|
6900
|
+
if (typeof value === 'string') {
|
|
6901
|
+
return 'String';
|
|
6902
|
+
}
|
|
6903
|
+
return 'unknown';
|
|
6212
6904
|
},
|
|
6213
6905
|
updateViewMode(mode) {
|
|
6214
6906
|
this.viewMode = mode;
|
|
@@ -6639,9 +7331,6 @@ module.exports = app => app.component('edit-date', {
|
|
|
6639
7331
|
template: template,
|
|
6640
7332
|
props: ['value', 'format'],
|
|
6641
7333
|
emits: ['input'],
|
|
6642
|
-
data: () => ({
|
|
6643
|
-
inputType: ''
|
|
6644
|
-
}),
|
|
6645
7334
|
methods: {
|
|
6646
7335
|
updateFromISO($event) {
|
|
6647
7336
|
const value = $event.target.value;
|
|
@@ -6661,23 +7350,6 @@ module.exports = app => app.component('edit-date', {
|
|
|
6661
7350
|
}
|
|
6662
7351
|
},
|
|
6663
7352
|
computed: {
|
|
6664
|
-
valueAsLocalString() {
|
|
6665
|
-
if (this.value == null) {
|
|
6666
|
-
return this.value;
|
|
6667
|
-
}
|
|
6668
|
-
const date = new Date(this.value);
|
|
6669
|
-
return [
|
|
6670
|
-
date.getFullYear(),
|
|
6671
|
-
'-',
|
|
6672
|
-
(date.getMonth() + 1).toString().padStart(2, '0'),
|
|
6673
|
-
'-',
|
|
6674
|
-
date.getDate().toString().padStart(2, '0'),
|
|
6675
|
-
'T',
|
|
6676
|
-
date.getHours().toString().padStart(2, '0'),
|
|
6677
|
-
':',
|
|
6678
|
-
date.getMinutes().toString().padStart(2, '0')
|
|
6679
|
-
].join('');
|
|
6680
|
-
},
|
|
6681
7353
|
valueAsISOString() {
|
|
6682
7354
|
if (this.value == null) {
|
|
6683
7355
|
return '' + this.value;
|
|
@@ -7256,7 +7928,7 @@ app.component('app-component', {
|
|
|
7256
7928
|
<div v-else-if="!hasAPIKey || user">
|
|
7257
7929
|
<navbar :user="user" :roles="roles" />
|
|
7258
7930
|
<div class="view">
|
|
7259
|
-
<router-view :key="$route.fullPath" :user="user" :roles="roles" :hasAPIKey="hasAPIKey" />
|
|
7931
|
+
<router-view :key="$route.meta.preserveStateKey || $route.fullPath" :user="user" :roles="roles" :hasAPIKey="hasAPIKey" />
|
|
7260
7932
|
</div>
|
|
7261
7933
|
</div>
|
|
7262
7934
|
</div>
|
|
@@ -8350,7 +9022,10 @@ const template = __webpack_require__(/*! ./document-search.html */ "./frontend/s
|
|
|
8350
9022
|
const {
|
|
8351
9023
|
buildAutocompleteTrie,
|
|
8352
9024
|
getAutocompleteSuggestions,
|
|
8353
|
-
applySuggestion
|
|
9025
|
+
applySuggestion,
|
|
9026
|
+
getDatePickerInsertionRange,
|
|
9027
|
+
dateArgumentSliceToDatetimeLocal,
|
|
9028
|
+
insertDateInDateArgument
|
|
8354
9029
|
} = __webpack_require__(/*! ../../_util/document-search-autocomplete */ "./frontend/src/_util/document-search-autocomplete.js");
|
|
8355
9030
|
|
|
8356
9031
|
module.exports = app => app.component('document-search', {
|
|
@@ -8370,7 +9045,11 @@ module.exports = app => app.component('document-search', {
|
|
|
8370
9045
|
autocompleteSuggestions: [],
|
|
8371
9046
|
autocompleteIndex: 0,
|
|
8372
9047
|
autocompleteTrie: null,
|
|
8373
|
-
searchText: this.value || ''
|
|
9048
|
+
searchText: this.value || '',
|
|
9049
|
+
datePickerContext: null,
|
|
9050
|
+
datePickerLocalValue: '',
|
|
9051
|
+
dateInsertFormat: 'quoted', // 'timestamp' | 'quoted'
|
|
9052
|
+
datePickerMinimized: false
|
|
8374
9053
|
};
|
|
8375
9054
|
},
|
|
8376
9055
|
watch: {
|
|
@@ -8412,10 +9091,31 @@ module.exports = app => app.component('document-search', {
|
|
|
8412
9091
|
});
|
|
8413
9092
|
}
|
|
8414
9093
|
},
|
|
9094
|
+
onSearchKeyup(ev) {
|
|
9095
|
+
if (
|
|
9096
|
+
this.autocompleteSuggestions.length > 0 &&
|
|
9097
|
+
(ev.key === 'ArrowUp' || ev.key === 'ArrowDown')
|
|
9098
|
+
) {
|
|
9099
|
+
return;
|
|
9100
|
+
}
|
|
9101
|
+
this.updateAutocomplete();
|
|
9102
|
+
},
|
|
8415
9103
|
updateAutocomplete() {
|
|
8416
9104
|
const input = this.$refs.searchInput;
|
|
8417
9105
|
const cursorPos = input ? input.selectionStart : 0;
|
|
8418
9106
|
|
|
9107
|
+
const dateRange = getDatePickerInsertionRange(this.searchText, cursorPos);
|
|
9108
|
+
if (dateRange && !this.datePickerContext) {
|
|
9109
|
+
this.datePickerMinimized = false;
|
|
9110
|
+
}
|
|
9111
|
+
this.datePickerContext = dateRange;
|
|
9112
|
+
if (dateRange) {
|
|
9113
|
+
const argSlice = this.searchText.slice(dateRange.innerStart, dateRange.innerEnd);
|
|
9114
|
+
this.datePickerLocalValue = dateArgumentSliceToDatetimeLocal(argSlice);
|
|
9115
|
+
} else {
|
|
9116
|
+
this.datePickerLocalValue = '';
|
|
9117
|
+
}
|
|
9118
|
+
|
|
8419
9119
|
if (this.autocompleteTrie) {
|
|
8420
9120
|
this.autocompleteSuggestions = getAutocompleteSuggestions(
|
|
8421
9121
|
this.autocompleteTrie,
|
|
@@ -8428,6 +9128,34 @@ module.exports = app => app.component('document-search', {
|
|
|
8428
9128
|
this.autocompleteSuggestions = [];
|
|
8429
9129
|
}
|
|
8430
9130
|
},
|
|
9131
|
+
applyDateFromPicker(localDateTime) {
|
|
9132
|
+
if (!localDateTime || !this.datePickerContext) {
|
|
9133
|
+
return;
|
|
9134
|
+
}
|
|
9135
|
+
const picked = new Date(localDateTime);
|
|
9136
|
+
if (Number.isNaN(picked.getTime())) {
|
|
9137
|
+
if (this.$toast) {
|
|
9138
|
+
this.$toast.error('Invalid date or time. Enter a valid date and time, then try again.');
|
|
9139
|
+
}
|
|
9140
|
+
return;
|
|
9141
|
+
}
|
|
9142
|
+
const result = insertDateInDateArgument(
|
|
9143
|
+
this.searchText,
|
|
9144
|
+
this.datePickerContext,
|
|
9145
|
+
picked,
|
|
9146
|
+
this.dateInsertFormat
|
|
9147
|
+
);
|
|
9148
|
+
const input = this.$refs.searchInput;
|
|
9149
|
+
this.searchText = result.text;
|
|
9150
|
+
this.autocompleteSuggestions = [];
|
|
9151
|
+
this.$nextTick(() => {
|
|
9152
|
+
if (input) {
|
|
9153
|
+
input.focus();
|
|
9154
|
+
input.setSelectionRange(result.newCursorPos, result.newCursorPos);
|
|
9155
|
+
}
|
|
9156
|
+
this.updateAutocomplete();
|
|
9157
|
+
});
|
|
9158
|
+
},
|
|
8431
9159
|
handleKeyDown(ev) {
|
|
8432
9160
|
if (this.autocompleteSuggestions.length === 0) {
|
|
8433
9161
|
return;
|
|
@@ -8462,6 +9190,12 @@ module.exports = app => app.component('document-search', {
|
|
|
8462
9190
|
});
|
|
8463
9191
|
this.autocompleteSuggestions = [];
|
|
8464
9192
|
},
|
|
9193
|
+
onWrapperFocusOut(ev) {
|
|
9194
|
+
if (!this.$el.contains(ev.relatedTarget)) {
|
|
9195
|
+
this.datePickerContext = null;
|
|
9196
|
+
this.datePickerMinimized = false;
|
|
9197
|
+
}
|
|
9198
|
+
},
|
|
8465
9199
|
addPathFilter(path) {
|
|
8466
9200
|
if (this.searchText) {
|
|
8467
9201
|
if (this.searchText.endsWith('}')) {
|
|
@@ -8779,6 +9513,7 @@ module.exports = app => app.component('models', {
|
|
|
8779
9513
|
currentModel: null,
|
|
8780
9514
|
modelDocumentCounts: {},
|
|
8781
9515
|
documents: [],
|
|
9516
|
+
allSchemaPaths: null,
|
|
8782
9517
|
schemaPaths: [],
|
|
8783
9518
|
filteredPaths: [],
|
|
8784
9519
|
selectedPaths: [],
|
|
@@ -8896,8 +9631,9 @@ module.exports = app => app.component('models', {
|
|
|
8896
9631
|
this.query = Object.assign({}, this.$route.query);
|
|
8897
9632
|
// Keep UI mode in sync with the URL on remounts.
|
|
8898
9633
|
this.isProjectionMenuSelected = this.$route?.query?.[PROJECTION_MODE_QUERY_KEY] === '1';
|
|
8899
|
-
const { models, readyState } = await api.Model.listModels();
|
|
9634
|
+
const { models, modelSchemaPaths, readyState } = await api.Model.listModels();
|
|
8900
9635
|
this.models = models;
|
|
9636
|
+
this.allSchemaPaths = modelSchemaPaths;
|
|
8901
9637
|
await this.loadModelCounts();
|
|
8902
9638
|
if (this.currentModel == null && this.models.length > 0) {
|
|
8903
9639
|
this.currentModel = this.models[0];
|
|
@@ -8908,6 +9644,29 @@ module.exports = app => app.component('models', {
|
|
|
8908
9644
|
if (readyState === 0) {
|
|
8909
9645
|
this.error = 'No models found and Mongoose is not connected. Check our documentation for more information.';
|
|
8910
9646
|
}
|
|
9647
|
+
} else {
|
|
9648
|
+
this._refreshSidebar = setInterval(async() => {
|
|
9649
|
+
try {
|
|
9650
|
+
const { models, modelSchemaPaths, readyState } = await api.Model.listModels();
|
|
9651
|
+
this.models = models;
|
|
9652
|
+
this.allSchemaPaths = modelSchemaPaths;
|
|
9653
|
+
await this.loadModelCounts();
|
|
9654
|
+
if (this.currentModel) {
|
|
9655
|
+
const schemaPaths = this.allSchemaPaths?.[this.currentModel] ?? {};
|
|
9656
|
+
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
9657
|
+
if (k1 === '_id' && k2 !== '_id') {
|
|
9658
|
+
return -1;
|
|
9659
|
+
}
|
|
9660
|
+
if (k1 !== '_id' && k2 === '_id') {
|
|
9661
|
+
return 1;
|
|
9662
|
+
}
|
|
9663
|
+
return 0;
|
|
9664
|
+
}).map(key => schemaPaths[key]);
|
|
9665
|
+
}
|
|
9666
|
+
} catch (e) {
|
|
9667
|
+
console.error(e);
|
|
9668
|
+
}
|
|
9669
|
+
}, 20000);
|
|
8911
9670
|
}
|
|
8912
9671
|
|
|
8913
9672
|
await this.initSearchFromUrl();
|
|
@@ -8918,25 +9677,33 @@ module.exports = app => app.component('models', {
|
|
|
8918
9677
|
this.$nextTick(() => {
|
|
8919
9678
|
const focusIntent = takeModelsRemountFocusIntent();
|
|
8920
9679
|
if (focusIntent === 'documentSearch') {
|
|
8921
|
-
|
|
8922
|
-
if (ds && typeof ds.focusInput === 'function') {
|
|
8923
|
-
ds.focusInput();
|
|
8924
|
-
}
|
|
9680
|
+
this.$refs.documentSearch?.focusInput();
|
|
8925
9681
|
return;
|
|
8926
9682
|
}
|
|
8927
9683
|
if (focusIntent === 'projection' && this.isProjectionMenuSelected) {
|
|
8928
|
-
|
|
8929
|
-
if (projectionSearch && typeof projectionSearch.focusInput === 'function') {
|
|
8930
|
-
projectionSearch.focusInput();
|
|
8931
|
-
}
|
|
9684
|
+
this.$refs.projectionSearch?.focusInput();
|
|
8932
9685
|
}
|
|
8933
9686
|
});
|
|
8934
9687
|
},
|
|
9688
|
+
unmounted() {
|
|
9689
|
+
clearInterval(this._refreshSidebar);
|
|
9690
|
+
},
|
|
8935
9691
|
watch: {
|
|
8936
9692
|
model(newModel) {
|
|
8937
9693
|
if (newModel !== this.currentModel) {
|
|
8938
9694
|
this.currentModel = newModel;
|
|
8939
9695
|
if (this.currentModel != null) {
|
|
9696
|
+
this.$refs.documentSearch?.focusInput();
|
|
9697
|
+
const schemaPaths = this.allSchemaPaths?.[newModel] ?? {};
|
|
9698
|
+
this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
|
|
9699
|
+
if (k1 === '_id' && k2 !== '_id') {
|
|
9700
|
+
return -1;
|
|
9701
|
+
}
|
|
9702
|
+
if (k1 !== '_id' && k2 === '_id') {
|
|
9703
|
+
return 1;
|
|
9704
|
+
}
|
|
9705
|
+
return 0;
|
|
9706
|
+
}).map(key => schemaPaths[key]);
|
|
8940
9707
|
this.initSearchFromUrl();
|
|
8941
9708
|
}
|
|
8942
9709
|
}
|
|
@@ -9380,6 +10147,8 @@ module.exports = app => app.component('models', {
|
|
|
9380
10147
|
this.query.sortKey = sortKey;
|
|
9381
10148
|
this.query.sortDirection = sortDirection;
|
|
9382
10149
|
delete this.query.sort;
|
|
10150
|
+
} else {
|
|
10151
|
+
this.sortBy = {};
|
|
9383
10152
|
}
|
|
9384
10153
|
if (this.currentModel != null) {
|
|
9385
10154
|
await this.getDocuments();
|
|
@@ -9395,6 +10164,8 @@ module.exports = app => app.component('models', {
|
|
|
9395
10164
|
this.selectedPaths = [...this.filteredPaths];
|
|
9396
10165
|
this.syncProjectionInputFromQueryOrPaths();
|
|
9397
10166
|
}
|
|
10167
|
+
} else {
|
|
10168
|
+
this.selectedPaths = [];
|
|
9398
10169
|
}
|
|
9399
10170
|
this.status = 'loaded';
|
|
9400
10171
|
|
|
@@ -9676,7 +10447,6 @@ module.exports = app => app.component('models', {
|
|
|
9676
10447
|
|
|
9677
10448
|
// Clear previous data
|
|
9678
10449
|
this.documents = [];
|
|
9679
|
-
this.schemaPaths = [];
|
|
9680
10450
|
this.numDocuments = null;
|
|
9681
10451
|
this.loadedAllDocs = false;
|
|
9682
10452
|
this.lastSelectedIndex = null;
|
|
@@ -10997,7 +11767,8 @@ module.exports = {
|
|
|
10997
11767
|
name: 'model',
|
|
10998
11768
|
component: 'models',
|
|
10999
11769
|
meta: {
|
|
11000
|
-
authorized: false
|
|
11770
|
+
authorized: false,
|
|
11771
|
+
preserveStateKey: 'model'
|
|
11001
11772
|
}
|
|
11002
11773
|
},
|
|
11003
11774
|
{
|
|
@@ -12379,6 +13150,8 @@ var map = {
|
|
|
12379
13150
|
"./": "./frontend/src/index.js",
|
|
12380
13151
|
"./_util/baseComponent": "./frontend/src/_util/baseComponent.js",
|
|
12381
13152
|
"./_util/baseComponent.js": "./frontend/src/_util/baseComponent.js",
|
|
13153
|
+
"./_util/calendar": "./frontend/src/_util/calendar.js",
|
|
13154
|
+
"./_util/calendar.js": "./frontend/src/_util/calendar.js",
|
|
12382
13155
|
"./_util/dateRange": "./frontend/src/_util/dateRange.js",
|
|
12383
13156
|
"./_util/dateRange.js": "./frontend/src/_util/dateRange.js",
|
|
12384
13157
|
"./_util/deepEqual": "./frontend/src/_util/deepEqual.js",
|
|
@@ -12421,6 +13194,9 @@ var map = {
|
|
|
12421
13194
|
"./create-document/create-document.css": "./frontend/src/create-document/create-document.css",
|
|
12422
13195
|
"./create-document/create-document.html": "./frontend/src/create-document/create-document.html",
|
|
12423
13196
|
"./create-document/create-document.js": "./frontend/src/create-document/create-document.js",
|
|
13197
|
+
"./custom-date-picker/custom-date-picker": "./frontend/src/custom-date-picker/custom-date-picker.js",
|
|
13198
|
+
"./custom-date-picker/custom-date-picker.html": "./frontend/src/custom-date-picker/custom-date-picker.html",
|
|
13199
|
+
"./custom-date-picker/custom-date-picker.js": "./frontend/src/custom-date-picker/custom-date-picker.js",
|
|
12424
13200
|
"./dashboard-result/dashboard-chart/dashboard-chart": "./frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js",
|
|
12425
13201
|
"./dashboard-result/dashboard-chart/dashboard-chart.html": "./frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html",
|
|
12426
13202
|
"./dashboard-result/dashboard-chart/dashboard-chart.js": "./frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js",
|
|
@@ -52856,6 +53632,17 @@ module.exports = "<div class=\"px-1\">\n <div class=\"mb-4\">\n <label class
|
|
|
52856
53632
|
|
|
52857
53633
|
/***/ },
|
|
52858
53634
|
|
|
53635
|
+
/***/ "./frontend/src/custom-date-picker/custom-date-picker.html"
|
|
53636
|
+
/*!*****************************************************************!*\
|
|
53637
|
+
!*** ./frontend/src/custom-date-picker/custom-date-picker.html ***!
|
|
53638
|
+
\*****************************************************************/
|
|
53639
|
+
(module) {
|
|
53640
|
+
|
|
53641
|
+
"use strict";
|
|
53642
|
+
module.exports = "<div\n class=\"custom-date-picker\"\n :class=\"rootClasses\"\n>\n <div :class=\"horizontal ? 'flex gap-3 items-start' : ''\">\n <div :class=\"[horizontal ? 'shrink-0' : '', calendarSectionClass]\">\n <div class=\"flex items-center justify-between gap-1 mb-1\" :class=\"mini ? 'mb-0.5' : 'mb-2'\">\n <button\n type=\"button\"\n class=\"shrink-0 flex items-center justify-center rounded border border-edge-strong hover:bg-surface-hover text-content-secondary\"\n :class=\"mini ? 'w-5 h-5 text-[10px]' : 'w-7 h-7'\"\n aria-label=\"Previous month\"\n @click=\"prevMonth\"\n >‹</button>\n <span\n class=\"flex-1 min-w-0 text-center font-medium text-content-primary truncate\"\n :class=\"mini ? 'text-[10px]' : ''\"\n >{{ monthLabel }}</span>\n <button\n type=\"button\"\n class=\"shrink-0 flex items-center justify-center rounded border border-edge-strong hover:bg-surface-hover text-content-secondary\"\n :class=\"mini ? 'w-5 h-5 text-[10px]' : 'w-7 h-7'\"\n aria-label=\"Next month\"\n @click=\"nextMonth\"\n >›</button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-px mb-0.5\">\n <div\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"flex items-center justify-center text-center text-content-tertiary font-medium min-w-0\"\n :class=\"[\n dayCellClass,\n mini ? 'text-[9px]' : (compact ? 'text-[10px]' : 'text-xs')\n ]\"\n >{{ mini ? label.charAt(0) : label }}</div>\n </div>\n\n <div class=\"grid grid-cols-7 gap-px\">\n <button\n v-for=\"day in calendarDays\"\n :key=\"day.key\"\n type=\"button\"\n class=\"min-w-0 w-full flex items-center justify-center rounded transition-colors\"\n :class=\"[\n dayCellClass,\n mini ? 'text-[10px]' : (compact ? 'text-xs' : 'text-sm'),\n dayClasses(day)\n ]\"\n :aria-label=\"day.label\"\n :aria-pressed=\"isSelectedDay(day)\"\n @click=\"selectDay(day)\"\n >{{ day.label }}</button>\n </div>\n </div>\n\n <div\n v-if=\"showTime\"\n :class=\"timeSectionClasses\"\n @mousedown.stop\n >\n <div\n class=\"text-content-secondary\"\n :class=\"[\n horizontal ? 'mb-1 text-xs' : 'mb-2',\n mini ? 'text-xs' : (horizontal ? '' : 'text-sm')\n ]\"\n >Time</div>\n <div class=\"relative flex items-center justify-center\">\n <div\n class=\"pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 border-y border-edge-strong bg-surface-hover/40\"\n :class=\"wheelHighlightClass\"\n aria-hidden=\"true\"\n ></div>\n <div class=\"relative flex items-center justify-center gap-1\">\n <div\n ref=\"hourWheel\"\n class=\"wheel-column\"\n :class=\"[wheelListClass, wheelColumnClass]\"\n :style=\"wheelListStyle\"\n @scroll=\"onHourWheelScroll\"\n >\n <div\n v-for=\"n in wheelPadCount\"\n :key=\"'hour-pad-top-' + n\"\n :class=\"wheelItemClass\"\n aria-hidden=\"true\"\n ></div>\n <button\n v-for=\"h in hourOptions\"\n :key=\"'hour-' + h\"\n type=\"button\"\n :class=\"[wheelItemClass, wheelItemClasses(h, hour)]\"\n :aria-label=\"'Hour ' + h\"\n :aria-selected=\"hour === h\"\n @click=\"selectHour(h)\"\n >{{ formatWheelValue(h) }}</button>\n <div\n v-for=\"n in wheelPadCount\"\n :key=\"'hour-pad-bottom-' + n\"\n :class=\"wheelItemClass\"\n aria-hidden=\"true\"\n ></div>\n </div>\n <span class=\"text-content-secondary font-medium\" :class=\"mini ? 'text-sm' : 'text-base'\">:</span>\n <div\n ref=\"minuteWheel\"\n class=\"wheel-column\"\n :class=\"[wheelListClass, wheelColumnClass]\"\n :style=\"wheelListStyle\"\n @scroll=\"onMinuteWheelScroll\"\n >\n <div\n v-for=\"n in wheelPadCount\"\n :key=\"'minute-pad-top-' + n\"\n :class=\"wheelItemClass\"\n aria-hidden=\"true\"\n ></div>\n <button\n v-for=\"m in minuteOptions\"\n :key=\"'minute-' + m\"\n type=\"button\"\n :class=\"[wheelItemClass, wheelItemClasses(m, minute)]\"\n :aria-label=\"'Minute ' + m\"\n :aria-selected=\"minute === m\"\n @click=\"selectMinute(m)\"\n >{{ formatWheelValue(m) }}</button>\n <div\n v-for=\"n in wheelPadCount\"\n :key=\"'minute-pad-bottom-' + n\"\n :class=\"wheelItemClass\"\n aria-hidden=\"true\"\n ></div>\n </div>\n <span class=\"text-content-secondary font-medium\" :class=\"mini ? 'text-sm' : 'text-base'\">:</span>\n <div\n ref=\"secondWheel\"\n class=\"wheel-column\"\n :class=\"[wheelListClass, wheelColumnClass]\"\n :style=\"wheelListStyle\"\n @scroll=\"onSecondWheelScroll\"\n >\n <div\n v-for=\"n in wheelPadCount\"\n :key=\"'second-pad-top-' + n\"\n :class=\"wheelItemClass\"\n aria-hidden=\"true\"\n ></div>\n <button\n v-for=\"s in secondOptions\"\n :key=\"'second-' + s\"\n type=\"button\"\n :class=\"[wheelItemClass, wheelItemClasses(s, second)]\"\n :aria-label=\"'Second ' + s\"\n :aria-selected=\"second === s\"\n @click=\"selectSecond(s)\"\n >{{ formatWheelValue(s) }}</button>\n <div\n v-for=\"n in wheelPadCount\"\n :key=\"'second-pad-bottom-' + n\"\n :class=\"wheelItemClass\"\n aria-hidden=\"true\"\n ></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div\n class=\"flex justify-end items-center gap-2\"\n :class=\"mini ? 'mt-1' : 'mt-2'\"\n >\n <button\n v-if=\"!immediate\"\n type=\"button\"\n class=\"text-xs font-medium text-white bg-merlot-700 hover:bg-merlot-800 px-2 py-1 rounded\"\n :class=\"mini ? 'py-0.5 px-1.5' : ''\"\n @click=\"commit\"\n >Apply</button>\n </div>\n</div>\n";
|
|
53643
|
+
|
|
53644
|
+
/***/ },
|
|
53645
|
+
|
|
52859
53646
|
/***/ "./frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html"
|
|
52860
53647
|
/*!****************************************************************************!*\
|
|
52861
53648
|
!*** ./frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html ***!
|
|
@@ -53039,7 +53826,7 @@ module.exports = "<pre class=\"w-full whitespace-pre-wrap break-words font-mono
|
|
|
53039
53826
|
(module) {
|
|
53040
53827
|
|
|
53041
53828
|
"use strict";
|
|
53042
|
-
module.exports = "<div class=\"flex items-center gap-2\" @click.stop>\n <select\n :value=\"format\"\n @input=\"onFormatChange($event.target.value)\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1\"\n >\n <option value=\"utc_iso\">UTC (ISO)</option>\n <option value=\"local_browser\">Local (Browser)</option>\n <option value=\"unix_ms\">Unix (ms)</option>\n <option value=\"unix_seconds\">Unix (seconds)</option>\n <option value=\"duration_relative\">Duration relative to now</option>\n <option value=\"custom_tz\">Custom TZ...</option>\n </select>\n <input\n v-if=\"format === 'custom_tz'\"\n :value=\"timezone\"\n @input=\"onTimezoneChange($event.target.value.trim())\"\n :list=\"timezoneDatalistId\"\n type=\"text\"\n placeholder=\"America/New_York\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1 w-40\"\n >\n <datalist v-if=\"format === 'custom_tz'\" :id=\"timezoneDatalistId\">\n <option v-for=\"tz in timezones\" :key=\"tz\" :value=\"tz\"></option>\n </datalist>\n</div>\n";
|
|
53829
|
+
module.exports = "<div class=\"flex items-center gap-2\" @click.stop>\n <select\n :value=\"format\"\n @input=\"onFormatChange($event.target.value)\"\n :disabled=\"editting\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1\"\n >\n <option value=\"utc_iso\">UTC (ISO)</option>\n <option value=\"local_browser\">Local (Browser)</option>\n <option value=\"unix_ms\">Unix (ms)</option>\n <option value=\"unix_seconds\">Unix (seconds)</option>\n <option value=\"duration_relative\">Duration relative to now</option>\n <option value=\"custom_tz\">Custom TZ...</option>\n </select>\n <input\n v-if=\"format === 'custom_tz'\"\n :value=\"timezone\"\n @input=\"onTimezoneChange($event.target.value.trim())\"\n :list=\"timezoneDatalistId\"\n type=\"text\"\n placeholder=\"America/New_York\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1 w-40\"\n >\n <datalist v-if=\"format === 'custom_tz'\" :id=\"timezoneDatalistId\">\n <option v-for=\"tz in timezones\" :key=\"tz\" :value=\"tz\"></option>\n </datalist>\n</div>\n";
|
|
53043
53830
|
|
|
53044
53831
|
/***/ },
|
|
53045
53832
|
|
|
@@ -53050,7 +53837,7 @@ module.exports = "<div class=\"flex items-center gap-2\" @click.stop>\n <select
|
|
|
53050
53837
|
(module) {
|
|
53051
53838
|
|
|
53052
53839
|
"use strict";
|
|
53053
|
-
module.exports = ".document-details {\n width: 100%;\n}\n\n.document-details .value {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n\n.document-details .path-key {\n background-color: #f0f0f0;\n margin-bottom: 0.5em;\n}\n\n.document-details .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n}\n\n.document-details .date-position {\n float: right;\n margin-top: -7px;\n}\n\n/*
|
|
53840
|
+
module.exports = ".document-details {\n width: 100%;\n}\n\n.document-details .value {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n\n.document-details .path-key {\n background-color: #f0f0f0;\n margin-bottom: 0.5em;\n}\n\n.document-details .path-type {\n color: rgba(0,0,0,.36);\n font-size: 0.8em;\n}\n\n.document-details .date-position {\n float: right;\n margin-top: -7px;\n}\n\n/* JSON View Styles */\n.json-view {\n width: 100%;\n}\n\n.json-view pre {\n margin: 0;\n max-height: 70vh;\n overflow: auto;\n line-height: 1.5;\n}\n\n.json-view pre::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n.json-view pre::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 4px;\n}\n\n.json-view pre::-webkit-scrollbar-thumb {\n background: #888;\n border-radius: 4px;\n}\n\n.json-view pre::-webkit-scrollbar-thumb:hover {\n background: #555;\n}\n";
|
|
53054
53841
|
|
|
53055
53842
|
/***/ },
|
|
53056
53843
|
|
|
@@ -53061,7 +53848,7 @@ module.exports = ".document-details {\n width: 100%;\n}\n\n.document-details .v
|
|
|
53061
53848
|
(module) {
|
|
53062
53849
|
|
|
53063
53850
|
"use strict";
|
|
53064
|
-
module.exports = "<div class=\"document-details\">\n <!-- View Toggle and Search/Filter Bar -->\n <div class=\"sticky top-[60px] z-40 bg-surface rounded-md p-4 border-b border-edge shadow-sm\">\n\n <!-- Search and Filter Bar (only show in fields view) -->\n <div v-if=\"viewMode === 'fields'\" class=\"flex md:gap-3\">\n <!-- Search Bar -->\n <div class=\"relative flex-1\">\n <input\n ref=\"searchInput\"\n v-model=\"searchQuery\"\n type=\"text\"\n placeholder=\"Search fields...\"\n class=\"w-full px-4 py-2 pl-10 pr-4 text-sm border border-edge-strong rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n />\n <div class=\"absolute inset-y-0 left-0 flex items-center pl-3\">\n <svg class=\"w-4 h-4 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"></path>\n </svg>\n </div>\n </div>\n\n <!-- Data Type Filter -->\n <div class=\"relative hidden md:block\">\n <select\n v-model=\"selectedType\"\n class=\"px-4 py-2 pr-8 text-sm border border-edge-strong rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-surface min-w-[140px] appearance-none\"\n >\n <option value=\"\">All Types</option>\n <option v-for=\"type in availableTypes\" :key=\"type\" :value=\"type\">\n {{type}}\n </option>\n </select>\n <div class=\"absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none\">\n <svg class=\"w-4 h-4 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fields View -->\n <div v-if=\"viewMode === 'fields'\">\n <!-- Matched Schema Paths (shown first when searching) -->\n <div\n v-for=\"path in matchedSchemaPaths\"\n :key=\"path.path || path\"\n class=\"value\"\n >\n <document-property\n :path=\"path\"\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :highlight=\"true\"\n :searchQuery=\"searchQuery\"\n :invalid=\"invalid\"></document-property>\n </div>\n\n <!-- Matched Virtual Fields (shown after matched schema paths when searching) -->\n <div\n v-for=\"path in matchedVirtuals\"\n :key=\"path.name\"\n class=\"border border-edge rounded-lg mb-2 transition-all duration-200 ease-in-out mt-4\"\n >\n <!-- Virtual Field Header (Always Visible) -->\n <div\n @click=\"toggleVirtualField(path.name)\"\n class=\"p-3 bg-slate-100 hover:bg-muted cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out\"\n >\n <div class=\"flex items-center\">\n <svg\n :class=\"isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">\n <span\n v-for=\"(part, index) in getSearchMatchParts(path.name)\"\n :key=\"index\"\n :class=\"{ 'font-bold': part.matched }\">{{ part.text }}</span>\n </span>\n <span v-if=\"path.isVirtual\" class=\"ml-2 text-sm text-purple-600\">(virtual - {{getVirtualFieldType(path)}})</span>\n <span v-else class=\"ml-2 text-sm text-blue-600\">(user-added - {{getVirtualFieldType(path)}})</span>\n </div>\n </div>\n\n <!-- Virtual Field Content (Collapsible) -->\n <div v-if=\"!isVirtualFieldCollapsed(path.name)\" class=\"p-3\">\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n </div>\n\n <!-- Unmatched Schema Paths (shown after matched items when searching, or all when not searching) -->\n <div\n v-for=\"path in unmatchedSchemaPaths\"\n :key=\"path.path || path\"\n class=\"value\"\n >\n <document-property\n :path=\"path\"\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :highlight=\"false\"\n :searchQuery=\"searchQuery\"\n :invalid=\"invalid\"></document-property>\n </div>\n\n <!-- Unmatched Virtual Fields (shown last) -->\n <div\n v-for=\"path in unmatchedVirtuals\"\n :key=\"path.name\"\n class=\"border border-edge rounded-lg mb-2 transition-all duration-200 ease-in-out\"\n >\n <!-- Virtual Field Header (Always Visible) -->\n <div\n @click=\"toggleVirtualField(path.name)\"\n class=\"p-3 bg-slate-100 hover:bg-muted cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out\"\n >\n <div class=\"flex items-center\">\n <svg\n :class=\"isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">{{path.name}}</span>\n <span v-if=\"path.isVirtual\" class=\"ml-2 text-sm text-purple-600\">(virtual - {{getVirtualFieldType(path)}})</span>\n <span v-else class=\"ml-2 text-sm text-blue-600\">(user-added - {{getVirtualFieldType(path)}})</span>\n </div>\n </div>\n\n <!-- Virtual Field Content (Collapsible) -->\n <div v-if=\"!isVirtualFieldCollapsed(path.name)\" class=\"p-3\">\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n </div>\n\n <!-- No Results Message -->\n <div v-if=\"searchQuery.trim() && !hasSearchMatches\" class=\"text-center py-8 text-content-tertiary\">\n <svg class=\"w-12 h-12 mx-auto mb-4 text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.29-1.009-5.824-2.709M15 6.291A7.962 7.962 0 0012 5c-2.34 0-4.29 1.009-5.824 2.709\"></path>\n </svg>\n <p>No fields found matching \"{{searchQuery}}\"</p>\n </div>\n </div>\n\n <!-- JSON View -->\n <div v-if=\"viewMode === 'json'\" class=\"json-view\">\n <div class=\"border border-edge-strong rounded-lg bg-page p-4 overflow-auto\">\n <pre class=\"text-sm font-mono text-gray-800 whitespace-pre\">{{formattedJson}}</pre>\n </div>\n </div>\n\n <!-- Add Field Modal -->\n <modal v-if=\"showAddFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"closeAddFieldModal\">×</div>\n <div class=\"add-field-modal\">\n <div class=\"mb-6\">\n <h2 class=\"text-xl font-semibold text-content mb-2\">Add New Field</h2>\n <p class=\"text-sm text-gray-600\">Create a new field for this document</p>\n </div>\n\n <form @submit.prevent=\"handleAddFieldSubmit\" class=\"space-y-4\">\n <!-- Field Name -->\n <div>\n <label for=\"fieldName\" class=\"block text-sm font-medium text-content-secondary mb-1\">\n Field Name *\n </label>\n <input\n id=\"fieldName\"\n v-model=\"fieldData.name\"\n type=\"text\"\n required\n placeholder=\"Enter field name...\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.name }\"\n />\n <p v-if=\"fieldErrors.name\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.name}}</p>\n <p v-if=\"fieldData.name && getTransformedFieldName() !== fieldData.name.trim()\" class=\"mt-1 text-sm text-blue-600\">\n Field name will be: <code class=\"bg-blue-50 px-1 py-0.5 rounded text-xs\">{{getTransformedFieldName()}}</code>\n </p>\n </div>\n\n <!-- Field Type -->\n <div>\n <label for=\"fieldType\" class=\"block text-sm font-medium text-content-secondary mb-1\">\n Field Type *\n </label>\n <select\n id=\"fieldType\"\n v-model=\"fieldData.type\"\n required\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.type }\"\n >\n <option value=\"\">Select field type...</option>\n <option v-for=\"type in allFieldTypes\" :key=\"type\" :value=\"type\">\n {{type}}\n </option>\n </select>\n <p v-if=\"fieldErrors.type\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.type}}</p>\n </div>\n\n <!-- Field Value -->\n <div>\n <label for=\"fieldValue\" class=\"block text-sm font-medium text-content-secondary mb-1\">\n Initial Value\n </label>\n\n <!-- Date picker for Date type -->\n <input\n v-if=\"shouldUseDatePicker\"\n v-model=\"fieldData.value\"\n type=\"date\"\n id=\"fieldValue\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <!-- Simple input for basic types -->\n <input\n v-else-if=\"!shouldUseAce\"\n v-model=\"fieldData.value\"\n type=\"text\"\n id=\"fieldValue\"\n placeholder=\"Enter initial value (optional)...\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <!-- Ace editor for complex types (Array/Object/Embedded) -->\n <ace-editor\n v-else\n id=\"fieldValue\"\n v-model=\"fieldData.value\"\n mode=\"json\"\n :line-numbers=\"true\"\n :min-lines=\"3\"\n class=\"add-field-ace-editor w-full min-h-[100px]\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <p v-if=\"fieldErrors.value\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.value}}</p>\n <p class=\"mt-1 text-xs text-content-tertiary\">\n <span v-if=\"shouldUseDatePicker\">Select a date or leave empty for null/undefined values.</span>\n <span v-else-if=\"shouldUseAce\">Leave empty for null/undefined values. Use valid JSON format.</span>\n <span v-else>Leave empty for null/undefined values.</span>\n </p>\n </div>\n\n\n <!-- Action Buttons -->\n <div class=\"flex justify-end gap-3 pt-4 border-t border-edge\">\n <button\n type=\"button\"\n @click=\"closeAddFieldModal\"\n class=\"px-4 py-2 text-sm font-medium text-content-secondary bg-surface border border-edge-strong rounded-md hover:bg-page focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n :disabled=\"isSubmittingField\"\n class=\"px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <span v-if=\"isSubmittingField\">Adding...</span>\n <span v-else>Add Field</span>\n </button>\n </div>\n </form>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
53851
|
+
module.exports = "<div class=\"document-details\">\n <!-- View Toggle and Search/Filter Bar -->\n <div class=\"sticky top-[60px] z-40 bg-surface rounded-md p-4 border-b border-edge shadow-sm\">\n\n <!-- Search and Filter Bar (only show in fields view) -->\n <div v-if=\"viewMode === 'fields'\" class=\"flex md:gap-3\">\n <!-- Search Bar -->\n <div class=\"relative flex-1\">\n <input\n ref=\"searchInput\"\n v-model=\"searchQuery\"\n type=\"text\"\n placeholder=\"Search fields...\"\n class=\"w-full px-4 py-2 pl-10 pr-4 text-sm border border-edge-strong rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n />\n <div class=\"absolute inset-y-0 left-0 flex items-center pl-3\">\n <svg class=\"w-4 h-4 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"></path>\n </svg>\n </div>\n </div>\n\n <!-- Data Type Filter -->\n <div class=\"relative hidden md:block\">\n <select\n v-model=\"selectedType\"\n class=\"px-4 py-2 pr-8 text-sm border border-edge-strong rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-surface min-w-[140px] appearance-none\"\n >\n <option value=\"\">All Types</option>\n <option v-for=\"type in availableTypes\" :key=\"type\" :value=\"type\">\n {{type}}\n </option>\n </select>\n <div class=\"absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none\">\n <svg class=\"w-4 h-4 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Fields View -->\n <div v-if=\"viewMode === 'fields'\">\n <!-- Matched Schema Paths (shown first when searching) -->\n <div\n v-for=\"path in matchedSchemaPaths\"\n :key=\"path.path || path\"\n class=\"value\"\n >\n <document-property\n :path=\"path\"\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :highlight=\"true\"\n :searchQuery=\"searchQuery\"\n :invalid=\"invalid\"></document-property>\n </div>\n\n <!-- Matched Virtual Fields (shown after matched schema paths when searching) -->\n <div\n v-for=\"path in matchedVirtuals\"\n :key=\"path.name\"\n class=\"border border-edge rounded-lg mb-2 transition-all duration-200 ease-in-out mt-4\"\n >\n <!-- Virtual Field Header (Always Visible) -->\n <div\n @click=\"toggleVirtualField(path.name)\"\n class=\"p-3 bg-slate-100 hover:bg-muted cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out\"\n >\n <div class=\"flex items-center\">\n <svg\n :class=\"isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">\n <span\n v-for=\"(part, index) in getSearchMatchParts(path.name)\"\n :key=\"index\"\n :class=\"{ 'font-bold': part.matched }\">{{ part.text }}</span>\n </span>\n <span v-if=\"path.isVirtual\" class=\"ml-2 text-sm text-purple-600\">(virtual - {{getVirtualFieldType(path)}})</span>\n <span v-else class=\"ml-2 text-sm text-blue-600\">(user-added - {{getVirtualFieldType(path)}})</span>\n </div>\n </div>\n\n <!-- Virtual Field Content (Collapsible) -->\n <div v-if=\"!isVirtualFieldCollapsed(path.name)\" class=\"p-3\">\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n </div>\n\n <!-- Unmatched Schema Paths (shown after matched items when searching, or all when not searching) -->\n <div\n v-for=\"path in unmatchedSchemaPaths\"\n :key=\"path.path || path\"\n class=\"value\"\n >\n <document-property\n :path=\"path\"\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :highlight=\"false\"\n :searchQuery=\"searchQuery\"\n :invalid=\"invalid\"></document-property>\n </div>\n\n <!-- Unmatched Virtual Fields (shown last) -->\n <div\n v-for=\"path in unmatchedVirtuals\"\n :key=\"path.name\"\n class=\"border border-edge rounded-lg mb-2 transition-all duration-200 ease-in-out\"\n >\n <!-- Virtual Field Header (Always Visible) -->\n <div\n @click=\"toggleVirtualField(path.name)\"\n class=\"p-3 bg-slate-100 hover:bg-muted cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out\"\n >\n <div class=\"flex items-center\">\n <svg\n :class=\"isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">{{path.name}}</span>\n <span v-if=\"path.isVirtual\" class=\"ml-2 text-sm text-purple-600\">(virtual - {{getVirtualFieldType(path)}})</span>\n <span v-else class=\"ml-2 text-sm text-blue-600\">(user-added - {{getVirtualFieldType(path)}})</span>\n </div>\n </div>\n\n <!-- Virtual Field Content (Collapsible) -->\n <div v-if=\"!isVirtualFieldCollapsed(path.name)\" class=\"p-3\">\n <div v-if=\"path.value == null\" class=\"text-sky-800\">\n {{'' + path.value}}\n </div>\n <div v-else>\n {{path.value}}\n </div>\n </div>\n </div>\n\n <!-- No Results Message -->\n <div v-if=\"searchQuery.trim() && !hasSearchMatches\" class=\"text-center py-8 text-content-tertiary\">\n <svg class=\"w-12 h-12 mx-auto mb-4 text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.29-1.009-5.824-2.709M15 6.291A7.962 7.962 0 0012 5c-2.34 0-4.29 1.009-5.824 2.709\"></path>\n </svg>\n <p>No fields found matching \"{{searchQuery}}\"</p>\n </div>\n </div>\n\n <!-- JSON View -->\n <div v-if=\"viewMode === 'json'\" class=\"json-view\">\n <div class=\"border border-edge-strong rounded-lg bg-page p-4 overflow-auto\">\n <pre class=\"text-sm font-mono text-gray-800 whitespace-pre\">{{formattedJson}}</pre>\n </div>\n </div>\n</div>\n";
|
|
53065
53852
|
|
|
53066
53853
|
/***/ },
|
|
53067
53854
|
|
|
@@ -53083,7 +53870,7 @@ module.exports = ".document-details {\n width: 100%;\n }\n \n .document-de
|
|
|
53083
53870
|
(module) {
|
|
53084
53871
|
|
|
53085
53872
|
"use strict";
|
|
53086
|
-
module.exports = "<div class=\"border border-edge bg-surface rounded-lg mb-2\" style=\"overflow: visible;\">\n <!-- Collapsible Header -->\n <div\n @click=\"toggleCollapse\"\n class=\"p-1 cursor-pointer flex items-center justify-between border-b border-edge bg-slate-100 hover:bg-muted transition-colors duration-200 ease-in-out\"\n style=\"overflow: visible; position: relative;\"\n >\n <div class=\"flex items-center\" >\n <svg\n :class=\"isCollapsed ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">\n <span\n v-for=\"(part, index) in pathNameParts\"\n :key=\"index\"\n :class=\"{ 'font-bold': part.matched }\">{{ part.text }}</span>\n </span>\n <span class=\"ml-2 text-sm text-content-tertiary\">({{(path.instance || 'unknown').toLowerCase()}})</span>\n <div v-if=\"path.instance === 'Array' && canUseArrayTableView\" class=\"ml-3 inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold\">\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"arrayDetailViewMode === 'list' ? 'bg-merlot-700 text-white shadow' : 'text-content-secondary hover:text-content'\"\n @click.stop=\"setArrayDetailViewMode('list')\">\n List\n </button>\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"arrayDetailViewMode === 'table' ? 'bg-merlot-700 text-white shadow' : 'text-content-secondary hover:text-content'\"\n @click.stop=\"setArrayDetailViewMode('table')\">\n Table\n </button>\n </div>\n <div v-if=\"isGeoJsonGeometry\" class=\"ml-3 inline-flex items-center gap-2\">\n <div class=\"inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold\">\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('text')\">\n Text\n </button>\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('map')\">\n Map\n </button>\n </div>\n <!-- Info icon with tooltip -->\n <div v-if=\"editting\" class=\"relative inline-block\" style=\"z-index: 10002;\" @mouseenter=\"showTooltip = true\" @mouseleave=\"showTooltip = false\" @click.stop>\n <svg\n ref=\"infoIcon\"\n class=\"w-6 h-6 text-gray-400 hover:text-gray-600 cursor-help\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n <div\n v-show=\"showTooltip\"\n ref=\"tooltip\"\n class=\"absolute left-full top-0 ml-2 w-64 p-3 text-white text-xs rounded-lg shadow-xl\"\n style=\"z-index: 99999; pointer-events: none; white-space: normal; position: fixed; background-color: #111827;\"\n :style=\"getTooltipStyle()\"\n >\n <div class=\"font-semibold mb-2\">Map Controls:</div>\n <div v-if=\"isGeoJsonPoint\" class=\"space-y-1\">\n <div>• Drag pin to move location</div>\n </div>\n <div v-else-if=\"isGeoJsonPolygon\" class=\"space-y-1\">\n <div>• Drag vertices to reshape polygon</div>\n <div v-if=\"isMultiPolygon\">• Right-click edge to add new vertex</div>\n <div>• Right-click vertex to delete</div>\n </div>\n <div class=\"absolute top-2 -left-1 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-900\"></div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"flex items-center gap-2\">\n <date-view-mode-picker\n v-if=\"isDatePath\"\n :viewMode=\"dateViewMode\"\n :path=\"path\"\n @update:viewMode=\"dateViewMode = $event\"\n ></date-view-mode-picker>\n <objectid-view-mode-picker\n v-if=\"isObjectIdPath\"\n :viewMode=\"objectIdViewMode\"\n @update:viewMode=\"objectIdViewMode = $event\"\n ></objectid-view-mode-picker>\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-sm text-gray-600 hover:text-gray-800 px-2 py-1 rounded-md border border-transparent hover:border-edge-strong bg-surface\"\n @click.stop.prevent=\"copyPropertyValue\"\n title=\"Copy value\"\n aria-label=\"Copy property value\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7h8m-8 4h8m-8 4h5m-7-9a2 2 0 012-2h7a2 2 0 012 2v10a2 2 0 01-2 2H8l-4-4V7a2 2 0 012-2z\" />\n </svg>\n {{copyButtonLabel}}\n </button>\n <router-link\n v-if=\"path.ref && getValueForPath(path.path)\"\n :to=\"`/model/${path.ref}/document/${getValueForPath(path.path)}`\"\n class=\"bg-primary hover:bg-primary-hover text-primary-text px-2 py-1 text-sm rounded-md\"\n @click.stop\n >View Document\n </router-link>\n </div>\n </div>\n\n <!-- Collapsible Content -->\n <div v-if=\"!isCollapsed\" class=\"p-2\">\n <!-- Date Type Selector (when editing dates) -->\n <div v-if=\"editting && path.instance === 'Date'\" class=\"mb-3 flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n\n <!-- Field Content -->\n <div v-if=\"editting && path.path !== '_id'\">\n <!-- Use detail-default with map editing for GeoJSON geometries -->\n <component\n v-if=\"isGeoJsonGeometry\"\n :is=\"getComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :viewMode=\"detailViewMode\"\n :on-change=\"handleInputChange\"\n >\n </component>\n <!-- Use standard edit components for other types -->\n <component\n v-else\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n v-bind=\"getEditComponentProps(path)\"\n @input=\"handleInputChange($event)\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <!-- Arrays own their truncation and array search behavior in detail-array. -->\n <component\n v-if=\"isArray\"\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"propertyDetailViewMode\"\n :truncate=\"needsTruncation\"\n @updated=\"renderedValue = $event\"></component>\n <!-- Non-array truncated view -->\n <div v-else-if=\"shouldShowTruncated && !isArray\" class=\"relative\">\n <div class=\"text-content-secondary whitespace-pre-wrap break-words font-mono text-sm\">{{truncatedString}}</div>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-merlot-700 hover:text-merlot-900 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show more ({{valueAsString.length}} characters)\n </button>\n </div>\n <!-- Expanded view -->\n <div v-else-if=\"needsTruncation && isValueExpanded\" class=\"relative\">\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"propertyDetailViewMode\"\n @updated=\"renderedValue = $event\"></component>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-merlot-700 hover:text-merlot-900 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\n >\n <svg class=\"w-4 h-4 rotate-180\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show less\n </button>\n </div>\n <!-- Full view (no truncation needed) -->\n <div v-else>\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"propertyDetailViewMode\"\n @updated=\"renderedValue = $event\"></component>\n </div>\n </div>\n </div>\n</div>\n";
|
|
53873
|
+
module.exports = "<div class=\"border border-edge bg-surface rounded-lg mb-2\" style=\"overflow: visible;\">\n <!-- Collapsible Header -->\n <div\n @click=\"toggleCollapse\"\n class=\"p-1 cursor-pointer flex items-center justify-between border-b border-edge bg-slate-100 hover:bg-muted transition-colors duration-200 ease-in-out\"\n style=\"overflow: visible; position: relative;\"\n >\n <div class=\"flex items-center\" >\n <svg\n :class=\"isCollapsed ? 'rotate-0' : 'rotate-90'\"\n class=\"w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n <span class=\"font-medium text-content\">\n <span\n v-for=\"(part, index) in pathNameParts\"\n :key=\"index\"\n :class=\"{ 'font-bold': part.matched }\">{{ part.text }}</span>\n </span>\n <span class=\"ml-2 text-sm text-content-tertiary\">({{(path.instance || 'unknown').toLowerCase()}})</span>\n <div v-if=\"path.instance === 'Array' && canUseArrayTableView\" class=\"ml-3 inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold\">\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"arrayDetailViewMode === 'list' ? 'bg-merlot-700 text-white shadow' : 'text-content-secondary hover:text-content'\"\n @click.stop=\"setArrayDetailViewMode('list')\">\n List\n </button>\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"arrayDetailViewMode === 'table' ? 'bg-merlot-700 text-white shadow' : 'text-content-secondary hover:text-content'\"\n @click.stop=\"setArrayDetailViewMode('table')\">\n Table\n </button>\n </div>\n <div v-if=\"isGeoJsonGeometry\" class=\"ml-3 inline-flex items-center gap-2\">\n <div class=\"inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold\">\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('text')\">\n Text\n </button>\n <button\n type=\"button\"\n class=\"rounded-full px-2.5 py-0.5 transition\"\n :class=\"detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'\"\n :style=\"detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''\"\n @click.stop=\"setDetailViewMode('map')\">\n Map\n </button>\n </div>\n <!-- Info icon with tooltip -->\n <div v-if=\"editting\" class=\"relative inline-block\" style=\"z-index: 10002;\" @mouseenter=\"showTooltip = true\" @mouseleave=\"showTooltip = false\" @click.stop>\n <svg\n ref=\"infoIcon\"\n class=\"w-6 h-6 text-gray-400 hover:text-gray-600 cursor-help\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n <div\n v-show=\"showTooltip\"\n ref=\"tooltip\"\n class=\"absolute left-full top-0 ml-2 w-64 p-3 text-white text-xs rounded-lg shadow-xl\"\n style=\"z-index: 99999; pointer-events: none; white-space: normal; position: fixed; background-color: #111827;\"\n :style=\"getTooltipStyle()\"\n >\n <div class=\"font-semibold mb-2\">Map Controls:</div>\n <div v-if=\"isGeoJsonPoint\" class=\"space-y-1\">\n <div>• Drag pin to move location</div>\n </div>\n <div v-else-if=\"isGeoJsonPolygon\" class=\"space-y-1\">\n <div>• Drag vertices to reshape polygon</div>\n <div v-if=\"isMultiPolygon\">• Right-click edge to add new vertex</div>\n <div>• Right-click vertex to delete</div>\n </div>\n <div class=\"absolute top-2 -left-1 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-900\"></div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"flex items-center gap-2\">\n <date-view-mode-picker\n v-if=\"isDatePath\"\n :viewMode=\"dateViewMode\"\n :path=\"path\"\n :editting=\"editting\"\n @update:viewMode=\"dateViewMode = $event\"\n ></date-view-mode-picker>\n <objectid-view-mode-picker\n v-if=\"isObjectIdPath\"\n :viewMode=\"objectIdViewMode\"\n @update:viewMode=\"objectIdViewMode = $event\"\n ></objectid-view-mode-picker>\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-sm text-gray-600 hover:text-gray-800 px-2 py-1 rounded-md border border-transparent hover:border-edge-strong bg-surface\"\n @click.stop.prevent=\"copyPropertyValue\"\n title=\"Copy value\"\n aria-label=\"Copy property value\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7h8m-8 4h8m-8 4h5m-7-9a2 2 0 012-2h7a2 2 0 012 2v10a2 2 0 01-2 2H8l-4-4V7a2 2 0 012-2z\" />\n </svg>\n {{copyButtonLabel}}\n </button>\n <router-link\n v-if=\"path.ref && getValueForPath(path.path)\"\n :to=\"`/model/${path.ref}/document/${getValueForPath(path.path)}`\"\n class=\"bg-primary hover:bg-primary-hover text-primary-text px-2 py-1 text-sm rounded-md\"\n @click.stop\n >View Document\n </router-link>\n </div>\n </div>\n\n <!-- Collapsible Content -->\n <div v-if=\"!isCollapsed\" class=\"p-2\">\n <!-- Date Type Selector (when editing dates) -->\n <div v-if=\"editting && path.instance === 'Date'\" class=\"mb-3 flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-merlot-700' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-merlot-700' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n\n <!-- Field Content -->\n <div v-if=\"editting && path.path !== '_id'\">\n <!-- Use detail-default with map editing for GeoJSON geometries -->\n <component\n v-if=\"isGeoJsonGeometry\"\n :is=\"getComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :viewMode=\"detailViewMode\"\n :on-change=\"handleInputChange\"\n >\n </component>\n <!-- Use standard edit components for other types -->\n <component\n v-else\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n v-bind=\"getEditComponentProps(path)\"\n @input=\"handleInputChange($event)\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else>\n <!-- Arrays own their truncation and array search behavior in detail-array. -->\n <component\n v-if=\"isArray\"\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"propertyDetailViewMode\"\n :truncate=\"needsTruncation\"\n @updated=\"renderedValue = $event\"></component>\n <!-- Non-array truncated view -->\n <div v-else-if=\"shouldShowTruncated && !isArray\" class=\"relative\">\n <div class=\"text-content-secondary whitespace-pre-wrap break-words font-mono text-sm\">{{truncatedString}}</div>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-merlot-700 hover:text-merlot-900 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show more ({{valueAsString.length}} characters)\n </button>\n </div>\n <!-- Expanded view -->\n <div v-else-if=\"needsTruncation && isValueExpanded\" class=\"relative\">\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"propertyDetailViewMode\"\n @updated=\"renderedValue = $event\"></component>\n <button\n @click=\"toggleValueExpansion\"\n class=\"mt-2 text-merlot-700 hover:text-merlot-900 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5\"\n >\n <svg class=\"w-4 h-4 rotate-180\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n Show less\n </button>\n </div>\n <!-- Full view (no truncation needed) -->\n <div v-else>\n <component\n :is=\"getComponentForPath(path)\"\n :value=\"rawValue\"\n :viewMode=\"propertyDetailViewMode\"\n @updated=\"renderedValue = $event\"></component>\n </div>\n </div>\n </div>\n</div>\n";
|
|
53087
53874
|
|
|
53088
53875
|
/***/ },
|
|
53089
53876
|
|
|
@@ -53127,7 +53914,7 @@ module.exports = "<div>\n <h2>\n Are you sure you want to delete the follo
|
|
|
53127
53914
|
(module) {
|
|
53128
53915
|
|
|
53129
53916
|
"use strict";
|
|
53130
|
-
module.exports = ".document .document-menu {\n display: flex;\n position: sticky;\n top: 0;\n z-index: 100;\n background-color: var(--studio-bg-surface);\n border-radius: 5px;\n padding: 15px 15px;\n margin: -15px 0 15px 0;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.document .document-menu .left {\n flex-grow: 1;\n}\n\n.document .document-menu .right {\n flex-grow: 1;\n text-align: right;\n}\n\n.document .document-menu .right button:not(:last-child) {\n margin-right: 0.5em;\n}\n\n.document button img {\n height: 1em;\n}\n";
|
|
53917
|
+
module.exports = ".document .document-menu {\n display: flex;\n position: sticky;\n top: 0;\n z-index: 100;\n background-color: var(--studio-bg-surface);\n border-radius: 5px;\n padding: 15px 15px;\n margin: -15px 0 15px 0;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.document .document-menu .left {\n flex-grow: 1;\n}\n\n.document .document-menu .right {\n flex-grow: 1;\n text-align: right;\n}\n\n.document .document-menu .right button:not(:last-child) {\n margin-right: 0.5em;\n}\n\n.document button img {\n height: 1em;\n}\n\n/* Add Field Modal Styles */\n.add-field-modal {\n max-width: 500px;\n width: 100%;\n}\n\n.add-field-modal .modal-exit {\n position: absolute;\n top: 15px;\n right: 20px;\n font-size: 24px;\n cursor: pointer;\n color: #6b7280;\n z-index: 10;\n}\n\n.add-field-modal .modal-exit:hover {\n color: #374151;\n}\n\n.add-field-modal form {\n max-height: 70vh;\n overflow-y: auto;\n}\n\n.add-field-modal input[type=\"text\"],\n.add-field-modal input[type=\"email\"],\n.add-field-modal input[type=\"password\"],\n.add-field-modal select,\n.add-field-modal textarea {\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.add-field-modal input:focus,\n.add-field-modal select:focus,\n.add-field-modal textarea:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n\n.add-field-modal .border-red-500 {\n border-color: #ef4444 !important;\n}\n\n.add-field-modal .border-red-500:focus {\n box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important;\n}\n\n/* Ace editor styling in modal */\n.add-field-modal .add-field-ace-editor.ace_editor {\n border: 1px solid #d1d5db;\n border-radius: 0.375rem;\n font-size: 14px;\n min-height: 100px;\n}\n\n.add-field-modal .add-field-ace-editor:focus-within.ace_editor,\n.add-field-modal .add-field-ace-editor.ace_focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n}\n";
|
|
53131
53918
|
|
|
53132
53919
|
/***/ },
|
|
53133
53920
|
|
|
@@ -53138,7 +53925,7 @@ module.exports = ".document .document-menu {\n display: flex;\n position: stic
|
|
|
53138
53925
|
(module) {
|
|
53139
53926
|
|
|
53140
53927
|
"use strict";
|
|
53141
|
-
module.exports = "<div class=\"document px-1 pt-4 pb-16 md:px-0 bg-slate-50 w-full\">\n <div class=\"max-w-7xl mx-auto\">\n <div class=\"flex gap-4 items-center sticky top-0 z-50 bg-surface p-4 border-b border-edge shadow-sm\">\n <div class=\"font-bold overflow-hidden text-ellipsis\">{{model}}: {{documentId}}</div>\n <div class=\"flex grow\">\n <button\n @click=\"viewMode = 'fields'\"\n :class=\"viewMode === 'fields'\n ? 'bg-primary text-white z-10'\n : 'bg-gray-200 text-content-secondary hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-edge-strong border-r-0 rounded-l-lg rounded-r-none\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 10h16M4 14h16M4 18h16\"></path>\n </svg>\n Fields\n </button>\n <button\n @click=\"viewMode = 'json'\"\n :class=\"viewMode === 'json'\n ? 'bg-primary text-white z-10'\n : 'bg-gray-200 text-content-secondary hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-edge-strong rounded-r-lg rounded-l-none\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4\"></path>\n </svg>\n JSON\n </button>\n </div>\n\n <div class=\"hidden md:flex items-center gap-3 text-sm text-slate-600\">\n <div class=\"text-right leading-tight\">\n <div class=\"text-xs uppercase tracking-wide text-slate-400 flex items-center gap-2 justify-end\">\n <span>Loaded at</span>\n <span class=\"inline-flex items-center gap-1 text-[10px] font-semibold\" :class=\"autoRefreshEnabled ? 'text-forest-green-600' : 'text-slate-400'\">\n <span class=\"inline-block h-1.5 w-1.5 rounded-full\" :class=\"autoRefreshEnabled ? 'bg-forest-green-500' : 'bg-slate-300'\"></span>\n </span>\n </div>\n <div class=\"font-medium text-slate-700\">{{lastUpdatedLabel}}</div>\n </div>\n </div>\n\n <div class=\"gap-2 hidden md:flex items-center\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canEdit\"\n :class=\"{'cursor-not-allowed opacity-50': !canEdit}\"\n type=\"button\"\n class=\"rounded-md bg-primary px-2.5 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n\n <!-- 3-dot menu -->\n <div class=\"relative\">\n <button\n @click=\"desktopMenuOpen = !desktopMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-2.5 py-1.5 text-sm font-medium text-content-secondary hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"desktopMenuOpen\"\n aria-label=\"More options\"\n >\n <svg class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"19\" r=\"2\"></circle>\n </svg>\n </button>\n <div\n v-show=\"desktopMenuOpen\"\n @click.away=\"desktopMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-surface ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); desktopMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); desktopMenuOpen = false\"\n :disabled=\"isLambda\"\n type=\"button\"\n :class=\"['flex items-center px-4 py-2 text-sm', isLambda ? 'text-gray-400 cursor-not-allowed' : 'text-content-secondary hover:bg-slate-100']\"\n :title=\"isLambda ? 'Auto-refresh only available on Express deployments' : ''\"\n >\n {{autoRefreshEnabled ? 'Disable Auto-Refresh' : 'Enable Auto-Refresh'}}\n </button>\n <button\n @click=\"addField(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openValidationModal()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Validate\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <div class=\"relative\">\n <button\n @click=\"mobileMenuOpen = !mobileMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-3 py-2 text-sm font-medium text-content-secondary hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"mobileMenuOpen\"\n aria-label=\"Open menu\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M4 6h16M4 12h16M4 18h16\"></path>\n </svg>\n </button>\n <div\n v-show=\"mobileMenuOpen\"\n @click.away=\"mobileMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-52 rounded-md shadow-lg bg-surface ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true; mobileMenuOpen = false\"\n :disabled=\"!canEdit\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canEdit ? 'cursor-not-allowed opacity-50' : 'hover:bg-primary-subtle']\"\n type=\"button\"\n >\n Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false; mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-slate-100\"\n >\n Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-green-100']\"\n @click=\"shouldShowConfirmModal=true; mobileMenuOpen = false\"\n type=\"button\"\n >\n Save\n </button>\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); mobileMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-slate-100\"\n >\n Auto-Refresh {{autoRefreshEnabled ? 'ON' : 'OFF'}}\n </button>\n <button\n @click=\"addField(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openValidationModal()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Validate\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :virtualPaths=\"virtualPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"\n :viewMode=\"viewMode\"\n :model=\"model\"\n @add-field=\"addField\"\n @view-mode-change=\"updateViewMode\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">×</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">×</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowValidationModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowValidationModal = false;\">×</div>\n <div class=\"text-xl font-bold mb-2\">Validate Document</div>\n <div v-if=\"!validationResult\" class=\"space-y-3\">\n <div class=\"h-4 w-32 animate-pulse rounded bg-muted\"></div>\n <div class=\"h-20 w-full animate-pulse rounded bg-muted\"></div>\n </div>\n <div v-else class=\"space-y-3\">\n <div\n class=\"rounded px-3 py-2 text-sm font-semibold\"\n :class=\"validationResult.valid ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'\"\n >\n {{ validationResult.valid ? 'Document is valid' : 'Document is invalid' }}\n </div>\n <div v-if=\"!validationResult.valid\" class=\"rounded border border-edge bg-muted p-3 text-sm text-content-secondary\">\n {{ validationResult.error }}\n </div>\n <div v-if=\"validationResult?.errors && Object.keys(validationResult.errors).length > 0\" class=\"rounded border border-edge\">\n <div class=\"border-b border-edge px-3 py-2 text-sm font-semibold text-content-secondary\">Invalid Paths</div>\n <div class=\"max-h-72 overflow-auto\">\n <div\n v-for=\"[path, pathError] in Object.entries(validationResult.errors)\"\n :key=\"path\"\n class=\"grid gap-2 border-b border-edge px-3 py-2 text-sm last:border-b-0 md:grid-cols-[minmax(160px,220px)_1fr]\"\n >\n <div class=\"font-mono text-content\">{{ path }}</div>\n <div class=\"text-content-secondary\">\n <div>{{ pathError.message }}</div>\n <div v-if=\"pathError.kind\" class=\"mt-1 text-xs text-content-tertiary\">Kind: {{ pathError.kind }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n </div>\n <execute-script\n :visible=\"scriptDrawerOpen\"\n :model=\"model\"\n :document-id=\"documentId\"\n :editting=\"editting\"\n @close=\"closeScriptDrawer\"\n @refresh=\"handleScriptRefresh\"\n ></execute-script>\n <div\n v-if=\"keyboardShortcuts.length > 0\"\n class=\"fixed bottom-4 left-4 z-40 group\"\n >\n <button\n type=\"button\"\n aria-label=\"Keyboard shortcuts\"\n title=\"Keyboard shortcuts\"\n class=\"rounded-full border border-slate-300 bg-surface p-2 text-slate-700 shadow-sm transition-colors hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <rect x=\"2.5\" y=\"6.5\" width=\"19\" height=\"11\" rx=\"2\" stroke-width=\"1.5\"></rect>\n <path d=\"M6 10.5h1.5M9 10.5h1.5M12 10.5h1.5M15 10.5h1.5M18 10.5h.01M6 13.5h1.5M9 13.5h6M16.5 13.5h1.5\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n <div class=\"pointer-events-none absolute left-0 bottom-full mb-2 min-w-[220px] translate-y-1 rounded-lg bg-slate-900 px-3 py-2.5 text-slate-200 opacity-0 shadow-xl transition-all duration-150 group-hover:pointer-events-auto group-hover:translate-y-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-y-0 group-focus-within:opacity-100\">\n <div\n v-for=\"shortcut in keyboardShortcuts\"\n :key=\"shortcut.command + shortcut.description\"\n class=\"flex items-center justify-between gap-3 text-xs\"\n >\n <span class=\"font-mono font-semibold text-slate-50\">{{shortcut.command}}</span>\n <span class=\"text-slate-300\">{{shortcut.description}}</span>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
|
|
53928
|
+
module.exports = "<div class=\"document px-1 pt-4 pb-16 md:px-0 bg-slate-50 w-full\">\n <div class=\"max-w-7xl mx-auto\">\n <div class=\"flex gap-4 items-center sticky top-0 z-50 bg-surface p-4 border-b border-edge shadow-sm\">\n <div class=\"font-bold overflow-hidden text-ellipsis\">{{model}}: {{documentId}}</div>\n <div class=\"flex grow\">\n <button\n @click=\"viewMode = 'fields'\"\n :class=\"viewMode === 'fields'\n ? 'bg-primary text-white z-10'\n : 'bg-gray-200 text-content-secondary hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-edge-strong border-r-0 rounded-l-lg rounded-r-none\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 10h16M4 14h16M4 18h16\"></path>\n </svg>\n Fields\n </button>\n <button\n @click=\"viewMode = 'json'\"\n :class=\"viewMode === 'json'\n ? 'bg-primary text-white z-10'\n : 'bg-gray-200 text-content-secondary hover:bg-gray-300'\"\n class=\"px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-edge-strong rounded-r-lg rounded-l-none\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4\"></path>\n </svg>\n JSON\n </button>\n </div>\n\n <div class=\"hidden md:flex items-center gap-3 text-sm text-slate-600\">\n <div class=\"text-right leading-tight\">\n <div class=\"text-xs uppercase tracking-wide text-slate-400 flex items-center gap-2 justify-end\">\n <span>Loaded at</span>\n <span class=\"inline-flex items-center gap-1 text-[10px] font-semibold\" :class=\"autoRefreshEnabled ? 'text-forest-green-600' : 'text-slate-400'\">\n <span class=\"inline-block h-1.5 w-1.5 rounded-full\" :class=\"autoRefreshEnabled ? 'bg-forest-green-500' : 'bg-slate-300'\"></span>\n </span>\n </div>\n <div class=\"font-medium text-slate-700\">{{lastUpdatedLabel}}</div>\n </div>\n </div>\n\n <div class=\"gap-2 hidden md:flex items-center\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canEdit\"\n :class=\"{'cursor-not-allowed opacity-50': !canEdit}\"\n type=\"button\"\n class=\"rounded-md bg-primary px-2.5 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n × Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n\n <!-- 3-dot menu -->\n <div class=\"relative\">\n <button\n @click=\"desktopMenuOpen = !desktopMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-2.5 py-1.5 text-sm font-medium text-content-secondary hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"desktopMenuOpen\"\n aria-label=\"More options\"\n >\n <svg class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"2\"></circle>\n <circle cx=\"12\" cy=\"19\" r=\"2\"></circle>\n </svg>\n </button>\n <div\n v-show=\"desktopMenuOpen\"\n @click.away=\"desktopMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-surface ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); desktopMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); desktopMenuOpen = false\"\n :disabled=\"isLambda\"\n type=\"button\"\n :class=\"['flex items-center px-4 py-2 text-sm', isLambda ? 'text-gray-400 cursor-not-allowed' : 'text-content-secondary hover:bg-slate-100']\"\n :title=\"isLambda ? 'Auto-refresh only available on Express deployments' : ''\"\n >\n {{autoRefreshEnabled ? 'Disable Auto-Refresh' : 'Enable Auto-Refresh'}}\n </button>\n <button\n @click=\"addField(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); desktopMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openValidationModal()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Validate\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; desktopMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <div class=\"relative\">\n <button\n @click=\"mobileMenuOpen = !mobileMenuOpen\"\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md bg-gray-200 px-3 py-2 text-sm font-medium text-content-secondary hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\"\n aria-expanded=\"mobileMenuOpen\"\n aria-label=\"Open menu\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M4 6h16M4 12h16M4 18h16\"></path>\n </svg>\n </button>\n <div\n v-show=\"mobileMenuOpen\"\n @click.away=\"mobileMenuOpen = false\"\n class=\"origin-top-right absolute right-0 mt-2 w-52 rounded-md shadow-lg bg-surface ring-1 ring-black ring-opacity-5 z-50\"\n >\n <div class=\"py-1 flex flex-col\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true; mobileMenuOpen = false\"\n :disabled=\"!canEdit\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canEdit ? 'cursor-not-allowed opacity-50' : 'hover:bg-primary-subtle']\"\n type=\"button\"\n >\n Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false; mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-slate-100\"\n >\n Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-green-100']\"\n @click=\"shouldShowConfirmModal=true; mobileMenuOpen = false\"\n type=\"button\"\n >\n Save\n </button>\n <button\n @click=\"refreshDocument({ force: true, source: 'manual' }); mobileMenuOpen = false\"\n :disabled=\"isRefreshing\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', isRefreshing ? 'cursor-not-allowed opacity-50' : 'hover:bg-slate-100']\"\n type=\"button\"\n >\n {{isRefreshing ? 'Refreshing...' : 'Refresh'}}\n </button>\n <button\n @click=\"toggleAutoRefresh(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-slate-100\"\n >\n Auto-Refresh {{autoRefreshEnabled ? 'ON' : 'OFF'}}\n </button>\n <button\n @click=\"addField(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Add Field\n </button>\n <button\n @click=\"copyDocument(); mobileMenuOpen = false\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-blue-100\"\n >\n Copy Document\n </button>\n <button\n @click=\"openValidationModal()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-green-100\"\n >\n Validate\n </button>\n <button\n @click=\"openScriptDrawer()\"\n type=\"button\"\n class=\"flex items-center px-4 py-2 text-sm text-content-secondary hover:bg-indigo-100\"\n >\n Run Script\n </button>\n <button\n @click=\"shouldShowDeleteModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-red-100']\"\n type=\"button\"\n >\n Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true; mobileMenuOpen = false\"\n :disabled=\"!canManipulate\"\n :class=\"['flex items-center px-4 py-2 text-sm text-content-secondary', !canManipulate ? 'cursor-not-allowed opacity-50' : 'hover:bg-pink-100']\"\n type=\"button\"\n >\n Clone\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :virtualPaths=\"virtualPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"\n :viewMode=\"viewMode\"\n @view-mode-change=\"updateViewMode\"></document-details>\n <modal v-if=\"showAddFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"closeAddFieldModal\">×</div>\n <div class=\"add-field-modal\">\n <div class=\"mb-6\">\n <h2 class=\"text-xl font-semibold text-content mb-2\">Add New Field</h2>\n <p class=\"text-sm text-gray-600\">Create a new field for this document</p>\n </div>\n\n <form @submit.prevent=\"handleAddFieldSubmit\" class=\"space-y-4\">\n <div>\n <label for=\"fieldName\" class=\"block text-sm font-medium text-content-secondary mb-1\">\n Field Name *\n </label>\n <input\n id=\"fieldName\"\n v-model=\"fieldData.name\"\n type=\"text\"\n required\n placeholder=\"Enter field name...\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.name }\"\n />\n <p v-if=\"fieldErrors.name\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.name}}</p>\n <p v-if=\"fieldData.name && getTransformedFieldName() !== fieldData.name.trim()\" class=\"mt-1 text-sm text-blue-600\">\n Field name will be: <code class=\"bg-blue-50 px-1 py-0.5 rounded text-xs\">{{getTransformedFieldName()}}</code>\n </p>\n </div>\n\n <div>\n <label for=\"fieldType\" class=\"block text-sm font-medium text-content-secondary mb-1\">\n Field Type *\n </label>\n <select\n id=\"fieldType\"\n v-model=\"fieldData.type\"\n required\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.type }\"\n >\n <option value=\"\">Select field type...</option>\n <option v-for=\"type in allFieldTypes\" :key=\"type\" :value=\"type\">\n {{type}}\n </option>\n </select>\n <p v-if=\"fieldErrors.type\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.type}}</p>\n </div>\n\n <div>\n <label for=\"fieldValue\" class=\"block text-sm font-medium text-content-secondary mb-1\">\n Initial Value\n </label>\n\n <custom-date-picker\n v-if=\"shouldUseDatePicker\"\n id=\"fieldValue\"\n mode=\"date\"\n :value=\"fieldData.value\"\n :class=\"{ 'border-red-500': fieldErrors.value, 'max-w-[400px]': true }\"\n :horizontal=\"true\"\n @input=\"fieldData.value = $event\"\n />\n\n <input\n v-else-if=\"!shouldUseAce\"\n v-model=\"fieldData.value\"\n type=\"text\"\n id=\"fieldValue\"\n placeholder=\"Enter initial value (optional)...\"\n class=\"w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <ace-editor\n v-else\n id=\"fieldValue\"\n v-model=\"fieldData.value\"\n mode=\"json\"\n :line-numbers=\"true\"\n :min-lines=\"3\"\n class=\"add-field-ace-editor w-full min-h-[100px]\"\n :class=\"{ 'border-red-500': fieldErrors.value }\"\n />\n\n <p v-if=\"fieldErrors.value\" class=\"mt-1 text-sm text-red-600\">{{fieldErrors.value}}</p>\n <p class=\"mt-1 text-xs text-content-tertiary\">\n <span v-if=\"shouldUseDatePicker\">Select a date or leave empty for null/undefined values.</span>\n <span v-else-if=\"shouldUseAce\">Leave empty for null/undefined values. Use valid JSON format.</span>\n <span v-else>Leave empty for null/undefined values.</span>\n </p>\n </div>\n\n <div class=\"flex justify-end gap-3 pt-4 border-t border-edge\">\n <button\n type=\"button\"\n @click=\"closeAddFieldModal\"\n class=\"px-4 py-2 text-sm font-medium text-content-secondary bg-surface border border-edge-strong rounded-md hover:bg-page focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n :disabled=\"isSubmittingField\"\n class=\"px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <span v-if=\"isSubmittingField\">Adding...</span>\n <span v-else>Add Field</span>\n </button>\n </div>\n </form>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">×</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">×</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">×</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowValidationModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowValidationModal = false;\">×</div>\n <div class=\"text-xl font-bold mb-2\">Validate Document</div>\n <div v-if=\"!validationResult\" class=\"space-y-3\">\n <div class=\"h-4 w-32 animate-pulse rounded bg-muted\"></div>\n <div class=\"h-20 w-full animate-pulse rounded bg-muted\"></div>\n </div>\n <div v-else class=\"space-y-3\">\n <div\n class=\"rounded px-3 py-2 text-sm font-semibold\"\n :class=\"validationResult.valid ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'\"\n >\n {{ validationResult.valid ? 'Document is valid' : 'Document is invalid' }}\n </div>\n <div v-if=\"!validationResult.valid\" class=\"rounded border border-edge bg-muted p-3 text-sm text-content-secondary\">\n {{ validationResult.error }}\n </div>\n <div v-if=\"validationResult?.errors && Object.keys(validationResult.errors).length > 0\" class=\"rounded border border-edge\">\n <div class=\"border-b border-edge px-3 py-2 text-sm font-semibold text-content-secondary\">Invalid Paths</div>\n <div class=\"max-h-72 overflow-auto\">\n <div\n v-for=\"[path, pathError] in Object.entries(validationResult.errors)\"\n :key=\"path\"\n class=\"grid gap-2 border-b border-edge px-3 py-2 text-sm last:border-b-0 md:grid-cols-[minmax(160px,220px)_1fr]\"\n >\n <div class=\"font-mono text-content\">{{ path }}</div>\n <div class=\"text-content-secondary\">\n <div>{{ pathError.message }}</div>\n <div v-if=\"pathError.kind\" class=\"mt-1 text-xs text-content-tertiary\">Kind: {{ pathError.kind }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n </div>\n <execute-script\n :visible=\"scriptDrawerOpen\"\n :model=\"model\"\n :document-id=\"documentId\"\n :editting=\"editting\"\n @close=\"closeScriptDrawer\"\n @refresh=\"handleScriptRefresh\"\n ></execute-script>\n <div\n v-if=\"keyboardShortcuts.length > 0\"\n class=\"fixed bottom-4 left-4 z-40 group\"\n >\n <button\n type=\"button\"\n aria-label=\"Keyboard shortcuts\"\n title=\"Keyboard shortcuts\"\n class=\"rounded-full border border-slate-300 bg-surface p-2 text-slate-700 shadow-sm transition-colors hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <rect x=\"2.5\" y=\"6.5\" width=\"19\" height=\"11\" rx=\"2\" stroke-width=\"1.5\"></rect>\n <path d=\"M6 10.5h1.5M9 10.5h1.5M12 10.5h1.5M15 10.5h1.5M18 10.5h.01M6 13.5h1.5M9 13.5h6M16.5 13.5h1.5\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n <div class=\"pointer-events-none absolute left-0 bottom-full mb-2 min-w-[220px] translate-y-1 rounded-lg bg-slate-900 px-3 py-2.5 text-slate-200 opacity-0 shadow-xl transition-all duration-150 group-hover:pointer-events-auto group-hover:translate-y-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-y-0 group-focus-within:opacity-100\">\n <div\n v-for=\"shortcut in keyboardShortcuts\"\n :key=\"shortcut.command + shortcut.description\"\n class=\"flex items-center justify-between gap-3 text-xs\"\n >\n <span class=\"font-mono font-semibold text-slate-50\">{{shortcut.command}}</span>\n <span class=\"text-slate-300\">{{shortcut.description}}</span>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
|
|
53142
53929
|
|
|
53143
53930
|
/***/ },
|
|
53144
53931
|
|
|
@@ -53193,7 +53980,7 @@ module.exports = "<div class=\"edit-boolean\">\n <div class=\"flex flex-wrap ga
|
|
|
53193
53980
|
(module) {
|
|
53194
53981
|
|
|
53195
53982
|
"use strict";
|
|
53196
|
-
module.exports = "<div>\n <
|
|
53983
|
+
module.exports = "<div>\n <custom-date-picker\n v-if=\"dateSelection == 'picker'\"\n :value=\"value\"\n mode=\"datetime\"\n :horizontal=\"true\"\n class=\"max-w-[420px]\"\n @input=\"$emit('input', $event)\"\n />\n <input\n v-if=\"dateSelection == 'iso'\"\n type=\"text\"\n class=\"w-64 h-8 border border-edge-strong outline-0\"\n :value=\"valueAsISOString\"\n @input=\"updateFromISO\">\n</div>\n";
|
|
53197
53984
|
|
|
53198
53985
|
/***/ },
|
|
53199
53986
|
|
|
@@ -53435,7 +54222,7 @@ module.exports = "<div>\n <div class=\"modal-exit\" @click=\"close\">×</d
|
|
|
53435
54222
|
(module) {
|
|
53436
54223
|
|
|
53437
54224
|
"use strict";
|
|
53438
|
-
module.exports = "<form @submit.prevent=\"emitSearch\" class=\"
|
|
54225
|
+
module.exports = "<div class=\"relative flex-grow m-0\" @focusout=\"onWrapperFocusOut\">\n <form @submit.prevent=\"emitSearch\" class=\"w-full mb-0\">\n <input\n ref=\"searchInput\"\n class=\"w-full font-mono rounded-md p-1 border border-edge-strong outline-edge-strong text-lg focus:ring-1 focus:ring-primary-subtle focus:ring-offset-0 focus:outline-none\"\n type=\"text\"\n placeholder=\"Filter (supports JS, dates, ObjectIds, and more)\"\n v-model=\"searchText\"\n @click=\"initFilter($event); updateAutocomplete()\"\n @input=\"updateAutocomplete\"\n @keyup=\"onSearchKeyup\"\n @keydown=\"handleKeyDown\"\n />\n <div\n v-if=\"autocompleteSuggestions.length\"\n ref=\"autocompleteDropdown\"\n class=\"absolute z-[9999] bg-surface border border-edge-strong rounded mt-1 w-full shadow\"\n >\n <ul class=\"max-h-40 overflow-y-auto\">\n <li\n v-for=\"(suggestion, index) in autocompleteSuggestions\"\n :key=\"suggestion\"\n class=\"px-2 py-1 cursor-pointer\"\n :class=\"{ 'bg-primary-subtle': index === autocompleteIndex }\"\n @mousedown.prevent=\"applySuggestion(index)\"\n >\n {{ suggestion }}\n </li>\n </ul>\n </div>\n </form>\n\n <div\n v-if=\"datePickerContext\"\n class=\"absolute top-full left-0 z-[10000] mt-0 w-[400px] max-w-[calc(100vw-2rem)] bg-surface border border-edge-strong rounded-lg shadow-xl\"\n role=\"dialog\"\n aria-label=\"Date picker\"\n @mousedown.stop\n >\n <div class=\"flex items-center justify-between gap-2 px-3 py-2 border-b border-edge-strong\">\n <span class=\"text-sm font-medium text-content-primary\">Insert date into filter</span>\n <div class=\"flex items-center gap-2 shrink-0\">\n <select\n v-if=\"!datePickerMinimized\"\n v-model=\"dateInsertFormat\"\n class=\"text-xs border border-edge rounded-md bg-surface px-2 py-1\"\n @mousedown.stop\n >\n <option value=\"timestamp\">Milliseconds</option>\n <option value=\"quoted\">Date string</option>\n </select>\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-5 h-5 rounded hover:bg-surface-hover text-content-secondary\"\n :aria-label=\"datePickerMinimized ? 'Expand date picker' : 'Minimize date picker'\"\n @click.stop=\"datePickerMinimized = !datePickerMinimized\"\n >\n <svg v-if=\"datePickerMinimized\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" class=\"w-3.5 h-3.5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m19.5 8.25-7.5 7.5-7.5-7.5\" />\n </svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" class=\"w-3.5 h-3.5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m4.5 15.75 7.5-7.5 7.5 7.5\" />\n </svg>\n </button>\n </div>\n </div>\n <div v-if=\"!datePickerMinimized\" class=\"p-2\">\n <custom-date-picker\n compact\n horizontal\n embedded\n :immediate=\"false\"\n mode=\"datetime\"\n :value=\"datePickerLocalValue\"\n @commit=\"applyDateFromPicker\"\n />\n </div>\n </div>\n</div>\n";
|
|
53439
54226
|
|
|
53440
54227
|
/***/ },
|
|
53441
54228
|
|
|
@@ -59848,7 +60635,7 @@ class Binary extends BSONValue {
|
|
|
59848
60635
|
!Array.isArray(buffer)) {
|
|
59849
60636
|
throw new BSONError('Binary can only be constructed from Uint8Array or number[]');
|
|
59850
60637
|
}
|
|
59851
|
-
this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;
|
|
60638
|
+
this.sub_type = (subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT) & 0xff;
|
|
59852
60639
|
if (buffer == null) {
|
|
59853
60640
|
this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);
|
|
59854
60641
|
this.position = 0;
|
|
@@ -59954,7 +60741,7 @@ class Binary extends BSONValue {
|
|
|
59954
60741
|
if (this.sub_type === Binary.SUBTYPE_UUID) {
|
|
59955
60742
|
return new UUID(this.buffer.subarray(0, this.position));
|
|
59956
60743
|
}
|
|
59957
|
-
throw new BSONError(`Binary sub_type "${this.sub_type}" is not supported for converting to UUID. Only
|
|
60744
|
+
throw new BSONError(`Binary sub_type "${this.sub_type}" (${typeof this.sub_type}) is not supported for converting to UUID. Only 0x${Binary.SUBTYPE_UUID.toString(16).padStart(2, '0')} is currently supported.`);
|
|
59958
60745
|
}
|
|
59959
60746
|
static createFromHexString(hex, subType) {
|
|
59960
60747
|
return new Binary(ByteUtils.fromHex(hex), subType);
|
|
@@ -61743,13 +62530,24 @@ class MinKey extends BSONValue {
|
|
|
61743
62530
|
}
|
|
61744
62531
|
}
|
|
61745
62532
|
|
|
61746
|
-
let PROCESS_UNIQUE = null;
|
|
61747
62533
|
const __idCache = new WeakMap();
|
|
61748
62534
|
class ObjectId extends BSONValue {
|
|
61749
62535
|
get _bsontype() {
|
|
61750
62536
|
return 'ObjectId';
|
|
61751
62537
|
}
|
|
61752
|
-
static index =
|
|
62538
|
+
static index = 0;
|
|
62539
|
+
static PROCESS_UNIQUE = null;
|
|
62540
|
+
static resetState = () => {
|
|
62541
|
+
this.index = Math.floor(Math.random() * 0x1000000);
|
|
62542
|
+
this.PROCESS_UNIQUE = ByteUtils.randomBytes(5);
|
|
62543
|
+
};
|
|
62544
|
+
static {
|
|
62545
|
+
this.resetState();
|
|
62546
|
+
const { startupSnapshot } = globalThis?.process?.getBuiltinModule('v8') ?? {};
|
|
62547
|
+
if (startupSnapshot?.isBuildingSnapshot()) {
|
|
62548
|
+
startupSnapshot?.addDeserializeCallback(this.resetState);
|
|
62549
|
+
}
|
|
62550
|
+
}
|
|
61753
62551
|
static cacheHexString;
|
|
61754
62552
|
buffer;
|
|
61755
62553
|
constructor(inputId) {
|
|
@@ -61826,7 +62624,7 @@ class ObjectId extends BSONValue {
|
|
|
61826
62624
|
return hexString;
|
|
61827
62625
|
}
|
|
61828
62626
|
static getInc() {
|
|
61829
|
-
return (ObjectId.index = (ObjectId.index + 1) %
|
|
62627
|
+
return (ObjectId.index = (ObjectId.index + 1) % 0x1000000);
|
|
61830
62628
|
}
|
|
61831
62629
|
static generate(time) {
|
|
61832
62630
|
if ('number' !== typeof time) {
|
|
@@ -61835,9 +62633,7 @@ class ObjectId extends BSONValue {
|
|
|
61835
62633
|
const inc = ObjectId.getInc();
|
|
61836
62634
|
const buffer = ByteUtils.allocateUnsafe(12);
|
|
61837
62635
|
NumberUtils.setInt32BE(buffer, 0, time);
|
|
61838
|
-
|
|
61839
|
-
PROCESS_UNIQUE = ByteUtils.randomBytes(5);
|
|
61840
|
-
}
|
|
62636
|
+
const PROCESS_UNIQUE = this.PROCESS_UNIQUE;
|
|
61841
62637
|
buffer[4] = PROCESS_UNIQUE[0];
|
|
61842
62638
|
buffer[5] = PROCESS_UNIQUE[1];
|
|
61843
62639
|
buffer[6] = PROCESS_UNIQUE[2];
|
|
@@ -61955,23 +62751,33 @@ class ObjectId extends BSONValue {
|
|
|
61955
62751
|
}
|
|
61956
62752
|
|
|
61957
62753
|
function internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined) {
|
|
61958
|
-
|
|
61959
|
-
|
|
61960
|
-
|
|
61961
|
-
|
|
62754
|
+
const objectStack = [
|
|
62755
|
+
{ obj: object, ignoreUndefined: ignoreUndefined ?? false }
|
|
62756
|
+
];
|
|
62757
|
+
let total = 0;
|
|
62758
|
+
while (objectStack.length > 0) {
|
|
62759
|
+
const { obj, ignoreUndefined: frameIgnoreUndefined } = objectStack.pop();
|
|
62760
|
+
total += 5;
|
|
62761
|
+
const isObjArray = Array.isArray(obj);
|
|
62762
|
+
let target = obj;
|
|
62763
|
+
if (!isObjArray && typeof obj?.toBSON === 'function') {
|
|
62764
|
+
target = obj.toBSON();
|
|
61962
62765
|
}
|
|
61963
|
-
|
|
61964
|
-
|
|
61965
|
-
|
|
61966
|
-
|
|
62766
|
+
if (isObjArray) {
|
|
62767
|
+
const array = target;
|
|
62768
|
+
for (let i = 0; i < array.length; i++) {
|
|
62769
|
+
total += calculateElementSize(i.toString(), array[i], serializeFunctions, true, frameIgnoreUndefined, objectStack);
|
|
62770
|
+
}
|
|
61967
62771
|
}
|
|
61968
|
-
|
|
61969
|
-
|
|
62772
|
+
else {
|
|
62773
|
+
for (const key of Object.keys(target)) {
|
|
62774
|
+
total += calculateElementSize(key, target[key], serializeFunctions, false, frameIgnoreUndefined, objectStack);
|
|
62775
|
+
}
|
|
61970
62776
|
}
|
|
61971
62777
|
}
|
|
61972
|
-
return
|
|
62778
|
+
return total;
|
|
61973
62779
|
}
|
|
61974
|
-
function
|
|
62780
|
+
function calculateElementSize(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false, objectStack) {
|
|
61975
62781
|
if (typeof value?.toBSON === 'function') {
|
|
61976
62782
|
value = value.toBSON();
|
|
61977
62783
|
}
|
|
@@ -61983,21 +62789,21 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
61983
62789
|
value >= JS_INT_MIN &&
|
|
61984
62790
|
value <= JS_INT_MAX) {
|
|
61985
62791
|
if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
|
|
61986
|
-
return
|
|
62792
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (4 + 1);
|
|
61987
62793
|
}
|
|
61988
62794
|
else {
|
|
61989
|
-
return
|
|
62795
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
|
|
61990
62796
|
}
|
|
61991
62797
|
}
|
|
61992
62798
|
else {
|
|
61993
|
-
return
|
|
62799
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
|
|
61994
62800
|
}
|
|
61995
62801
|
case 'undefined':
|
|
61996
62802
|
if (isArray || !ignoreUndefined)
|
|
61997
|
-
return
|
|
62803
|
+
return ByteUtils.utf8ByteLength(name) + 1 + 1;
|
|
61998
62804
|
return 0;
|
|
61999
62805
|
case 'boolean':
|
|
62000
|
-
return
|
|
62806
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (1 + 1);
|
|
62001
62807
|
case 'object':
|
|
62002
62808
|
if (value != null &&
|
|
62003
62809
|
typeof value._bsontype === 'string' &&
|
|
@@ -62005,39 +62811,41 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
62005
62811
|
throw new BSONVersionError();
|
|
62006
62812
|
}
|
|
62007
62813
|
else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
62008
|
-
return
|
|
62814
|
+
return ByteUtils.utf8ByteLength(name) + 1 + 1;
|
|
62009
62815
|
}
|
|
62010
62816
|
else if (value._bsontype === 'ObjectId') {
|
|
62011
|
-
return
|
|
62817
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (12 + 1);
|
|
62012
62818
|
}
|
|
62013
62819
|
else if (value instanceof Date || isDate(value)) {
|
|
62014
|
-
return
|
|
62820
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
|
|
62015
62821
|
}
|
|
62016
62822
|
else if (ArrayBuffer.isView(value) ||
|
|
62017
62823
|
value instanceof ArrayBuffer ||
|
|
62018
62824
|
isAnyArrayBuffer(value)) {
|
|
62019
|
-
return
|
|
62825
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (1 + 4 + 1) + value.byteLength;
|
|
62020
62826
|
}
|
|
62021
62827
|
else if (value._bsontype === 'Long' ||
|
|
62022
62828
|
value._bsontype === 'Double' ||
|
|
62023
62829
|
value._bsontype === 'Timestamp') {
|
|
62024
|
-
return
|
|
62830
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
|
|
62025
62831
|
}
|
|
62026
62832
|
else if (value._bsontype === 'Decimal128') {
|
|
62027
|
-
return
|
|
62833
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (16 + 1);
|
|
62028
62834
|
}
|
|
62029
62835
|
else if (value._bsontype === 'Code') {
|
|
62030
62836
|
if (value.scope != null && Object.keys(value.scope).length > 0) {
|
|
62031
|
-
|
|
62837
|
+
objectStack.push({ obj: value.scope, ignoreUndefined });
|
|
62838
|
+
return (ByteUtils.utf8ByteLength(name) +
|
|
62839
|
+
1 +
|
|
62032
62840
|
1 +
|
|
62033
62841
|
4 +
|
|
62034
62842
|
4 +
|
|
62035
62843
|
ByteUtils.utf8ByteLength(value.code.toString()) +
|
|
62036
|
-
1
|
|
62037
|
-
internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined));
|
|
62844
|
+
1);
|
|
62038
62845
|
}
|
|
62039
62846
|
else {
|
|
62040
|
-
return (
|
|
62847
|
+
return (ByteUtils.utf8ByteLength(name) +
|
|
62848
|
+
1 +
|
|
62041
62849
|
1 +
|
|
62042
62850
|
4 +
|
|
62043
62851
|
ByteUtils.utf8ByteLength(value.code.toString()) +
|
|
@@ -62047,19 +62855,14 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
62047
62855
|
else if (value._bsontype === 'Binary') {
|
|
62048
62856
|
const binary = value;
|
|
62049
62857
|
if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
|
|
62050
|
-
return (
|
|
62051
|
-
(binary.position + 1 + 4 + 1 + 4));
|
|
62858
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1 + 4);
|
|
62052
62859
|
}
|
|
62053
62860
|
else {
|
|
62054
|
-
return
|
|
62861
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (binary.position + 1 + 4 + 1);
|
|
62055
62862
|
}
|
|
62056
62863
|
}
|
|
62057
62864
|
else if (value._bsontype === 'Symbol') {
|
|
62058
|
-
return ((name
|
|
62059
|
-
ByteUtils.utf8ByteLength(value.value) +
|
|
62060
|
-
4 +
|
|
62061
|
-
1 +
|
|
62062
|
-
1);
|
|
62865
|
+
return (ByteUtils.utf8ByteLength(name) + 1 + ByteUtils.utf8ByteLength(value.value) + 4 + 1 + 1);
|
|
62063
62866
|
}
|
|
62064
62867
|
else if (value._bsontype === 'DBRef') {
|
|
62065
62868
|
const ordered_values = Object.assign({
|
|
@@ -62069,12 +62872,12 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
62069
62872
|
if (value.db != null) {
|
|
62070
62873
|
ordered_values['$db'] = value.db;
|
|
62071
62874
|
}
|
|
62072
|
-
|
|
62073
|
-
|
|
62074
|
-
internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined));
|
|
62875
|
+
objectStack.push({ obj: ordered_values, ignoreUndefined: true });
|
|
62876
|
+
return ByteUtils.utf8ByteLength(name) + 1 + 1;
|
|
62075
62877
|
}
|
|
62076
62878
|
else if (value instanceof RegExp || isRegExp(value)) {
|
|
62077
|
-
return (
|
|
62879
|
+
return (ByteUtils.utf8ByteLength(name) +
|
|
62880
|
+
1 +
|
|
62078
62881
|
1 +
|
|
62079
62882
|
ByteUtils.utf8ByteLength(value.source) +
|
|
62080
62883
|
1 +
|
|
@@ -62084,7 +62887,8 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
62084
62887
|
1);
|
|
62085
62888
|
}
|
|
62086
62889
|
else if (value._bsontype === 'BSONRegExp') {
|
|
62087
|
-
return (
|
|
62890
|
+
return (ByteUtils.utf8ByteLength(name) +
|
|
62891
|
+
1 +
|
|
62088
62892
|
1 +
|
|
62089
62893
|
ByteUtils.utf8ByteLength(value.pattern) +
|
|
62090
62894
|
1 +
|
|
@@ -62092,13 +62896,13 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
62092
62896
|
1);
|
|
62093
62897
|
}
|
|
62094
62898
|
else {
|
|
62095
|
-
|
|
62096
|
-
|
|
62097
|
-
1);
|
|
62899
|
+
objectStack.push({ obj: value, ignoreUndefined });
|
|
62900
|
+
return ByteUtils.utf8ByteLength(name) + 1 + 1;
|
|
62098
62901
|
}
|
|
62099
62902
|
case 'function':
|
|
62100
62903
|
if (serializeFunctions) {
|
|
62101
|
-
return (
|
|
62904
|
+
return (ByteUtils.utf8ByteLength(name) +
|
|
62905
|
+
1 +
|
|
62102
62906
|
1 +
|
|
62103
62907
|
4 +
|
|
62104
62908
|
ByteUtils.utf8ByteLength(value.toString()) +
|
|
@@ -62106,7 +62910,7 @@ function calculateElement(name, value, serializeFunctions = false, isArray = fal
|
|
|
62106
62910
|
}
|
|
62107
62911
|
return 0;
|
|
62108
62912
|
case 'bigint':
|
|
62109
|
-
return
|
|
62913
|
+
return ByteUtils.utf8ByteLength(name) + 1 + (8 + 1);
|
|
62110
62914
|
case 'symbol':
|
|
62111
62915
|
return 0;
|
|
62112
62916
|
default:
|
|
@@ -62322,7 +63126,28 @@ function internalDeserialize(buffer, options, isArray) {
|
|
|
62322
63126
|
return deserializeObject(buffer, index, options, isArray);
|
|
62323
63127
|
}
|
|
62324
63128
|
const allowedDBRefKeys = /^\$ref$|^\$id$|^\$db$/;
|
|
63129
|
+
function assignValue(dest, name, value) {
|
|
63130
|
+
if (name === '__proto__') {
|
|
63131
|
+
Object.defineProperty(dest, name, {
|
|
63132
|
+
value,
|
|
63133
|
+
writable: true,
|
|
63134
|
+
enumerable: true,
|
|
63135
|
+
configurable: true
|
|
63136
|
+
});
|
|
63137
|
+
}
|
|
63138
|
+
else {
|
|
63139
|
+
dest[name] = value;
|
|
63140
|
+
}
|
|
63141
|
+
}
|
|
63142
|
+
function toPotentialDbRef(doc) {
|
|
63143
|
+
if (isDBRefLike(doc)) {
|
|
63144
|
+
const { $ref, $id, $db, ...fields } = doc;
|
|
63145
|
+
return new DBRef($ref, $id, $db, fields);
|
|
63146
|
+
}
|
|
63147
|
+
return doc;
|
|
63148
|
+
}
|
|
62325
63149
|
function deserializeObject(buffer, index, options, isArray = false) {
|
|
63150
|
+
options = { ...options };
|
|
62326
63151
|
const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
|
|
62327
63152
|
const raw = options['raw'] == null ? false : options['raw'];
|
|
62328
63153
|
const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
|
|
@@ -62373,31 +63198,87 @@ function deserializeObject(buffer, index, options, isArray = false) {
|
|
|
62373
63198
|
index += 4;
|
|
62374
63199
|
if (size < 5 || size > buffer.length)
|
|
62375
63200
|
throw new BSONError('corrupt bson message');
|
|
62376
|
-
const
|
|
63201
|
+
const rootObject = isArray ? [] : {};
|
|
62377
63202
|
let arrayIndex = 0;
|
|
62378
63203
|
let isPossibleDBRef = isArray ? false : null;
|
|
63204
|
+
let currentFrame = null;
|
|
63205
|
+
let currentDest = rootObject;
|
|
63206
|
+
let currentIsArray = isArray;
|
|
62379
63207
|
while (true) {
|
|
62380
63208
|
const elementType = buffer[index++];
|
|
62381
|
-
if (elementType === 0)
|
|
62382
|
-
|
|
63209
|
+
if (elementType === 0) {
|
|
63210
|
+
if (currentFrame) {
|
|
63211
|
+
if (index === currentFrame.lastIndex) {
|
|
63212
|
+
const completedFrame = currentFrame;
|
|
63213
|
+
currentFrame = completedFrame.prev;
|
|
63214
|
+
if (currentFrame === null) {
|
|
63215
|
+
currentDest = rootObject;
|
|
63216
|
+
currentIsArray = isArray;
|
|
63217
|
+
}
|
|
63218
|
+
else {
|
|
63219
|
+
currentDest = currentFrame.holdingDocument;
|
|
63220
|
+
currentIsArray = currentFrame.isArray;
|
|
63221
|
+
}
|
|
63222
|
+
let result = completedFrame.holdingDocument;
|
|
63223
|
+
switch (completedFrame.elementType) {
|
|
63224
|
+
case BSON_DATA_OBJECT:
|
|
63225
|
+
if (completedFrame.isPossibleDBRef) {
|
|
63226
|
+
result = toPotentialDbRef(result);
|
|
63227
|
+
}
|
|
63228
|
+
break;
|
|
63229
|
+
case BSON_DATA_ARRAY:
|
|
63230
|
+
break;
|
|
63231
|
+
case BSON_DATA_CODE_W_SCOPE:
|
|
63232
|
+
result = new Code(completedFrame.functionString, completedFrame.holdingDocument);
|
|
63233
|
+
break;
|
|
63234
|
+
default:
|
|
63235
|
+
throw new BSONError('Unexpected element type in frame stack');
|
|
63236
|
+
}
|
|
63237
|
+
assignValue(currentDest, completedFrame.propertyName, result);
|
|
63238
|
+
continue;
|
|
63239
|
+
}
|
|
63240
|
+
else {
|
|
63241
|
+
if (currentFrame.elementType === BSON_DATA_ARRAY) {
|
|
63242
|
+
throw new BSONError('corrupted array bson');
|
|
63243
|
+
}
|
|
63244
|
+
throw new BSONError('Bad BSON Document: object not properly terminated');
|
|
63245
|
+
}
|
|
63246
|
+
}
|
|
63247
|
+
else {
|
|
63248
|
+
break;
|
|
63249
|
+
}
|
|
63250
|
+
}
|
|
62383
63251
|
let i = index;
|
|
62384
63252
|
while (buffer[i] !== 0x00 && i < buffer.length) {
|
|
62385
63253
|
i++;
|
|
62386
63254
|
}
|
|
62387
63255
|
if (i >= buffer.byteLength)
|
|
62388
63256
|
throw new BSONError('Bad BSON Document: illegal CString');
|
|
62389
|
-
const name =
|
|
62390
|
-
|
|
62391
|
-
|
|
63257
|
+
const name = currentIsArray
|
|
63258
|
+
? currentFrame !== null
|
|
63259
|
+
? currentFrame.arrayIndex++
|
|
63260
|
+
: arrayIndex++
|
|
63261
|
+
: ByteUtils.toUTF8(buffer, index, i, false);
|
|
63262
|
+
let shouldValidateKey;
|
|
63263
|
+
if (currentFrame !== null) {
|
|
63264
|
+
shouldValidateKey = currentFrame.validationSetting;
|
|
63265
|
+
}
|
|
63266
|
+
else if (globalUTFValidation || utf8KeysSet?.has(name)) {
|
|
62392
63267
|
shouldValidateKey = validationSetting;
|
|
62393
63268
|
}
|
|
62394
63269
|
else {
|
|
62395
63270
|
shouldValidateKey = !validationSetting;
|
|
62396
63271
|
}
|
|
62397
|
-
if (
|
|
63272
|
+
if (currentFrame !== null) {
|
|
63273
|
+
if (currentFrame.isPossibleDBRef !== false && typeof name === 'string' && name[0] === '$') {
|
|
63274
|
+
currentFrame.isPossibleDBRef = allowedDBRefKeys.test(name);
|
|
63275
|
+
}
|
|
63276
|
+
}
|
|
63277
|
+
else if (isPossibleDBRef !== false && name[0] === '$') {
|
|
62398
63278
|
isPossibleDBRef = allowedDBRefKeys.test(name);
|
|
62399
63279
|
}
|
|
62400
63280
|
let value;
|
|
63281
|
+
let isDeferredValue = false;
|
|
62401
63282
|
index = i + 1;
|
|
62402
63283
|
if (elementType === BSON_DATA_STRING) {
|
|
62403
63284
|
const stringSize = NumberUtils.getInt32LE(buffer, index);
|
|
@@ -62443,39 +63324,58 @@ function deserializeObject(buffer, index, options, isArray = false) {
|
|
|
62443
63324
|
value = buffer[index++] === 1;
|
|
62444
63325
|
}
|
|
62445
63326
|
else if (elementType === BSON_DATA_OBJECT) {
|
|
62446
|
-
const _index = index;
|
|
62447
63327
|
const objectSize = NumberUtils.getInt32LE(buffer, index);
|
|
62448
|
-
if (objectSize
|
|
63328
|
+
if (objectSize < 5 || objectSize > buffer.length - index)
|
|
62449
63329
|
throw new BSONError('bad embedded document length in bson');
|
|
62450
|
-
if (raw) {
|
|
63330
|
+
if (raw || (currentFrame?.raw ?? false)) {
|
|
62451
63331
|
value = buffer.subarray(index, index + objectSize);
|
|
63332
|
+
index = index + objectSize;
|
|
62452
63333
|
}
|
|
62453
63334
|
else {
|
|
62454
|
-
|
|
62455
|
-
|
|
62456
|
-
|
|
62457
|
-
|
|
62458
|
-
|
|
63335
|
+
isDeferredValue = true;
|
|
63336
|
+
const objectFrame = {
|
|
63337
|
+
holdingDocument: {},
|
|
63338
|
+
elementType: BSON_DATA_OBJECT,
|
|
63339
|
+
propertyName: name,
|
|
63340
|
+
functionString: null,
|
|
63341
|
+
lastIndex: index + objectSize,
|
|
63342
|
+
isArray: false,
|
|
63343
|
+
arrayIndex: 0,
|
|
63344
|
+
raw: false,
|
|
63345
|
+
isPossibleDBRef: null,
|
|
63346
|
+
validationSetting: shouldValidateKey,
|
|
63347
|
+
prev: currentFrame
|
|
63348
|
+
};
|
|
63349
|
+
currentFrame = objectFrame;
|
|
63350
|
+
currentDest = objectFrame.holdingDocument;
|
|
63351
|
+
currentIsArray = false;
|
|
63352
|
+
index = index + 4;
|
|
62459
63353
|
}
|
|
62460
|
-
index = index + objectSize;
|
|
62461
63354
|
}
|
|
62462
63355
|
else if (elementType === BSON_DATA_ARRAY) {
|
|
62463
|
-
const _index = index;
|
|
62464
63356
|
const objectSize = NumberUtils.getInt32LE(buffer, index);
|
|
62465
|
-
|
|
63357
|
+
if (objectSize < 5 || objectSize > buffer.length - index)
|
|
63358
|
+
throw new BSONError('bad embedded array length in bson');
|
|
62466
63359
|
const stopIndex = index + objectSize;
|
|
62467
|
-
|
|
62468
|
-
|
|
62469
|
-
|
|
62470
|
-
|
|
62471
|
-
|
|
62472
|
-
|
|
62473
|
-
|
|
62474
|
-
|
|
62475
|
-
|
|
62476
|
-
|
|
62477
|
-
|
|
62478
|
-
|
|
63360
|
+
const arrayRaw = !!(fieldsAsRaw && fieldsAsRaw[name]) || (currentFrame?.raw ?? false);
|
|
63361
|
+
isDeferredValue = true;
|
|
63362
|
+
const arrayFrame = {
|
|
63363
|
+
holdingDocument: [],
|
|
63364
|
+
elementType: BSON_DATA_ARRAY,
|
|
63365
|
+
propertyName: name,
|
|
63366
|
+
functionString: null,
|
|
63367
|
+
lastIndex: stopIndex,
|
|
63368
|
+
isArray: true,
|
|
63369
|
+
arrayIndex: 0,
|
|
63370
|
+
raw: arrayRaw,
|
|
63371
|
+
isPossibleDBRef: false,
|
|
63372
|
+
validationSetting: shouldValidateKey,
|
|
63373
|
+
prev: currentFrame
|
|
63374
|
+
};
|
|
63375
|
+
currentFrame = arrayFrame;
|
|
63376
|
+
currentDest = arrayFrame.holdingDocument;
|
|
63377
|
+
currentIsArray = true;
|
|
63378
|
+
index = index + 4;
|
|
62479
63379
|
}
|
|
62480
63380
|
else if (elementType === BSON_DATA_UNDEFINED) {
|
|
62481
63381
|
value = undefined;
|
|
@@ -62647,15 +63547,32 @@ function deserializeObject(buffer, index, options, isArray = false) {
|
|
|
62647
63547
|
index = index + stringSize;
|
|
62648
63548
|
const _index = index;
|
|
62649
63549
|
const objectSize = NumberUtils.getInt32LE(buffer, index);
|
|
62650
|
-
|
|
62651
|
-
|
|
63550
|
+
if (objectSize < 5 || objectSize > buffer.length - index)
|
|
63551
|
+
throw new BSONError('bad scope document size in code_w_scope');
|
|
62652
63552
|
if (totalSize < 4 + 4 + objectSize + stringSize) {
|
|
62653
63553
|
throw new BSONError('code_w_scope total size is too short, truncating scope');
|
|
62654
63554
|
}
|
|
62655
63555
|
if (totalSize > 4 + 4 + objectSize + stringSize) {
|
|
62656
63556
|
throw new BSONError('code_w_scope total size is too long, clips outer document');
|
|
62657
63557
|
}
|
|
62658
|
-
|
|
63558
|
+
isDeferredValue = true;
|
|
63559
|
+
const scopeFrame = {
|
|
63560
|
+
holdingDocument: {},
|
|
63561
|
+
elementType: BSON_DATA_CODE_W_SCOPE,
|
|
63562
|
+
propertyName: name,
|
|
63563
|
+
functionString: functionString,
|
|
63564
|
+
lastIndex: _index + objectSize,
|
|
63565
|
+
isArray: false,
|
|
63566
|
+
arrayIndex: 0,
|
|
63567
|
+
raw: false,
|
|
63568
|
+
isPossibleDBRef: null,
|
|
63569
|
+
validationSetting: shouldValidateKey,
|
|
63570
|
+
prev: currentFrame
|
|
63571
|
+
};
|
|
63572
|
+
currentFrame = scopeFrame;
|
|
63573
|
+
currentDest = scopeFrame.holdingDocument;
|
|
63574
|
+
currentIsArray = false;
|
|
63575
|
+
index = index + 4;
|
|
62659
63576
|
}
|
|
62660
63577
|
else if (elementType === BSON_DATA_DBPOINTER) {
|
|
62661
63578
|
const stringSize = NumberUtils.getInt32LE(buffer, index);
|
|
@@ -62676,18 +63593,14 @@ function deserializeObject(buffer, index, options, isArray = false) {
|
|
|
62676
63593
|
else {
|
|
62677
63594
|
throw new BSONError(`Detected unknown BSON type ${elementType.toString(16)} for fieldname "${name}"`);
|
|
62678
63595
|
}
|
|
62679
|
-
if (
|
|
62680
|
-
|
|
62681
|
-
value,
|
|
62682
|
-
writable: true,
|
|
62683
|
-
enumerable: true,
|
|
62684
|
-
configurable: true
|
|
62685
|
-
});
|
|
62686
|
-
}
|
|
62687
|
-
else {
|
|
62688
|
-
object[name] = value;
|
|
63596
|
+
if (!isDeferredValue) {
|
|
63597
|
+
assignValue(currentDest, name, value);
|
|
62689
63598
|
}
|
|
62690
63599
|
}
|
|
63600
|
+
if (currentFrame !== null) {
|
|
63601
|
+
throw new BSONError('corrupted bson, more objects expected based on the current document size');
|
|
63602
|
+
}
|
|
63603
|
+
const object = rootObject;
|
|
62691
63604
|
if (size !== index - startIndex) {
|
|
62692
63605
|
if (isArray)
|
|
62693
63606
|
throw new BSONError('corrupt array bson');
|
|
@@ -62695,14 +63608,7 @@ function deserializeObject(buffer, index, options, isArray = false) {
|
|
|
62695
63608
|
}
|
|
62696
63609
|
if (!isPossibleDBRef)
|
|
62697
63610
|
return object;
|
|
62698
|
-
|
|
62699
|
-
const copy = Object.assign({}, object);
|
|
62700
|
-
delete copy.$ref;
|
|
62701
|
-
delete copy.$id;
|
|
62702
|
-
delete copy.$db;
|
|
62703
|
-
return new DBRef(object.$ref, object.$id, object.$db, copy);
|
|
62704
|
-
}
|
|
62705
|
-
return object;
|
|
63611
|
+
return toPotentialDbRef(object);
|
|
62706
63612
|
}
|
|
62707
63613
|
|
|
62708
63614
|
const regexp = /\x00/;
|
|
@@ -62811,7 +63717,7 @@ function serializeMinMax(buffer, key, value, index) {
|
|
|
62811
63717
|
if (value === null) {
|
|
62812
63718
|
buffer[index++] = BSON_DATA_NULL;
|
|
62813
63719
|
}
|
|
62814
|
-
else if (value
|
|
63720
|
+
else if (value[bsonType] === 'MinKey') {
|
|
62815
63721
|
buffer[index++] = BSON_DATA_MIN_KEY;
|
|
62816
63722
|
}
|
|
62817
63723
|
else {
|
|
@@ -62848,19 +63754,6 @@ function serializeBuffer(buffer, key, value, index) {
|
|
|
62848
63754
|
index = index + size;
|
|
62849
63755
|
return index;
|
|
62850
63756
|
}
|
|
62851
|
-
function serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path) {
|
|
62852
|
-
if (path.has(value)) {
|
|
62853
|
-
throw new BSONError('Cannot convert circular structure to BSON');
|
|
62854
|
-
}
|
|
62855
|
-
path.add(value);
|
|
62856
|
-
buffer[index++] = Array.isArray(value) ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
|
|
62857
|
-
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
62858
|
-
index = index + numberOfWrittenBytes;
|
|
62859
|
-
buffer[index++] = 0;
|
|
62860
|
-
const endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
|
|
62861
|
-
path.delete(value);
|
|
62862
|
-
return endIndex;
|
|
62863
|
-
}
|
|
62864
63757
|
function serializeDecimal128(buffer, key, value, index) {
|
|
62865
63758
|
buffer[index++] = BSON_DATA_DECIMAL128;
|
|
62866
63759
|
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
@@ -62872,7 +63765,7 @@ function serializeDecimal128(buffer, key, value, index) {
|
|
|
62872
63765
|
}
|
|
62873
63766
|
function serializeLong(buffer, key, value, index) {
|
|
62874
63767
|
buffer[index++] =
|
|
62875
|
-
value
|
|
63768
|
+
value[bsonType] === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
|
|
62876
63769
|
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
62877
63770
|
index = index + numberOfWrittenBytes;
|
|
62878
63771
|
buffer[index++] = 0;
|
|
@@ -62911,38 +63804,6 @@ function serializeFunction(buffer, key, value, index) {
|
|
|
62911
63804
|
buffer[index++] = 0;
|
|
62912
63805
|
return index;
|
|
62913
63806
|
}
|
|
62914
|
-
function serializeCode(buffer, key, value, index, checkKeys = false, depth = 0, serializeFunctions = false, ignoreUndefined = true, path) {
|
|
62915
|
-
if (value.scope && typeof value.scope === 'object') {
|
|
62916
|
-
buffer[index++] = BSON_DATA_CODE_W_SCOPE;
|
|
62917
|
-
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
62918
|
-
index = index + numberOfWrittenBytes;
|
|
62919
|
-
buffer[index++] = 0;
|
|
62920
|
-
let startIndex = index;
|
|
62921
|
-
const functionString = value.code;
|
|
62922
|
-
index = index + 4;
|
|
62923
|
-
const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
62924
|
-
NumberUtils.setInt32LE(buffer, index, codeSize);
|
|
62925
|
-
buffer[index + 4 + codeSize - 1] = 0;
|
|
62926
|
-
index = index + codeSize + 4;
|
|
62927
|
-
const endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
|
|
62928
|
-
index = endIndex - 1;
|
|
62929
|
-
const totalSize = endIndex - startIndex;
|
|
62930
|
-
startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
|
|
62931
|
-
buffer[index++] = 0;
|
|
62932
|
-
}
|
|
62933
|
-
else {
|
|
62934
|
-
buffer[index++] = BSON_DATA_CODE;
|
|
62935
|
-
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
62936
|
-
index = index + numberOfWrittenBytes;
|
|
62937
|
-
buffer[index++] = 0;
|
|
62938
|
-
const functionString = value.code.toString();
|
|
62939
|
-
const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
62940
|
-
NumberUtils.setInt32LE(buffer, index, size);
|
|
62941
|
-
index = index + 4 + size - 1;
|
|
62942
|
-
buffer[index++] = 0;
|
|
62943
|
-
}
|
|
62944
|
-
return index;
|
|
62945
|
-
}
|
|
62946
63807
|
function serializeBinary(buffer, key, value, index) {
|
|
62947
63808
|
buffer[index++] = BSON_DATA_BINARY;
|
|
62948
63809
|
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
@@ -62982,26 +63843,59 @@ function serializeSymbol(buffer, key, value, index) {
|
|
|
62982
63843
|
buffer[index++] = 0;
|
|
62983
63844
|
return index;
|
|
62984
63845
|
}
|
|
62985
|
-
function
|
|
62986
|
-
|
|
62987
|
-
|
|
62988
|
-
|
|
62989
|
-
|
|
62990
|
-
|
|
62991
|
-
|
|
62992
|
-
|
|
62993
|
-
|
|
62994
|
-
|
|
62995
|
-
|
|
62996
|
-
|
|
63846
|
+
function makeFrame(sourceObject, objectSizeIndex, codeSizeIndex, prev, checkKeys, ignoreUndefined) {
|
|
63847
|
+
if (Array.isArray(sourceObject)) {
|
|
63848
|
+
return {
|
|
63849
|
+
sourceObject,
|
|
63850
|
+
isArray: true,
|
|
63851
|
+
objectSizeIndex,
|
|
63852
|
+
codeSizeIndex,
|
|
63853
|
+
iterTarget: sourceObject,
|
|
63854
|
+
keys: null,
|
|
63855
|
+
keyIndex: 0,
|
|
63856
|
+
mapIterator: null,
|
|
63857
|
+
prev,
|
|
63858
|
+
checkKeys,
|
|
63859
|
+
ignoreUndefined
|
|
63860
|
+
};
|
|
62997
63861
|
}
|
|
62998
|
-
|
|
62999
|
-
|
|
63000
|
-
|
|
63001
|
-
|
|
63002
|
-
|
|
63003
|
-
|
|
63004
|
-
|
|
63862
|
+
if (sourceObject instanceof Map || isMap(sourceObject)) {
|
|
63863
|
+
return {
|
|
63864
|
+
sourceObject,
|
|
63865
|
+
isArray: false,
|
|
63866
|
+
objectSizeIndex,
|
|
63867
|
+
codeSizeIndex,
|
|
63868
|
+
iterTarget: sourceObject,
|
|
63869
|
+
keys: null,
|
|
63870
|
+
keyIndex: 0,
|
|
63871
|
+
mapIterator: sourceObject.entries(),
|
|
63872
|
+
prev,
|
|
63873
|
+
checkKeys,
|
|
63874
|
+
ignoreUndefined
|
|
63875
|
+
};
|
|
63876
|
+
}
|
|
63877
|
+
let target = sourceObject;
|
|
63878
|
+
if (typeof target?.toBSON === 'function') {
|
|
63879
|
+
target = target.toBSON();
|
|
63880
|
+
if (target != null && typeof target !== 'object') {
|
|
63881
|
+
throw new BSONError('toBSON function did not return an object');
|
|
63882
|
+
}
|
|
63883
|
+
}
|
|
63884
|
+
return {
|
|
63885
|
+
sourceObject,
|
|
63886
|
+
isArray: false,
|
|
63887
|
+
objectSizeIndex,
|
|
63888
|
+
codeSizeIndex,
|
|
63889
|
+
iterTarget: target,
|
|
63890
|
+
keys: Object.keys(target),
|
|
63891
|
+
keyIndex: 0,
|
|
63892
|
+
mapIterator: null,
|
|
63893
|
+
prev,
|
|
63894
|
+
checkKeys,
|
|
63895
|
+
ignoreUndefined
|
|
63896
|
+
};
|
|
63897
|
+
}
|
|
63898
|
+
function serializeInto(buffer, object, checkKeys, startingIndex, serializeFunctions, ignoreUndefined, path) {
|
|
63005
63899
|
if (path == null) {
|
|
63006
63900
|
if (object == null) {
|
|
63007
63901
|
buffer[0] = 0x05;
|
|
@@ -63029,308 +63923,200 @@ function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializ
|
|
|
63029
63923
|
path = new Set();
|
|
63030
63924
|
}
|
|
63031
63925
|
path.add(object);
|
|
63926
|
+
let currentFrame = makeFrame(object, startingIndex, null, null, checkKeys, ignoreUndefined);
|
|
63032
63927
|
let index = startingIndex + 4;
|
|
63033
|
-
|
|
63034
|
-
|
|
63035
|
-
|
|
63036
|
-
|
|
63037
|
-
|
|
63038
|
-
|
|
63039
|
-
|
|
63040
|
-
|
|
63041
|
-
|
|
63042
|
-
|
|
63043
|
-
|
|
63044
|
-
else if (value === null) {
|
|
63045
|
-
index = serializeNull(buffer, key, value, index);
|
|
63046
|
-
}
|
|
63047
|
-
else if (type === 'string') {
|
|
63048
|
-
index = serializeString(buffer, key, value, index);
|
|
63049
|
-
}
|
|
63050
|
-
else if (type === 'number') {
|
|
63051
|
-
index = serializeNumber(buffer, key, value, index);
|
|
63052
|
-
}
|
|
63053
|
-
else if (type === 'bigint') {
|
|
63054
|
-
index = serializeBigInt(buffer, key, value, index);
|
|
63055
|
-
}
|
|
63056
|
-
else if (type === 'boolean') {
|
|
63057
|
-
index = serializeBoolean(buffer, key, value, index);
|
|
63058
|
-
}
|
|
63059
|
-
else if (type === 'object' && value._bsontype == null) {
|
|
63060
|
-
if (value instanceof Date || isDate(value)) {
|
|
63061
|
-
index = serializeDate(buffer, key, value, index);
|
|
63062
|
-
}
|
|
63063
|
-
else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
63064
|
-
index = serializeBuffer(buffer, key, value, index);
|
|
63065
|
-
}
|
|
63066
|
-
else if (value instanceof RegExp || isRegExp(value)) {
|
|
63067
|
-
index = serializeRegExp(buffer, key, value, index);
|
|
63068
|
-
}
|
|
63069
|
-
else {
|
|
63070
|
-
index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
|
|
63928
|
+
while (currentFrame !== null) {
|
|
63929
|
+
const frame = currentFrame;
|
|
63930
|
+
let key;
|
|
63931
|
+
let value;
|
|
63932
|
+
if (frame.mapIterator !== null) {
|
|
63933
|
+
const next = frame.mapIterator.next();
|
|
63934
|
+
if (next.done) {
|
|
63935
|
+
buffer[index++] = 0x00;
|
|
63936
|
+
NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
|
|
63937
|
+
if (frame.codeSizeIndex !== null) {
|
|
63938
|
+
NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
|
|
63071
63939
|
}
|
|
63940
|
+
path.delete(frame.sourceObject);
|
|
63941
|
+
currentFrame = frame.prev;
|
|
63942
|
+
continue;
|
|
63072
63943
|
}
|
|
63073
|
-
|
|
63074
|
-
|
|
63075
|
-
|
|
63076
|
-
|
|
63077
|
-
|
|
63078
|
-
|
|
63079
|
-
|
|
63080
|
-
|
|
63081
|
-
|
|
63082
|
-
}
|
|
63083
|
-
else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
|
|
63084
|
-
index = serializeLong(buffer, key, value, index);
|
|
63085
|
-
}
|
|
63086
|
-
else if (value._bsontype === 'Double') {
|
|
63087
|
-
index = serializeDouble(buffer, key, value, index);
|
|
63088
|
-
}
|
|
63089
|
-
else if (value._bsontype === 'Code') {
|
|
63090
|
-
index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
|
|
63091
|
-
}
|
|
63092
|
-
else if (value._bsontype === 'Binary') {
|
|
63093
|
-
index = serializeBinary(buffer, key, value, index);
|
|
63094
|
-
}
|
|
63095
|
-
else if (value._bsontype === 'BSONSymbol') {
|
|
63096
|
-
index = serializeSymbol(buffer, key, value, index);
|
|
63097
|
-
}
|
|
63098
|
-
else if (value._bsontype === 'DBRef') {
|
|
63099
|
-
index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
|
|
63100
|
-
}
|
|
63101
|
-
else if (value._bsontype === 'BSONRegExp') {
|
|
63102
|
-
index = serializeBSONRegExp(buffer, key, value, index);
|
|
63103
|
-
}
|
|
63104
|
-
else if (value._bsontype === 'Int32') {
|
|
63105
|
-
index = serializeInt32(buffer, key, value, index);
|
|
63106
|
-
}
|
|
63107
|
-
else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
63108
|
-
index = serializeMinMax(buffer, key, value, index);
|
|
63109
|
-
}
|
|
63110
|
-
else if (typeof value._bsontype !== 'undefined') {
|
|
63111
|
-
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
63944
|
+
key = next.value[0];
|
|
63945
|
+
value = next.value[1];
|
|
63946
|
+
}
|
|
63947
|
+
else if (frame.keys !== null) {
|
|
63948
|
+
if (frame.keyIndex >= frame.keys.length) {
|
|
63949
|
+
buffer[index++] = 0x00;
|
|
63950
|
+
NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
|
|
63951
|
+
if (frame.codeSizeIndex !== null) {
|
|
63952
|
+
NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
|
|
63112
63953
|
}
|
|
63954
|
+
path.delete(frame.sourceObject);
|
|
63955
|
+
currentFrame = frame.prev;
|
|
63956
|
+
continue;
|
|
63113
63957
|
}
|
|
63114
|
-
|
|
63115
|
-
|
|
63116
|
-
}
|
|
63958
|
+
key = frame.keys[frame.keyIndex++];
|
|
63959
|
+
value = frame.iterTarget[key];
|
|
63117
63960
|
}
|
|
63118
|
-
|
|
63119
|
-
|
|
63120
|
-
|
|
63121
|
-
|
|
63122
|
-
|
|
63123
|
-
|
|
63124
|
-
|
|
63125
|
-
if (done)
|
|
63126
|
-
continue;
|
|
63127
|
-
const key = entry.value ? entry.value[0] : undefined;
|
|
63128
|
-
let value = entry.value ? entry.value[1] : undefined;
|
|
63129
|
-
if (typeof value?.toBSON === 'function') {
|
|
63130
|
-
value = value.toBSON();
|
|
63131
|
-
}
|
|
63132
|
-
const type = typeof value;
|
|
63133
|
-
if (typeof key === 'string' && !ignoreKeys.has(key)) {
|
|
63134
|
-
if (key.match(regexp) != null) {
|
|
63135
|
-
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
63136
|
-
}
|
|
63137
|
-
if (checkKeys) {
|
|
63138
|
-
if ('$' === key[0]) {
|
|
63139
|
-
throw new BSONError('key ' + key + " must not start with '$'");
|
|
63140
|
-
}
|
|
63141
|
-
else if (key.includes('.')) {
|
|
63142
|
-
throw new BSONError('key ' + key + " must not contain '.'");
|
|
63143
|
-
}
|
|
63961
|
+
else {
|
|
63962
|
+
const arr = frame.iterTarget;
|
|
63963
|
+
if (frame.keyIndex >= arr.length) {
|
|
63964
|
+
buffer[index++] = 0x00;
|
|
63965
|
+
NumberUtils.setInt32LE(buffer, frame.objectSizeIndex, index - frame.objectSizeIndex);
|
|
63966
|
+
if (frame.codeSizeIndex !== null) {
|
|
63967
|
+
NumberUtils.setInt32LE(buffer, frame.codeSizeIndex, index - frame.codeSizeIndex);
|
|
63144
63968
|
}
|
|
63969
|
+
path.delete(frame.sourceObject);
|
|
63970
|
+
currentFrame = frame.prev;
|
|
63971
|
+
continue;
|
|
63145
63972
|
}
|
|
63146
|
-
|
|
63147
|
-
|
|
63148
|
-
|
|
63149
|
-
|
|
63150
|
-
|
|
63151
|
-
|
|
63973
|
+
const i = frame.keyIndex++;
|
|
63974
|
+
key = String(i);
|
|
63975
|
+
value = arr[i];
|
|
63976
|
+
}
|
|
63977
|
+
if (typeof value?.toBSON === 'function') {
|
|
63978
|
+
value = value.toBSON();
|
|
63979
|
+
}
|
|
63980
|
+
if (!frame.isArray && typeof key === 'string' && !(key[0] === '$' && ignoreKeys.has(key))) {
|
|
63981
|
+
if (regexp.test(key)) {
|
|
63982
|
+
throw new BSONError('key ' + key + ' must not contain null bytes');
|
|
63152
63983
|
}
|
|
63153
|
-
|
|
63154
|
-
|
|
63984
|
+
if (frame.checkKeys) {
|
|
63985
|
+
if ('$' === key[0]) {
|
|
63986
|
+
throw new BSONError('key ' + key + " must not start with '$'");
|
|
63987
|
+
}
|
|
63988
|
+
else if (key.includes('.')) {
|
|
63989
|
+
throw new BSONError('key ' + key + " must not contain '.'");
|
|
63990
|
+
}
|
|
63155
63991
|
}
|
|
63156
|
-
|
|
63157
|
-
|
|
63992
|
+
}
|
|
63993
|
+
const type = typeof value;
|
|
63994
|
+
if (value === undefined) {
|
|
63995
|
+
if (frame.isArray || frame.ignoreUndefined === false) {
|
|
63996
|
+
index = serializeNull(buffer, key, value, index);
|
|
63158
63997
|
}
|
|
63159
|
-
|
|
63160
|
-
|
|
63998
|
+
}
|
|
63999
|
+
else if (value === null) {
|
|
64000
|
+
index = serializeNull(buffer, key, value, index);
|
|
64001
|
+
}
|
|
64002
|
+
else if (type === 'string') {
|
|
64003
|
+
index = serializeString(buffer, key, value, index);
|
|
64004
|
+
}
|
|
64005
|
+
else if (type === 'number') {
|
|
64006
|
+
index = serializeNumber(buffer, key, value, index);
|
|
64007
|
+
}
|
|
64008
|
+
else if (type === 'bigint') {
|
|
64009
|
+
index = serializeBigInt(buffer, key, value, index);
|
|
64010
|
+
}
|
|
64011
|
+
else if (type === 'boolean') {
|
|
64012
|
+
index = serializeBoolean(buffer, key, value, index);
|
|
64013
|
+
}
|
|
64014
|
+
else if (type === 'object' && value._bsontype == null) {
|
|
64015
|
+
if (value instanceof Date || isDate(value)) {
|
|
64016
|
+
index = serializeDate(buffer, key, value, index);
|
|
63161
64017
|
}
|
|
63162
|
-
else if (
|
|
63163
|
-
index =
|
|
64018
|
+
else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
64019
|
+
index = serializeBuffer(buffer, key, value, index);
|
|
63164
64020
|
}
|
|
63165
|
-
else if (
|
|
63166
|
-
|
|
63167
|
-
index = serializeDate(buffer, key, value, index);
|
|
63168
|
-
}
|
|
63169
|
-
else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
63170
|
-
index = serializeBuffer(buffer, key, value, index);
|
|
63171
|
-
}
|
|
63172
|
-
else if (value instanceof RegExp || isRegExp(value)) {
|
|
63173
|
-
index = serializeRegExp(buffer, key, value, index);
|
|
63174
|
-
}
|
|
63175
|
-
else {
|
|
63176
|
-
index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
|
|
63177
|
-
}
|
|
64021
|
+
else if (value instanceof RegExp || isRegExp(value)) {
|
|
64022
|
+
index = serializeRegExp(buffer, key, value, index);
|
|
63178
64023
|
}
|
|
63179
|
-
else
|
|
63180
|
-
if (value
|
|
63181
|
-
throw new
|
|
63182
|
-
}
|
|
63183
|
-
else if (value._bsontype === 'ObjectId') {
|
|
63184
|
-
index = serializeObjectId(buffer, key, value, index);
|
|
63185
|
-
}
|
|
63186
|
-
else if (value._bsontype === 'Decimal128') {
|
|
63187
|
-
index = serializeDecimal128(buffer, key, value, index);
|
|
63188
|
-
}
|
|
63189
|
-
else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
|
|
63190
|
-
index = serializeLong(buffer, key, value, index);
|
|
63191
|
-
}
|
|
63192
|
-
else if (value._bsontype === 'Double') {
|
|
63193
|
-
index = serializeDouble(buffer, key, value, index);
|
|
63194
|
-
}
|
|
63195
|
-
else if (value._bsontype === 'Code') {
|
|
63196
|
-
index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
|
|
63197
|
-
}
|
|
63198
|
-
else if (value._bsontype === 'Binary') {
|
|
63199
|
-
index = serializeBinary(buffer, key, value, index);
|
|
63200
|
-
}
|
|
63201
|
-
else if (value._bsontype === 'BSONSymbol') {
|
|
63202
|
-
index = serializeSymbol(buffer, key, value, index);
|
|
63203
|
-
}
|
|
63204
|
-
else if (value._bsontype === 'DBRef') {
|
|
63205
|
-
index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
|
|
63206
|
-
}
|
|
63207
|
-
else if (value._bsontype === 'BSONRegExp') {
|
|
63208
|
-
index = serializeBSONRegExp(buffer, key, value, index);
|
|
63209
|
-
}
|
|
63210
|
-
else if (value._bsontype === 'Int32') {
|
|
63211
|
-
index = serializeInt32(buffer, key, value, index);
|
|
63212
|
-
}
|
|
63213
|
-
else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
63214
|
-
index = serializeMinMax(buffer, key, value, index);
|
|
63215
|
-
}
|
|
63216
|
-
else if (typeof value._bsontype !== 'undefined') {
|
|
63217
|
-
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
64024
|
+
else {
|
|
64025
|
+
if (path.has(value)) {
|
|
64026
|
+
throw new BSONError('Cannot convert circular structure to BSON');
|
|
63218
64027
|
}
|
|
63219
|
-
|
|
63220
|
-
|
|
63221
|
-
index
|
|
63222
|
-
|
|
63223
|
-
|
|
63224
|
-
|
|
63225
|
-
|
|
63226
|
-
|
|
63227
|
-
object = object.toBSON();
|
|
63228
|
-
if (object != null && typeof object !== 'object') {
|
|
63229
|
-
throw new BSONError('toBSON function did not return an object');
|
|
64028
|
+
const nestedIsArray = Array.isArray(value);
|
|
64029
|
+
buffer[index++] = nestedIsArray ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
|
|
64030
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
64031
|
+
buffer[index++] = 0x00;
|
|
64032
|
+
const nestedStartIndex = index;
|
|
64033
|
+
path.add(value);
|
|
64034
|
+
currentFrame = makeFrame(value, nestedStartIndex, null, frame, frame.checkKeys, frame.ignoreUndefined);
|
|
64035
|
+
index += 4;
|
|
63230
64036
|
}
|
|
63231
64037
|
}
|
|
63232
|
-
|
|
63233
|
-
|
|
63234
|
-
|
|
63235
|
-
value = value.toBSON();
|
|
64038
|
+
else if (type === 'object') {
|
|
64039
|
+
if (value[BSON_VERSION_SYMBOL] !== BSON_MAJOR_VERSION) {
|
|
64040
|
+
throw new BSONVersionError();
|
|
63236
64041
|
}
|
|
63237
|
-
const
|
|
63238
|
-
if (
|
|
63239
|
-
|
|
63240
|
-
|
|
63241
|
-
|
|
63242
|
-
|
|
63243
|
-
|
|
63244
|
-
|
|
63245
|
-
|
|
63246
|
-
|
|
63247
|
-
|
|
64042
|
+
const tag = value[bsonType];
|
|
64043
|
+
if (tag === 'ObjectId') {
|
|
64044
|
+
index = serializeObjectId(buffer, key, value, index);
|
|
64045
|
+
}
|
|
64046
|
+
else if (tag === 'Decimal128') {
|
|
64047
|
+
index = serializeDecimal128(buffer, key, value, index);
|
|
64048
|
+
}
|
|
64049
|
+
else if (tag === 'Long' || tag === 'Timestamp') {
|
|
64050
|
+
index = serializeLong(buffer, key, value, index);
|
|
64051
|
+
}
|
|
64052
|
+
else if (tag === 'Double') {
|
|
64053
|
+
index = serializeDouble(buffer, key, value, index);
|
|
64054
|
+
}
|
|
64055
|
+
else if (tag === 'Code') {
|
|
64056
|
+
const codeValue = value;
|
|
64057
|
+
if (codeValue.scope && typeof codeValue.scope === 'object') {
|
|
64058
|
+
buffer[index++] = BSON_DATA_CODE_W_SCOPE;
|
|
64059
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
64060
|
+
buffer[index++] = 0x00;
|
|
64061
|
+
const codeTotalSizeIndex = index;
|
|
64062
|
+
index += 4;
|
|
64063
|
+
const functionString = codeValue.code;
|
|
64064
|
+
const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
64065
|
+
NumberUtils.setInt32LE(buffer, index, codeSize);
|
|
64066
|
+
buffer[index + 4 + codeSize - 1] = 0;
|
|
64067
|
+
index = index + codeSize + 4;
|
|
64068
|
+
const scope = codeValue.scope;
|
|
64069
|
+
if (path.has(scope)) {
|
|
64070
|
+
throw new BSONError('Cannot convert circular structure to BSON');
|
|
63248
64071
|
}
|
|
64072
|
+
path.add(scope);
|
|
64073
|
+
currentFrame = makeFrame(scope, index, codeTotalSizeIndex, frame, frame.checkKeys, frame.ignoreUndefined);
|
|
64074
|
+
index += 4;
|
|
64075
|
+
}
|
|
64076
|
+
else {
|
|
64077
|
+
buffer[index++] = BSON_DATA_CODE;
|
|
64078
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
64079
|
+
buffer[index++] = 0x00;
|
|
64080
|
+
const functionString = codeValue.code.toString();
|
|
64081
|
+
const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
64082
|
+
NumberUtils.setInt32LE(buffer, index, size);
|
|
64083
|
+
index = index + 4 + size - 1;
|
|
64084
|
+
buffer[index++] = 0;
|
|
63249
64085
|
}
|
|
63250
64086
|
}
|
|
63251
|
-
if (
|
|
63252
|
-
|
|
63253
|
-
index = serializeNull(buffer, key, value, index);
|
|
63254
|
-
}
|
|
63255
|
-
else if (value === null) {
|
|
63256
|
-
index = serializeNull(buffer, key, value, index);
|
|
63257
|
-
}
|
|
63258
|
-
else if (type === 'string') {
|
|
63259
|
-
index = serializeString(buffer, key, value, index);
|
|
64087
|
+
else if (tag === 'Binary') {
|
|
64088
|
+
index = serializeBinary(buffer, key, value, index);
|
|
63260
64089
|
}
|
|
63261
|
-
else if (
|
|
63262
|
-
index =
|
|
64090
|
+
else if (tag === 'BSONSymbol') {
|
|
64091
|
+
index = serializeSymbol(buffer, key, value, index);
|
|
63263
64092
|
}
|
|
63264
|
-
else if (
|
|
63265
|
-
|
|
64093
|
+
else if (tag === 'DBRef') {
|
|
64094
|
+
const dbref = value;
|
|
64095
|
+
const orderedValues = Object.assign({ $ref: dbref.collection, $id: dbref.oid }, dbref.db != null ? { $db: dbref.db } : null, dbref.fields);
|
|
64096
|
+
buffer[index++] = BSON_DATA_OBJECT;
|
|
64097
|
+
index += ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
64098
|
+
buffer[index++] = 0x00;
|
|
64099
|
+
path.add(orderedValues);
|
|
64100
|
+
currentFrame = makeFrame(orderedValues, index, null, frame, false, true);
|
|
64101
|
+
index += 4;
|
|
63266
64102
|
}
|
|
63267
|
-
else if (
|
|
63268
|
-
index =
|
|
64103
|
+
else if (tag === 'BSONRegExp') {
|
|
64104
|
+
index = serializeBSONRegExp(buffer, key, value, index);
|
|
63269
64105
|
}
|
|
63270
|
-
else if (
|
|
63271
|
-
|
|
63272
|
-
index = serializeDate(buffer, key, value, index);
|
|
63273
|
-
}
|
|
63274
|
-
else if (value instanceof Uint8Array || isUint8Array(value)) {
|
|
63275
|
-
index = serializeBuffer(buffer, key, value, index);
|
|
63276
|
-
}
|
|
63277
|
-
else if (value instanceof RegExp || isRegExp(value)) {
|
|
63278
|
-
index = serializeRegExp(buffer, key, value, index);
|
|
63279
|
-
}
|
|
63280
|
-
else {
|
|
63281
|
-
index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
|
|
63282
|
-
}
|
|
64106
|
+
else if (tag === 'Int32') {
|
|
64107
|
+
index = serializeInt32(buffer, key, value, index);
|
|
63283
64108
|
}
|
|
63284
|
-
else if (
|
|
63285
|
-
|
|
63286
|
-
throw new BSONVersionError();
|
|
63287
|
-
}
|
|
63288
|
-
else if (value._bsontype === 'ObjectId') {
|
|
63289
|
-
index = serializeObjectId(buffer, key, value, index);
|
|
63290
|
-
}
|
|
63291
|
-
else if (value._bsontype === 'Decimal128') {
|
|
63292
|
-
index = serializeDecimal128(buffer, key, value, index);
|
|
63293
|
-
}
|
|
63294
|
-
else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
|
|
63295
|
-
index = serializeLong(buffer, key, value, index);
|
|
63296
|
-
}
|
|
63297
|
-
else if (value._bsontype === 'Double') {
|
|
63298
|
-
index = serializeDouble(buffer, key, value, index);
|
|
63299
|
-
}
|
|
63300
|
-
else if (value._bsontype === 'Code') {
|
|
63301
|
-
index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
|
|
63302
|
-
}
|
|
63303
|
-
else if (value._bsontype === 'Binary') {
|
|
63304
|
-
index = serializeBinary(buffer, key, value, index);
|
|
63305
|
-
}
|
|
63306
|
-
else if (value._bsontype === 'BSONSymbol') {
|
|
63307
|
-
index = serializeSymbol(buffer, key, value, index);
|
|
63308
|
-
}
|
|
63309
|
-
else if (value._bsontype === 'DBRef') {
|
|
63310
|
-
index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
|
|
63311
|
-
}
|
|
63312
|
-
else if (value._bsontype === 'BSONRegExp') {
|
|
63313
|
-
index = serializeBSONRegExp(buffer, key, value, index);
|
|
63314
|
-
}
|
|
63315
|
-
else if (value._bsontype === 'Int32') {
|
|
63316
|
-
index = serializeInt32(buffer, key, value, index);
|
|
63317
|
-
}
|
|
63318
|
-
else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
|
|
63319
|
-
index = serializeMinMax(buffer, key, value, index);
|
|
63320
|
-
}
|
|
63321
|
-
else if (typeof value._bsontype !== 'undefined') {
|
|
63322
|
-
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
63323
|
-
}
|
|
64109
|
+
else if (tag === 'MinKey' || tag === 'MaxKey') {
|
|
64110
|
+
index = serializeMinMax(buffer, key, value, index);
|
|
63324
64111
|
}
|
|
63325
|
-
else if (
|
|
63326
|
-
|
|
64112
|
+
else if (typeof value._bsontype !== 'undefined') {
|
|
64113
|
+
throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
|
|
63327
64114
|
}
|
|
63328
64115
|
}
|
|
64116
|
+
else if (type === 'function' && serializeFunctions) {
|
|
64117
|
+
index = serializeFunction(buffer, key, value, index);
|
|
64118
|
+
}
|
|
63329
64119
|
}
|
|
63330
|
-
path.delete(object);
|
|
63331
|
-
buffer[index++] = 0x00;
|
|
63332
|
-
const size = index - startingIndex;
|
|
63333
|
-
startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
|
|
63334
64120
|
return index;
|
|
63335
64121
|
}
|
|
63336
64122
|
|
|
@@ -63613,21 +64399,22 @@ function parse(text, options) {
|
|
|
63613
64399
|
return deserializeValue(value, ejsonOptions);
|
|
63614
64400
|
});
|
|
63615
64401
|
}
|
|
63616
|
-
function stringify(value,
|
|
63617
|
-
if (
|
|
63618
|
-
options =
|
|
63619
|
-
|
|
64402
|
+
function stringify(value, replacerOrOptions, spaceOrOptions, options) {
|
|
64403
|
+
if (spaceOrOptions != null && typeof spaceOrOptions === 'object') {
|
|
64404
|
+
options = spaceOrOptions;
|
|
64405
|
+
spaceOrOptions = undefined;
|
|
63620
64406
|
}
|
|
63621
|
-
if (
|
|
63622
|
-
|
|
63623
|
-
|
|
63624
|
-
|
|
64407
|
+
if (replacerOrOptions != null &&
|
|
64408
|
+
typeof replacerOrOptions === 'object' &&
|
|
64409
|
+
!Array.isArray(replacerOrOptions)) {
|
|
64410
|
+
options = replacerOrOptions;
|
|
64411
|
+
replacerOrOptions = undefined;
|
|
63625
64412
|
}
|
|
63626
64413
|
const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
|
|
63627
64414
|
seenObjects: [{ propertyName: '(root)', obj: null }]
|
|
63628
64415
|
});
|
|
63629
64416
|
const doc = serializeValue(value, serializeOptions);
|
|
63630
|
-
return JSON.stringify(doc,
|
|
64417
|
+
return JSON.stringify(doc, replacerOrOptions, spaceOrOptions);
|
|
63631
64418
|
}
|
|
63632
64419
|
function EJSONserialize(value, options) {
|
|
63633
64420
|
options = options || {};
|
|
@@ -63789,7 +64576,7 @@ function serialize(object, options = {}) {
|
|
|
63789
64576
|
if (buffer.length < minInternalBufferSize) {
|
|
63790
64577
|
buffer = ByteUtils.allocate(minInternalBufferSize);
|
|
63791
64578
|
}
|
|
63792
|
-
const serializationIndex = serializeInto(buffer, object, checkKeys, 0,
|
|
64579
|
+
const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
|
|
63793
64580
|
const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
|
|
63794
64581
|
finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
|
|
63795
64582
|
return finishedBuffer;
|
|
@@ -63799,7 +64586,7 @@ function serializeWithBufferAndIndex(object, finalBuffer, options = {}) {
|
|
|
63799
64586
|
const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
|
|
63800
64587
|
const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
|
|
63801
64588
|
const startIndex = typeof options.index === 'number' ? options.index : 0;
|
|
63802
|
-
const serializationIndex = serializeInto(buffer, object, checkKeys, 0,
|
|
64589
|
+
const serializationIndex = serializeInto(buffer, object, checkKeys, 0, serializeFunctions, ignoreUndefined, null);
|
|
63803
64590
|
finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
|
|
63804
64591
|
return startIndex + serializationIndex - 1;
|
|
63805
64592
|
}
|
|
@@ -64975,7 +65762,7 @@ var src_default = VueToastificationPlugin;
|
|
|
64975
65762
|
(module) {
|
|
64976
65763
|
|
|
64977
65764
|
"use strict";
|
|
64978
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.5.
|
|
65765
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.5.9","description":"AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","mpath":"^0.9.0","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"@nestjs/common":"10.x || 11.x","mongoose":"7.x || 8.x || ^9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","@nestjs/common":"^11.1.9","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js","eval:agent":"node eval/agent/run.js"}}');
|
|
64979
65766
|
|
|
64980
65767
|
/***/ }
|
|
64981
65768
|
|