collabmd 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{drawio-editor-Br4qyh3r.js → drawio-editor-BMTwP5mD.js} +1 -1
- package/dist/client/assets/{drawioEditor-ClmnTx5J.js → drawioEditor-D6clB5OH.js} +2 -2
- package/dist/client/assets/{editor-session-Cf-iV5-3.js → editor-session-Dzy_85Wp.js} +1 -1
- package/dist/client/assets/{excalidraw-editor-CXAxVhlw.js → excalidraw-editor-CAP9F8CB.js} +3 -3
- package/dist/client/assets/{excalidrawEditor-DoH_kMdu.js → excalidrawEditor-DD4AvzYP.js} +2 -2
- package/dist/client/assets/{exportDocument-9wTrBZNS.css → exportDocument-BbkN85c4.css} +1 -1
- package/dist/client/assets/index-BIt5EZXz.css +1 -0
- package/dist/client/assets/{index-C8QZYHvV.js → index-BLrtj9YM.js} +2 -2
- package/dist/client/assets/main-CfSHaGes.js +1368 -0
- package/dist/client/assets/{vault-api-client-Bf9tB_GW.js → vault-api-client-p7h7cXKM.js} +1 -1
- package/dist/client/drawio-editor.html +1 -1
- package/dist/client/excalidraw-editor.html +1 -1
- package/dist/client/export-document.html +2 -2
- package/dist/client/index.html +2 -2
- package/package.json +6 -5
- package/src/client/application/app-shell/git-feature.js +44 -36
- package/src/client/application/app-shell/ui-feature-shell.js +84 -63
- package/src/client/bootstrap/collabmd-app-shell.js +6 -0
- package/src/client/infrastructure/editor-session.js +4 -0
- package/src/client/infrastructure/editor-view-adapter.js +23 -0
- package/src/client/infrastructure/vault-api-client.js +48 -0
- package/src/client/presentation/bases-preview-controller.js +1388 -28
- package/src/client/presentation/comments-panel.js +50 -5
- package/src/client/presentation/excalidraw-embed-controller.js +4 -1
- package/src/client/presentation/file-history-view-controller.js +9 -1
- package/src/client/presentation/git-panel-controller.js +101 -91
- package/src/client/styles/components/scrollbars.css +5 -0
- package/src/client/styles/features/preview-markdown.css +468 -23
- package/src/server/domain/backlink-index.js +202 -11
- package/src/server/domain/bases/base-definition.js +138 -11
- package/src/server/domain/bases/base-expression-runtime.js +54 -17
- package/src/server/domain/bases/base-index-snapshot-store.js +167 -20
- package/src/server/domain/bases/base-query-metadata.js +242 -0
- package/src/server/domain/bases/base-query-results.js +18 -8
- package/src/server/domain/bases/base-query-service.js +360 -49
- package/src/server/domain/bases/base-transform.js +116 -0
- package/src/server/infrastructure/http/create-request-handler.js +63 -28
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +275 -256
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +258 -184
- package/src/server/infrastructure/persistence/vault-file-store.js +68 -12
- package/dist/client/assets/index-CxSbUTs2.css +0 -1
- package/dist/client/assets/main-BjHHMlv0.js +0 -1249
- /package/dist/client/assets/{exportDocument-Bia2hI25.js → exportDocument-Zfwq1XVx.js} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildColumns,
|
|
3
3
|
collectEvaluatedPropertyIds,
|
|
4
|
+
findView,
|
|
4
5
|
normalizeBaseDefinition,
|
|
5
6
|
} from './base-definition.js';
|
|
6
7
|
import {
|
|
@@ -8,12 +9,97 @@ import {
|
|
|
8
9
|
createEvaluationRootContext,
|
|
9
10
|
evaluateFilterNode,
|
|
10
11
|
getPropertyValue,
|
|
12
|
+
serializeBaseValue,
|
|
11
13
|
} from './base-expression-runtime.js';
|
|
12
14
|
import { BaseIndexSnapshotStore } from './base-index-snapshot-store.js';
|
|
15
|
+
import {
|
|
16
|
+
buildAvailableProperties,
|
|
17
|
+
collectDistinctPropertyValues,
|
|
18
|
+
createPropertyCatalogSnapshot,
|
|
19
|
+
inferValueType,
|
|
20
|
+
} from './base-query-metadata.js';
|
|
13
21
|
import { buildQueryResultPayload, rowMatchesSearch } from './base-query-results.js';
|
|
22
|
+
import { transformBaseSource } from './base-transform.js';
|
|
14
23
|
|
|
15
24
|
export { serializeBaseDefinition } from './base-definition.js';
|
|
16
25
|
|
|
26
|
+
function buildSortChain(activeView) {
|
|
27
|
+
const entries = [];
|
|
28
|
+
(activeView.sort ?? []).forEach((sortConfig) => {
|
|
29
|
+
if (
|
|
30
|
+
sortConfig?.property
|
|
31
|
+
&& !entries.some((entry) => entry.property === sortConfig.property)
|
|
32
|
+
) {
|
|
33
|
+
entries.push(sortConfig);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return entries;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function sortRows(rows, sortChain = []) {
|
|
40
|
+
if (!Array.isArray(sortChain) || sortChain.length === 0) {
|
|
41
|
+
return rows;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return rows.slice().sort((left, right) => {
|
|
45
|
+
for (const sortConfig of sortChain) {
|
|
46
|
+
const delta = compareValues(left.rawCells[sortConfig.property], right.rawCells[sortConfig.property]);
|
|
47
|
+
if (delta !== 0) {
|
|
48
|
+
return sortConfig.direction === 'desc' ? -delta : delta;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return String(left.file.path ?? '').localeCompare(String(right.file.path ?? ''));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function escapeRegExp(value = '') {
|
|
57
|
+
return String(value ?? '').replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function pruneFilterNodeForProperty(filterNode, propertyId = '') {
|
|
61
|
+
if (!propertyId || !filterNode) {
|
|
62
|
+
return filterNode;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof filterNode === 'string') {
|
|
66
|
+
const escapedPropertyId = escapeRegExp(propertyId);
|
|
67
|
+
const propertyFilterPattern = new RegExp(
|
|
68
|
+
`^!?${escapedPropertyId}(?:\\.(?:contains|startsWith|endsWith|isEmpty)\\(.*\\)|\\s*(?:==|!=|>=|<=|>|<)\\s*.+)$`,
|
|
69
|
+
'u',
|
|
70
|
+
);
|
|
71
|
+
return propertyFilterPattern.test(String(filterNode ?? '').trim()) ? null : filterNode;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (Array.isArray(filterNode?.and)) {
|
|
75
|
+
const children = filterNode.and
|
|
76
|
+
.map((child) => pruneFilterNodeForProperty(child, propertyId))
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
return children.length > 0 ? { and: children } : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (Array.isArray(filterNode?.or)) {
|
|
82
|
+
const children = filterNode.or
|
|
83
|
+
.map((child) => pruneFilterNodeForProperty(child, propertyId))
|
|
84
|
+
.filter(Boolean);
|
|
85
|
+
return children.length > 0 ? { or: children } : null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (filterNode?.not != null) {
|
|
89
|
+
if (Array.isArray(filterNode.not)) {
|
|
90
|
+
const children = filterNode.not
|
|
91
|
+
.map((child) => pruneFilterNodeForProperty(child, propertyId))
|
|
92
|
+
.filter(Boolean);
|
|
93
|
+
return children.length > 0 ? { not: children } : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const child = pruneFilterNodeForProperty(filterNode.not, propertyId);
|
|
97
|
+
return child ? { not: child } : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return filterNode;
|
|
101
|
+
}
|
|
102
|
+
|
|
17
103
|
export class BaseQueryService {
|
|
18
104
|
constructor({
|
|
19
105
|
vaultFileStore,
|
|
@@ -26,6 +112,7 @@ export class BaseQueryService {
|
|
|
26
112
|
workspaceStateProvider,
|
|
27
113
|
workspaceStateSynchronizer,
|
|
28
114
|
});
|
|
115
|
+
this.propertyCatalogCache = new Map();
|
|
29
116
|
}
|
|
30
117
|
|
|
31
118
|
get workspaceStateProvider() {
|
|
@@ -105,13 +192,27 @@ export class BaseQueryService {
|
|
|
105
192
|
}
|
|
106
193
|
|
|
107
194
|
async applyWorkspaceChange(...args) {
|
|
108
|
-
|
|
195
|
+
const result = await this.snapshotStore.applyWorkspaceChange(...args);
|
|
196
|
+
const scannedAt = this.snapshotStore.indexSnapshot?.scannedAt;
|
|
197
|
+
if (scannedAt) {
|
|
198
|
+
this.invalidatePropertyCatalogsExcept(scannedAt);
|
|
199
|
+
} else {
|
|
200
|
+
this.propertyCatalogCache.clear();
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
109
203
|
}
|
|
110
204
|
|
|
111
|
-
|
|
205
|
+
invalidatePropertyCatalogsExcept(scannedAt = '') {
|
|
206
|
+
Array.from(this.propertyCatalogCache.keys()).forEach((cacheKey) => {
|
|
207
|
+
if (cacheKey !== scannedAt) {
|
|
208
|
+
this.propertyCatalogCache.delete(cacheKey);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async resolveQueryContext({
|
|
112
214
|
activeFilePath = '',
|
|
113
215
|
basePath = '',
|
|
114
|
-
search = '',
|
|
115
216
|
source = null,
|
|
116
217
|
sourcePath = '',
|
|
117
218
|
view: requestedView = '',
|
|
@@ -126,74 +227,195 @@ export class BaseQueryService {
|
|
|
126
227
|
basePath,
|
|
127
228
|
sourcePath,
|
|
128
229
|
});
|
|
230
|
+
this.invalidatePropertyCatalogsExcept(snapshot?.scannedAt ?? '');
|
|
231
|
+
|
|
129
232
|
const thisFilePath = sourcePath || activeFilePath || basePath || '';
|
|
130
233
|
const thisFile = snapshot.rowsByPath.get(thisFilePath)?.file ?? null;
|
|
131
|
-
const activeView = definition
|
|
234
|
+
const activeView = findView(definition, requestedView);
|
|
132
235
|
const columns = buildColumns(definition, activeView);
|
|
133
236
|
const evaluatedPropertyIds = collectEvaluatedPropertyIds(columns, activeView);
|
|
134
237
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
238
|
+
return {
|
|
239
|
+
activeView,
|
|
240
|
+
basePath,
|
|
241
|
+
baseSource,
|
|
242
|
+
columns,
|
|
243
|
+
definition,
|
|
244
|
+
evaluatedPropertyIds,
|
|
245
|
+
snapshot,
|
|
246
|
+
sourcePath,
|
|
247
|
+
thisFile,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
148
250
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
251
|
+
collectCandidateRows({
|
|
252
|
+
activeView,
|
|
253
|
+
astCache = new Map(),
|
|
254
|
+
definition,
|
|
255
|
+
evaluatedPropertyIds,
|
|
256
|
+
snapshot,
|
|
257
|
+
thisFile,
|
|
258
|
+
}) {
|
|
259
|
+
const rows = [];
|
|
260
|
+
|
|
261
|
+
snapshot.filePaths.forEach((filePath) => {
|
|
262
|
+
const row = snapshot.rowsByPath.get(filePath);
|
|
263
|
+
if (!row) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const rowContext = this.createRowQueryContext({
|
|
268
|
+
astCache,
|
|
269
|
+
definition,
|
|
270
|
+
row,
|
|
271
|
+
snapshot,
|
|
272
|
+
thisFile,
|
|
166
273
|
});
|
|
274
|
+
if (!evaluateFilterNode(definition.filters, rowContext) || !evaluateFilterNode(activeView.filters, rowContext)) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
167
277
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
278
|
+
const rawCells = {};
|
|
279
|
+
evaluatedPropertyIds.forEach((propertyId) => {
|
|
280
|
+
rawCells[propertyId] = getPropertyValue(propertyId, row, definition, snapshot, thisFile, rowContext);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
rows.push({
|
|
284
|
+
file: row.file,
|
|
285
|
+
rawCells,
|
|
172
286
|
});
|
|
173
287
|
});
|
|
174
288
|
|
|
175
|
-
|
|
176
|
-
|
|
289
|
+
return rows;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
createRowQueryContext({
|
|
293
|
+
astCache = new Map(),
|
|
294
|
+
definition,
|
|
295
|
+
row,
|
|
296
|
+
snapshot,
|
|
297
|
+
thisFile,
|
|
298
|
+
}) {
|
|
299
|
+
return createEvaluationRootContext({
|
|
300
|
+
astCache,
|
|
301
|
+
currentRow: row,
|
|
302
|
+
definition,
|
|
303
|
+
snapshot,
|
|
304
|
+
thisFile,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
getPropertyCatalog(snapshot) {
|
|
309
|
+
const cacheKey = snapshot?.scannedAt ?? 'unknown';
|
|
310
|
+
if (!this.propertyCatalogCache.has(cacheKey)) {
|
|
311
|
+
this.propertyCatalogCache.set(cacheKey, createPropertyCatalogSnapshot(snapshot));
|
|
177
312
|
}
|
|
178
313
|
|
|
179
|
-
|
|
314
|
+
return this.propertyCatalogCache.get(cacheKey);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
inferFormulaValueTypes(definition, rows = []) {
|
|
318
|
+
const valueTypes = {};
|
|
319
|
+
|
|
320
|
+
Object.keys(definition.formulas ?? {}).forEach((propertyId) => {
|
|
321
|
+
let inferred = 'unknown';
|
|
322
|
+
for (const row of rows) {
|
|
323
|
+
inferred = inferValueType(row?.rawCells?.[propertyId]);
|
|
324
|
+
if (inferred !== 'unknown') {
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
valueTypes[propertyId] = inferred;
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
return valueTypes;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
buildMeta({
|
|
335
|
+
activeView,
|
|
336
|
+
basePath = '',
|
|
337
|
+
columns,
|
|
338
|
+
definition,
|
|
339
|
+
rows,
|
|
340
|
+
snapshot,
|
|
341
|
+
}) {
|
|
342
|
+
const propertyCatalog = this.getPropertyCatalog(snapshot);
|
|
343
|
+
const formulaValueTypes = this.inferFormulaValueTypes(definition, rows);
|
|
180
344
|
|
|
181
345
|
return {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
346
|
+
activeViewConfig: {
|
|
347
|
+
filters: activeView.filters ?? null,
|
|
348
|
+
groupBy: activeView.groupBy ?? null,
|
|
349
|
+
order: [...(activeView.order ?? [])],
|
|
350
|
+
sort: (activeView.sort ?? []).map((entry) => ({ ...entry })),
|
|
351
|
+
},
|
|
352
|
+
availableProperties: buildAvailableProperties({
|
|
185
353
|
activeView,
|
|
186
354
|
columns,
|
|
187
355
|
definition,
|
|
356
|
+
formulaValueTypes,
|
|
357
|
+
propertyCatalog,
|
|
358
|
+
}),
|
|
359
|
+
editable: Boolean(basePath),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async query({
|
|
364
|
+
activeFilePath = '',
|
|
365
|
+
basePath = '',
|
|
366
|
+
includeCsv = false,
|
|
367
|
+
search = '',
|
|
368
|
+
source = null,
|
|
369
|
+
sourcePath = '',
|
|
370
|
+
view: requestedView = '',
|
|
371
|
+
} = {}) {
|
|
372
|
+
const context = await this.resolveQueryContext({
|
|
373
|
+
activeFilePath,
|
|
374
|
+
basePath,
|
|
375
|
+
source,
|
|
376
|
+
sourcePath,
|
|
377
|
+
view: requestedView,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const astCache = new Map();
|
|
381
|
+
let rows = this.collectCandidateRows({
|
|
382
|
+
...context,
|
|
383
|
+
astCache,
|
|
384
|
+
});
|
|
385
|
+
rows = rows.filter((row) => rowMatchesSearch(row, context.columns, search));
|
|
386
|
+
rows = sortRows(rows, buildSortChain(context.activeView));
|
|
387
|
+
|
|
388
|
+
if (context.activeView.limit != null) {
|
|
389
|
+
rows = rows.slice(0, context.activeView.limit);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const payload = buildQueryResultPayload({
|
|
393
|
+
activeView: context.activeView,
|
|
394
|
+
columns: context.columns,
|
|
395
|
+
definition: context.definition,
|
|
396
|
+
includeCsv,
|
|
397
|
+
rows,
|
|
398
|
+
snapshot: context.snapshot,
|
|
399
|
+
thisFile: context.thisFile,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
columns: context.columns,
|
|
404
|
+
definition: context.definition,
|
|
405
|
+
meta: this.buildMeta({
|
|
406
|
+
activeView: context.activeView,
|
|
407
|
+
basePath,
|
|
408
|
+
columns: context.columns,
|
|
409
|
+
definition: context.definition,
|
|
188
410
|
rows,
|
|
189
|
-
snapshot,
|
|
190
|
-
thisFile,
|
|
411
|
+
snapshot: context.snapshot,
|
|
191
412
|
}),
|
|
413
|
+
...payload,
|
|
192
414
|
view: {
|
|
193
|
-
...activeView,
|
|
194
|
-
supported: activeView.supported,
|
|
415
|
+
...context.activeView,
|
|
416
|
+
supported: context.activeView.supported,
|
|
195
417
|
},
|
|
196
|
-
views: definition.views.map((view) => ({
|
|
418
|
+
views: context.definition.views.map((view) => ({
|
|
197
419
|
id: view.id,
|
|
198
420
|
name: view.name,
|
|
199
421
|
supported: view.supported,
|
|
@@ -201,4 +423,93 @@ export class BaseQueryService {
|
|
|
201
423
|
})),
|
|
202
424
|
};
|
|
203
425
|
}
|
|
426
|
+
|
|
427
|
+
async propertyValues({
|
|
428
|
+
activeFilePath = '',
|
|
429
|
+
basePath = '',
|
|
430
|
+
propertyId = '',
|
|
431
|
+
query = '',
|
|
432
|
+
source = null,
|
|
433
|
+
sourcePath = '',
|
|
434
|
+
view: requestedView = '',
|
|
435
|
+
} = {}) {
|
|
436
|
+
if (!propertyId) {
|
|
437
|
+
throw new Error('Missing propertyId');
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const context = await this.resolveQueryContext({
|
|
441
|
+
activeFilePath,
|
|
442
|
+
basePath,
|
|
443
|
+
source,
|
|
444
|
+
sourcePath,
|
|
445
|
+
view: requestedView,
|
|
446
|
+
});
|
|
447
|
+
const propertyValueContext = {
|
|
448
|
+
...context,
|
|
449
|
+
activeView: {
|
|
450
|
+
...context.activeView,
|
|
451
|
+
filters: pruneFilterNodeForProperty(context.activeView?.filters, propertyId),
|
|
452
|
+
},
|
|
453
|
+
definition: {
|
|
454
|
+
...context.definition,
|
|
455
|
+
filters: pruneFilterNodeForProperty(context.definition?.filters, propertyId),
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
const astCache = new Map();
|
|
459
|
+
const rows = this.collectCandidateRows({
|
|
460
|
+
...propertyValueContext,
|
|
461
|
+
astCache,
|
|
462
|
+
evaluatedPropertyIds: [...new Set([
|
|
463
|
+
...(context.evaluatedPropertyIds ?? []),
|
|
464
|
+
propertyId,
|
|
465
|
+
])],
|
|
466
|
+
});
|
|
467
|
+
const values = collectDistinctPropertyValues(rows, propertyId, query).map((entry) => ({
|
|
468
|
+
count: entry.count,
|
|
469
|
+
text: entry.text,
|
|
470
|
+
value: serializeBaseValue(entry.value, {
|
|
471
|
+
snapshot: context.snapshot,
|
|
472
|
+
sourcePath: context.thisFile?.path ?? '',
|
|
473
|
+
}),
|
|
474
|
+
}));
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
ok: true,
|
|
478
|
+
propertyId,
|
|
479
|
+
values,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
async transform({
|
|
484
|
+
activeFilePath = '',
|
|
485
|
+
basePath = '',
|
|
486
|
+
mutation = null,
|
|
487
|
+
source = null,
|
|
488
|
+
sourcePath = '',
|
|
489
|
+
view = '',
|
|
490
|
+
} = {}) {
|
|
491
|
+
if (!basePath) {
|
|
492
|
+
throw new Error('Only .base files can be transformed');
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const baseSource = source ?? await this.vaultFileStore.readBaseFile(basePath);
|
|
496
|
+
if (typeof baseSource !== 'string') {
|
|
497
|
+
throw new Error('Base source not found');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const nextSource = transformBaseSource(baseSource, mutation);
|
|
501
|
+
const result = await this.query({
|
|
502
|
+
activeFilePath,
|
|
503
|
+
basePath,
|
|
504
|
+
source: nextSource,
|
|
505
|
+
sourcePath,
|
|
506
|
+
view,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
meta: result.meta,
|
|
511
|
+
result,
|
|
512
|
+
source: nextSource,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
204
515
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
findView,
|
|
5
|
+
normalizeBaseDefinition,
|
|
6
|
+
normalizeRawDefinitionForWrite,
|
|
7
|
+
} from './base-definition.js';
|
|
8
|
+
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
return Object.prototype.toString.call(value) === '[object Object]';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function cloneRawSource(source = '') {
|
|
14
|
+
const parsed = yaml.load(String(source ?? '')) ?? {};
|
|
15
|
+
return isPlainObject(parsed) ? parsed : {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function ensureViews(rawDefinition) {
|
|
19
|
+
if (!Array.isArray(rawDefinition.views) || rawDefinition.views.length === 0) {
|
|
20
|
+
rawDefinition.views = [{ name: 'Table', type: 'table' }];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return rawDefinition.views;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function findViewIndex(definition, requestedView = '') {
|
|
27
|
+
const activeView = findView(definition, requestedView);
|
|
28
|
+
return definition.views.findIndex((view) => view.id === activeView.id);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function serializeGroupBy(groupBy) {
|
|
32
|
+
if (!groupBy?.property) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
direction: String(groupBy.direction ?? 'asc').toUpperCase(),
|
|
38
|
+
property: groupBy.property,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function serializeSort(sort = []) {
|
|
43
|
+
return sort
|
|
44
|
+
.filter((entry) => entry?.property)
|
|
45
|
+
.map((entry) => ({
|
|
46
|
+
direction: String(entry.direction ?? 'asc').toUpperCase(),
|
|
47
|
+
property: entry.property,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function applySetViewConfig(rawDefinition, definition, mutation = {}) {
|
|
52
|
+
const viewIndex = findViewIndex(definition, mutation.view ?? '');
|
|
53
|
+
if (viewIndex < 0) {
|
|
54
|
+
throw new Error('Base view not found');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const rawViews = ensureViews(rawDefinition);
|
|
58
|
+
const nextView = isPlainObject(rawViews[viewIndex])
|
|
59
|
+
? { ...rawViews[viewIndex] }
|
|
60
|
+
: {};
|
|
61
|
+
const nextConfig = isPlainObject(mutation.config) ? mutation.config : {};
|
|
62
|
+
|
|
63
|
+
if (Object.hasOwn(nextConfig, 'order')) {
|
|
64
|
+
nextView.order = Array.isArray(nextConfig.order)
|
|
65
|
+
? nextConfig.order.filter((entry) => typeof entry === 'string' && entry.trim())
|
|
66
|
+
: [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (Object.hasOwn(nextConfig, 'sort')) {
|
|
70
|
+
const sort = serializeSort(nextConfig.sort);
|
|
71
|
+
if (sort.length > 0) {
|
|
72
|
+
nextView.sort = sort;
|
|
73
|
+
} else {
|
|
74
|
+
delete nextView.sort;
|
|
75
|
+
delete nextView.sorts;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (Object.hasOwn(nextConfig, 'groupBy')) {
|
|
80
|
+
const groupBy = serializeGroupBy(nextConfig.groupBy);
|
|
81
|
+
if (groupBy) {
|
|
82
|
+
nextView.groupBy = groupBy;
|
|
83
|
+
} else {
|
|
84
|
+
delete nextView.groupBy;
|
|
85
|
+
delete nextView.group_by;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (Object.hasOwn(nextConfig, 'filters')) {
|
|
90
|
+
if (nextConfig.filters == null || nextConfig.filters === '') {
|
|
91
|
+
delete nextView.filters;
|
|
92
|
+
} else {
|
|
93
|
+
nextView.filters = nextConfig.filters;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
rawViews[viewIndex] = nextView;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function transformBaseSource(source = '', mutation = {}) {
|
|
101
|
+
const definition = normalizeBaseDefinition(source);
|
|
102
|
+
const rawDefinition = normalizeRawDefinitionForWrite({
|
|
103
|
+
raw: cloneRawSource(source),
|
|
104
|
+
});
|
|
105
|
+
const mutationType = String(mutation?.type ?? '');
|
|
106
|
+
|
|
107
|
+
switch (mutationType) {
|
|
108
|
+
case 'set-view-config':
|
|
109
|
+
applySetViewConfig(rawDefinition, definition, mutation);
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
throw new Error(`Unsupported base mutation: ${mutationType || 'unknown'}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return `${yaml.dump(normalizeRawDefinitionForWrite(rawDefinition), { lineWidth: -1, noRefs: true }).trim()}\n`;
|
|
116
|
+
}
|