collabmd 0.1.33 → 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/{index-Czg_KL-i.js → index-BLrtj9YM.js} +2 -2
- package/dist/client/assets/{main-DeowJOSp.js → main-CfSHaGes.js} +86 -86
- package/dist/client/index.html +1 -1
- package/package.json +2 -1
- 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/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/server/domain/backlink-index.js +41 -22
- 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 +255 -233
- package/src/server/infrastructure/persistence/vault-file-store.js +68 -12
|
@@ -371,10 +371,23 @@ export class BacklinkIndex {
|
|
|
371
371
|
previousState = null,
|
|
372
372
|
nextState = null,
|
|
373
373
|
} = {}) {
|
|
374
|
-
let refreshIndex = false;
|
|
375
374
|
const previousEntries = previousState?.entries ?? new Map();
|
|
376
375
|
const nextEntries = nextState?.entries ?? new Map();
|
|
377
376
|
const changedPaths = Array.from(new Set(workspaceChange.changedPaths ?? []));
|
|
377
|
+
const { impactedSources, renameMap } = this._computeWorkspaceChangeMeta(workspaceChange, changedPaths, previousEntries, nextEntries);
|
|
378
|
+
|
|
379
|
+
let refreshIndex = this._applyDeletedPaths(workspaceChange.deletedPaths);
|
|
380
|
+
refreshIndex = this._applyRenamedPaths(workspaceChange.renamedPaths, refreshIndex);
|
|
381
|
+
refreshIndex = await this._applyChangedPaths(changedPaths, previousEntries, nextEntries, refreshIndex);
|
|
382
|
+
|
|
383
|
+
if (refreshIndex) {
|
|
384
|
+
const sourcesToRefresh = this._collectRefreshSources(impactedSources, changedPaths, workspaceChange.renamedPaths, nextEntries);
|
|
385
|
+
this._refreshWikiTargetIndex();
|
|
386
|
+
await this._refreshImpactedSources(sourcesToRefresh, { nextEntries, renameMap });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
_computeWorkspaceChangeMeta(workspaceChange, changedPaths, previousEntries, nextEntries) {
|
|
378
391
|
const createdPaths = changedPaths.filter((pathValue) => (
|
|
379
392
|
nextEntries.has(pathValue) && !previousEntries.has(pathValue)
|
|
380
393
|
));
|
|
@@ -393,18 +406,28 @@ export class BacklinkIndex {
|
|
|
393
406
|
.filter((entry) => entry?.oldPath && entry?.newPath)
|
|
394
407
|
.map((entry) => [entry.oldPath, entry.newPath]),
|
|
395
408
|
);
|
|
409
|
+
return { impactedSources, renameMap };
|
|
410
|
+
}
|
|
396
411
|
|
|
397
|
-
|
|
412
|
+
_applyDeletedPaths(deletedPaths) {
|
|
413
|
+
let refreshIndex = false;
|
|
414
|
+
for (const pathValue of deletedPaths ?? []) {
|
|
398
415
|
refreshIndex = this.onFileDeleted(pathValue, { refreshIndex: false }) || refreshIndex;
|
|
399
416
|
}
|
|
417
|
+
return refreshIndex;
|
|
418
|
+
}
|
|
400
419
|
|
|
401
|
-
|
|
420
|
+
_applyRenamedPaths(renamedPaths, refreshIndex) {
|
|
421
|
+
for (const entry of renamedPaths ?? []) {
|
|
402
422
|
if (!entry?.oldPath || !entry?.newPath) {
|
|
403
423
|
continue;
|
|
404
424
|
}
|
|
405
425
|
refreshIndex = this.onFileRenamed(entry.oldPath, entry.newPath, { refreshIndex: false }) || refreshIndex;
|
|
406
426
|
}
|
|
427
|
+
return refreshIndex;
|
|
428
|
+
}
|
|
407
429
|
|
|
430
|
+
async _applyChangedPaths(changedPaths, previousEntries, nextEntries, refreshIndex) {
|
|
408
431
|
for (const pathValue of changedPaths) {
|
|
409
432
|
const existsNow = nextEntries.has(pathValue);
|
|
410
433
|
const existedBefore = previousEntries.has(pathValue);
|
|
@@ -433,26 +456,22 @@ export class BacklinkIndex {
|
|
|
433
456
|
refreshIndex = this.onFileCreated(pathValue, content, { refreshIndex: false }) || refreshIndex;
|
|
434
457
|
}
|
|
435
458
|
}
|
|
459
|
+
return refreshIndex;
|
|
460
|
+
}
|
|
436
461
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
this._refreshWikiTargetIndex();
|
|
451
|
-
await this._refreshImpactedSources(sourcesToRefresh, {
|
|
452
|
-
nextEntries,
|
|
453
|
-
renameMap,
|
|
454
|
-
});
|
|
455
|
-
}
|
|
462
|
+
_collectRefreshSources(impactedSources, changedPaths, renamedPaths, nextEntries) {
|
|
463
|
+
const sourcesToRefresh = new Set(impactedSources);
|
|
464
|
+
changedPaths.forEach((pathValue) => {
|
|
465
|
+
if (nextEntries.has(pathValue) && isMarkdownFilePath(pathValue)) {
|
|
466
|
+
sourcesToRefresh.add(pathValue);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
(renamedPaths ?? []).forEach((entry) => {
|
|
470
|
+
if (entry?.newPath && isMarkdownFilePath(entry.newPath)) {
|
|
471
|
+
sourcesToRefresh.add(entry.newPath);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
return sourcesToRefresh;
|
|
456
475
|
}
|
|
457
476
|
|
|
458
477
|
/**
|
|
@@ -70,8 +70,7 @@ export function createRequestHandler(
|
|
|
70
70
|
workspaceMutationCoordinator,
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
const originalRequestUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
73
|
+
function handleBasePathRedirect(req, res, originalRequestUrl) {
|
|
75
74
|
if (
|
|
76
75
|
config.basePath
|
|
77
76
|
&& (req.method === 'GET' || req.method === 'HEAD')
|
|
@@ -80,50 +79,56 @@ export function createRequestHandler(
|
|
|
80
79
|
const location = `${config.basePath}/${originalRequestUrl.search}`;
|
|
81
80
|
res.writeHead(308, { Location: location });
|
|
82
81
|
res.end();
|
|
83
|
-
return;
|
|
82
|
+
return true;
|
|
84
83
|
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const isSameOriginWrite = isSameOriginWriteRequest(req, requestUrl);
|
|
91
|
-
|
|
92
|
-
setHeaders(res, SECURITY_HEADERS);
|
|
93
|
-
|
|
94
|
-
if (req.method === 'OPTIONS') {
|
|
95
|
-
const requestedMethod = String(req.headers['access-control-request-method'] || '').toUpperCase();
|
|
96
|
-
const preflightTargetsWrite = WRITE_METHODS.has(requestedMethod);
|
|
97
|
-
if (preflightTargetsWrite && !isSameOriginWrite) {
|
|
98
|
-
jsonResponse(req, res, 403, { error: 'Cross-origin write requests are not allowed' });
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
87
|
+
function handleCorsPreflight(req, res, isSameOriginWrite) {
|
|
88
|
+
if (req.method !== 'OPTIONS') {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
101
91
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
const requestedMethod = String(req.headers['access-control-request-method'] || '').toUpperCase();
|
|
93
|
+
const preflightTargetsWrite = WRITE_METHODS.has(requestedMethod);
|
|
94
|
+
if (preflightTargetsWrite && !isSameOriginWrite) {
|
|
95
|
+
jsonResponse(req, res, 403, { error: 'Cross-origin write requests are not allowed' });
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
res.
|
|
108
|
-
return;
|
|
99
|
+
if (isSameOriginWrite) {
|
|
100
|
+
applyCorsHeaders(res, req.headers.origin);
|
|
109
101
|
}
|
|
110
102
|
|
|
103
|
+
res.writeHead(204);
|
|
104
|
+
res.end();
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function handleCorsWriteGuard(req, res, isSameOriginWrite) {
|
|
111
109
|
if (WRITE_METHODS.has(req.method) && !isSameOriginWrite) {
|
|
112
110
|
jsonResponse(req, res, 403, { error: 'Cross-origin write requests are not allowed' });
|
|
113
|
-
return;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function handleTestEndpoints(req, res, requestUrl) {
|
|
117
|
+
if (config.nodeEnv !== 'test') {
|
|
118
|
+
return false;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
if (
|
|
121
|
+
if (requestUrl.pathname === '/api/test/reset-state' && req.method === 'POST') {
|
|
117
122
|
await fileSystemSyncService?.resetForExternalStateChange?.();
|
|
118
123
|
await roomRegistry?.reset?.();
|
|
119
124
|
await backlinkIndex?.build?.();
|
|
120
125
|
await workspaceMutationCoordinator?.initialize?.();
|
|
121
126
|
await fileSystemSyncService?.resetForExternalStateChange?.();
|
|
122
127
|
jsonResponse(req, res, 200, { ok: true });
|
|
123
|
-
return;
|
|
128
|
+
return true;
|
|
124
129
|
}
|
|
125
130
|
|
|
126
|
-
if (
|
|
131
|
+
if (requestUrl.pathname === '/api/test/hydrate-delay' && req.method === 'POST') {
|
|
127
132
|
const body = await parseJsonBody(req).catch(() => ({}));
|
|
128
133
|
testControls.wsRoomHydrateDelayMs = Math.max(0, Number(body?.delayMs) || 0);
|
|
129
134
|
await fileSystemSyncService?.resetForExternalStateChange?.();
|
|
@@ -132,6 +137,36 @@ export function createRequestHandler(
|
|
|
132
137
|
await workspaceMutationCoordinator?.initialize?.();
|
|
133
138
|
await fileSystemSyncService?.resetForExternalStateChange?.();
|
|
134
139
|
jsonResponse(req, res, 200, { delayMs: testControls.wsRoomHydrateDelayMs, ok: true });
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return async function handleRequest(req, res) {
|
|
147
|
+
const originalRequestUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
148
|
+
|
|
149
|
+
if (handleBasePathRedirect(req, res, originalRequestUrl)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const requestUrl = createRequestUrlWithPathname(
|
|
154
|
+
originalRequestUrl,
|
|
155
|
+
stripBasePath(originalRequestUrl.pathname, config.basePath),
|
|
156
|
+
);
|
|
157
|
+
const isSameOriginWrite = isSameOriginWriteRequest(req, requestUrl);
|
|
158
|
+
|
|
159
|
+
setHeaders(res, SECURITY_HEADERS);
|
|
160
|
+
|
|
161
|
+
if (handleCorsPreflight(req, res, isSameOriginWrite)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (handleCorsWriteGuard(req, res, isSameOriginWrite)) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (await handleTestEndpoints(req, res, requestUrl)) {
|
|
135
170
|
return;
|
|
136
171
|
}
|
|
137
172
|
|