@unson/brainbase-mcp 0.40.0 → 0.41.1
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/foundation-http.d.ts +12 -2
- package/dist/foundation-http.js +183 -8
- package/dist/graph-web.d.ts +2 -0
- package/dist/graph-web.js +10 -1
- package/dist/judgment-history.js +3 -1
- package/dist/local-web-host.js +1 -1
- package/dist/portable-graph.d.ts +2 -2
- package/dist/portable-graph.js +4 -3
- package/dist/types.d.ts +32 -1
- package/package.json +2 -2
- package/ui/foundation-overview.js +17 -1
- package/ui/graph-own-share.js +4 -2
- package/ui/graph-projects-view.js +167 -11
- package/ui/graph-registry-view.js +135 -5
- package/ui/graph-view-shared.js +6 -1
- package/ui/judgment-history.js +5 -1
- package/ui/meeting-minutes.css +19 -0
- package/ui/meeting-minutes.js +124 -6
- package/ui/objective-editor-http-port.js +21 -8
- package/ui/objective-editor.js +186 -24
- package/ui/objective-readback-contract.js +177 -0
- package/ui/outcome-knowledge.css +18 -1
- package/ui/outcome-knowledge.js +523 -55
- package/ui/project-workspace.js +177 -4
- package/ui/world/world-canvas-ui.css +32 -0
- package/ui/world/world-canvas-ui.js +30 -2
- package/ui/world/world-view.js +5 -0
package/ui/meeting-minutes.js
CHANGED
|
@@ -16,6 +16,15 @@ function text(value, fallback = '') {
|
|
|
16
16
|
return typeof value === 'string' && value.trim() ? value.trim() : fallback;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function meetingLabelText(meetingLabel, meeting) {
|
|
20
|
+
if (typeof meetingLabel !== 'function') return '';
|
|
21
|
+
try {
|
|
22
|
+
return text(meetingLabel(meeting));
|
|
23
|
+
} catch {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
function makeElement(doc, tag, { className, textContent, type, value, disabled, attrs = {} } = {}) {
|
|
20
29
|
const element = doc.createElement(tag);
|
|
21
30
|
if (className) element.className = className;
|
|
@@ -205,6 +214,9 @@ export function createMeetingMinutesUI({
|
|
|
205
214
|
token,
|
|
206
215
|
basePath = '/api/meeting-minutes',
|
|
207
216
|
versionActionExtensions = [],
|
|
217
|
+
meetingLabel,
|
|
218
|
+
canCreateMeeting,
|
|
219
|
+
canEditMinutes,
|
|
208
220
|
} = {}) {
|
|
209
221
|
if (!root) throw new TypeError('root is required');
|
|
210
222
|
const doc = explicitDocument ?? (typeof document === 'undefined' ? null : document);
|
|
@@ -213,6 +225,8 @@ export function createMeetingMinutesUI({
|
|
|
213
225
|
? fetcher
|
|
214
226
|
: typeof globalThis.fetch === 'function' ? (path, init) => globalThis.fetch(path, init) : null;
|
|
215
227
|
const base = String(basePath).replace(/\/+$/u, '');
|
|
228
|
+
const createBlockedMessage = 'この画面では会議を作成できません。';
|
|
229
|
+
const editBlockedMessage = 'この画面では議事録を編集できません。';
|
|
216
230
|
const state = {
|
|
217
231
|
meetings: [],
|
|
218
232
|
detail: null,
|
|
@@ -223,6 +237,8 @@ export function createMeetingMinutesUI({
|
|
|
223
237
|
listError: null,
|
|
224
238
|
detailError: null,
|
|
225
239
|
};
|
|
240
|
+
let listRequestGeneration = 0;
|
|
241
|
+
let detailRequestGeneration = 0;
|
|
226
242
|
|
|
227
243
|
const screen = makeElement(doc, 'div', { className: 'bb-minutes-screen', attrs: { 'data-contract-version': MEETING_MINUTES_UI_CONTRACT_VERSION } });
|
|
228
244
|
const heading = makeElement(doc, 'header', { className: 'bb-minutes-heading' });
|
|
@@ -233,7 +249,10 @@ export function createMeetingMinutesUI({
|
|
|
233
249
|
);
|
|
234
250
|
const status = makeElement(doc, 'p', { className: 'bb-minutes-status', attrs: { role: 'status', 'aria-live': 'polite' } });
|
|
235
251
|
const columns = makeElement(doc, 'div', { className: 'bb-minutes-columns' });
|
|
236
|
-
const listPane = makeElement(doc, 'section', {
|
|
252
|
+
const listPane = makeElement(doc, 'section', {
|
|
253
|
+
className: 'bb-minutes-list-pane',
|
|
254
|
+
attrs: { 'aria-label': '会議一覧', tabindex: '-1' },
|
|
255
|
+
});
|
|
237
256
|
const detailPane = makeElement(doc, 'section', { className: 'bb-minutes-detail-pane', attrs: { 'aria-label': '会議詳細' } });
|
|
238
257
|
columns.append(listPane, detailPane);
|
|
239
258
|
screen.append(heading, status, columns);
|
|
@@ -271,6 +290,49 @@ export function createMeetingMinutesUI({
|
|
|
271
290
|
|
|
272
291
|
function clear(node) { node.replaceChildren(); return node; }
|
|
273
292
|
|
|
293
|
+
function canCreate() {
|
|
294
|
+
if (typeof canCreateMeeting === 'function') {
|
|
295
|
+
try {
|
|
296
|
+
return canCreateMeeting() === true;
|
|
297
|
+
} catch {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return canCreateMeeting !== false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function canEdit() {
|
|
305
|
+
if (typeof canEditMinutes === 'function') {
|
|
306
|
+
try {
|
|
307
|
+
return canEditMinutes() === true;
|
|
308
|
+
} catch {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return canEditMinutes !== false;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function appendCreateBlockedNotice(parent, { alert = false } = {}) {
|
|
316
|
+
parent.append(makeElement(doc, 'p', {
|
|
317
|
+
className: 'bb-minutes-create-guard',
|
|
318
|
+
textContent: createBlockedMessage,
|
|
319
|
+
attrs: alert ? { role: 'alert' } : {},
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function appendEditBlockedNotice(parent, { alert = false } = {}) {
|
|
324
|
+
const existing = [...(parent.children ?? [])].find((child) => child?.className === 'bb-minutes-edit-guard');
|
|
325
|
+
if (existing) {
|
|
326
|
+
if (alert) existing.setAttribute('role', 'alert');
|
|
327
|
+
return existing;
|
|
328
|
+
}
|
|
329
|
+
parent.append(makeElement(doc, 'p', {
|
|
330
|
+
className: 'bb-minutes-edit-guard',
|
|
331
|
+
textContent: editBlockedMessage,
|
|
332
|
+
attrs: alert ? { role: 'alert' } : {},
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
|
|
274
336
|
function appendLoadError(parent, message, retry) {
|
|
275
337
|
const notice = makeElement(doc, 'div', {
|
|
276
338
|
className: 'bb-minutes-load-error',
|
|
@@ -285,10 +347,15 @@ export function createMeetingMinutesUI({
|
|
|
285
347
|
clear(listPane);
|
|
286
348
|
const head = makeElement(doc, 'div', { className: 'bb-minutes-pane-head' });
|
|
287
349
|
head.append(makeElement(doc, 'h2', { textContent: '会議一覧' }));
|
|
288
|
-
|
|
350
|
+
const createAllowed = canCreate();
|
|
351
|
+
head.append(button(doc, '会議を作成', () => renderCreateForm(), {
|
|
352
|
+
className: 'bb-minutes-button is-primary',
|
|
353
|
+
disabled: !createAllowed,
|
|
354
|
+
}));
|
|
355
|
+
if (!createAllowed) appendCreateBlockedNotice(head);
|
|
289
356
|
listPane.append(head);
|
|
290
357
|
if (state.listError) {
|
|
291
|
-
appendLoadError(listPane, `会議一覧を読み込めませんでした。理由: ${state.listError}`, loadMeetings);
|
|
358
|
+
appendLoadError(listPane, `会議一覧を読み込めませんでした。理由: ${state.listError}`, () => loadMeetings({ restoreFocus: true }));
|
|
292
359
|
}
|
|
293
360
|
if (state.meetings.length === 0) {
|
|
294
361
|
if (state.listError) return;
|
|
@@ -304,8 +371,10 @@ export function createMeetingMinutesUI({
|
|
|
304
371
|
type: 'button',
|
|
305
372
|
attrs: { type: 'button' },
|
|
306
373
|
});
|
|
374
|
+
const label = meetingLabelText(meetingLabel, meeting);
|
|
307
375
|
item.append(
|
|
308
376
|
makeElement(doc, 'strong', { textContent: text(meeting.title, '無題の会議') }),
|
|
377
|
+
...(label ? [makeElement(doc, 'span', { className: 'bb-minutes-meeting-label', textContent: label })] : []),
|
|
309
378
|
makeElement(doc, 'span', { className: 'bb-minutes-item-meta', textContent: displayDate(meeting.scheduled_at) }),
|
|
310
379
|
makeElement(doc, 'span', { className: 'bb-minutes-item-meta', textContent: `${Array.isArray(entry.minutes) ? entry.minutes.length : 0}件の議事録` }),
|
|
311
380
|
);
|
|
@@ -379,8 +448,14 @@ export function createMeetingMinutesUI({
|
|
|
379
448
|
section.append(content);
|
|
380
449
|
if (selected) {
|
|
381
450
|
const actions = makeElement(doc, 'div', { className: 'bb-minutes-actions' });
|
|
451
|
+
const editAllowed = canEdit();
|
|
382
452
|
const confirm = button(doc, selected.confirmation ? '確認済み' : 'この版を確認', async () => {
|
|
383
453
|
if (selected.confirmation) return;
|
|
454
|
+
if (!canEdit()) {
|
|
455
|
+
confirm.disabled = true;
|
|
456
|
+
appendEditBlockedNotice(section, { alert: true });
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
384
459
|
confirm.disabled = true;
|
|
385
460
|
try {
|
|
386
461
|
await mutate(`/${encodeURIComponent(detail.meeting.meeting_id)}/minutes/${encodeURIComponent(documentRecord.minutes_id)}/confirm`, {
|
|
@@ -393,9 +468,10 @@ export function createMeetingMinutesUI({
|
|
|
393
468
|
confirm.disabled = false;
|
|
394
469
|
setStatus(error instanceof Error ? error.message : '確認を保存できませんでした。', 'error');
|
|
395
470
|
}
|
|
396
|
-
}, { className: 'bb-minutes-button is-primary', disabled: Boolean(selected.confirmation) });
|
|
471
|
+
}, { className: 'bb-minutes-button is-primary', disabled: Boolean(selected.confirmation) || !editAllowed });
|
|
397
472
|
actions.append(confirm);
|
|
398
473
|
section.append(actions);
|
|
474
|
+
if (!editAllowed) appendEditBlockedNotice(section);
|
|
399
475
|
}
|
|
400
476
|
appendVersionActionExtensions(doc, section, {
|
|
401
477
|
extensions: versionActionExtensions,
|
|
@@ -419,6 +495,10 @@ export function createMeetingMinutesUI({
|
|
|
419
495
|
}
|
|
420
496
|
|
|
421
497
|
function appendSaveForm(section, detail, documentRecord) {
|
|
498
|
+
if (!canEdit()) {
|
|
499
|
+
appendEditBlockedNotice(section);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
422
502
|
const form = makeElement(doc, 'form', { className: 'bb-minutes-save-form' });
|
|
423
503
|
form.append(makeElement(doc, 'h4', { textContent: '訂正版を保存' }));
|
|
424
504
|
const field = labelInput(doc, '本文(保存すると新しい版になります)', { type: 'textarea', name: 'body', placeholder: '議事録の本文を入力' });
|
|
@@ -427,6 +507,11 @@ export function createMeetingMinutesUI({
|
|
|
427
507
|
form.append(field.wrapper, save);
|
|
428
508
|
form.addEventListener('submit', async (event) => {
|
|
429
509
|
event.preventDefault();
|
|
510
|
+
if (!canEdit()) {
|
|
511
|
+
save.disabled = false;
|
|
512
|
+
appendEditBlockedNotice(section, { alert: true });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
430
515
|
save.disabled = true;
|
|
431
516
|
try {
|
|
432
517
|
await mutate(`/${encodeURIComponent(detail.meeting.meeting_id)}/minutes/${encodeURIComponent(documentRecord.minutes_id)}`, {
|
|
@@ -463,6 +548,8 @@ export function createMeetingMinutesUI({
|
|
|
463
548
|
const meeting = detail.meeting;
|
|
464
549
|
const header = makeElement(doc, 'header', { className: 'bb-minutes-detail-head' });
|
|
465
550
|
header.append(makeElement(doc, 'h2', { textContent: text(meeting.title, '無題の会議') }));
|
|
551
|
+
const label = meetingLabelText(meetingLabel, meeting);
|
|
552
|
+
if (label) header.append(makeElement(doc, 'p', { className: 'bb-minutes-meeting-label', textContent: label }));
|
|
466
553
|
header.append(makeElement(doc, 'p', { className: 'bb-minutes-item-meta', textContent: `${displayDate(meeting.scheduled_at)} / ${Array.isArray(meeting.participant_ids) && meeting.participant_ids.length ? meeting.participant_ids.join('、') : '参加者未登録'}` }));
|
|
467
554
|
detailPane.append(header);
|
|
468
555
|
const documents = Array.isArray(detail.minutes) ? detail.minutes : [];
|
|
@@ -474,6 +561,11 @@ export function createMeetingMinutesUI({
|
|
|
474
561
|
}
|
|
475
562
|
|
|
476
563
|
function renderCreateMinutesForm(detail) {
|
|
564
|
+
if (!canEdit()) {
|
|
565
|
+
const guard = makeElement(doc, 'section', { className: 'bb-minutes-edit-guard' });
|
|
566
|
+
appendEditBlockedNotice(guard);
|
|
567
|
+
return guard;
|
|
568
|
+
}
|
|
477
569
|
const form = makeElement(doc, 'form', { className: 'bb-minutes-create-form' });
|
|
478
570
|
form.append(makeElement(doc, 'h3', { textContent: '議事録文書を追加' }));
|
|
479
571
|
const title = labelInput(doc, '議事録の名前', { name: 'title', value: '議事録' });
|
|
@@ -483,6 +575,11 @@ export function createMeetingMinutesUI({
|
|
|
483
575
|
form.append(title.wrapper, body.wrapper, save);
|
|
484
576
|
form.addEventListener('submit', async (event) => {
|
|
485
577
|
event.preventDefault();
|
|
578
|
+
if (!canEdit()) {
|
|
579
|
+
save.disabled = false;
|
|
580
|
+
appendEditBlockedNotice(form, { alert: true });
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
486
583
|
save.disabled = true;
|
|
487
584
|
try {
|
|
488
585
|
await mutate(`/${encodeURIComponent(detail.meeting.meeting_id)}/minutes`, {
|
|
@@ -503,6 +600,10 @@ export function createMeetingMinutesUI({
|
|
|
503
600
|
|
|
504
601
|
function renderCreateForm() {
|
|
505
602
|
clear(detailPane);
|
|
603
|
+
if (!canCreate()) {
|
|
604
|
+
appendCreateBlockedNotice(detailPane, { alert: true });
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
506
607
|
const form = makeElement(doc, 'form', { className: 'bb-minutes-create-form' });
|
|
507
608
|
form.append(makeElement(doc, 'h2', { textContent: '会議を作成' }), makeElement(doc, 'p', { className: 'bb-minutes-item-meta', textContent: '外部サービスを接続しなくても、会議と議事録を保存できます。' }));
|
|
508
609
|
const title = labelInput(doc, '会議名', { name: 'title', placeholder: '例: 週次定例' });
|
|
@@ -515,6 +616,11 @@ export function createMeetingMinutesUI({
|
|
|
515
616
|
form.append(title.wrapper, date.wrapper, participants.wrapper, minutesTitle.wrapper, body.wrapper, save);
|
|
516
617
|
form.addEventListener('submit', async (event) => {
|
|
517
618
|
event.preventDefault();
|
|
619
|
+
if (!canCreate()) {
|
|
620
|
+
save.disabled = false;
|
|
621
|
+
appendCreateBlockedNotice(detailPane, { alert: true });
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
518
624
|
save.disabled = true;
|
|
519
625
|
try {
|
|
520
626
|
const scheduledAt = date.input.value ? new Date(date.input.value).toISOString() : undefined;
|
|
@@ -538,17 +644,21 @@ export function createMeetingMinutesUI({
|
|
|
538
644
|
detailPane.append(form);
|
|
539
645
|
}
|
|
540
646
|
|
|
541
|
-
async function loadMeetings() {
|
|
647
|
+
async function loadMeetings({ restoreFocus = false } = {}) {
|
|
648
|
+
const requestGeneration = ++listRequestGeneration;
|
|
649
|
+
const isCurrentRequest = () => requestGeneration === listRequestGeneration;
|
|
542
650
|
state.loading = true;
|
|
543
651
|
setStatus('会議一覧を読み込んでいます。');
|
|
544
652
|
try {
|
|
545
653
|
const payload = await call('');
|
|
654
|
+
if (!isCurrentRequest()) return state.meetings;
|
|
546
655
|
if (!isRecord(payload) || !Array.isArray(payload.meetings) || payload.absence_confirmed !== true) throw new Error('会議一覧の形式を確認できません。');
|
|
547
656
|
state.meetings = payload.meetings;
|
|
548
657
|
state.listError = null;
|
|
549
658
|
const selectedMeetingStillListed = state.meetingId !== null
|
|
550
659
|
&& state.meetings.some((entry) => entry?.meeting?.meeting_id === state.meetingId);
|
|
551
660
|
if (state.meetingId !== null && !selectedMeetingStillListed) {
|
|
661
|
+
detailRequestGeneration += 1;
|
|
552
662
|
state.detail = null;
|
|
553
663
|
state.meetingId = null;
|
|
554
664
|
state.minutesId = null;
|
|
@@ -562,13 +672,16 @@ export function createMeetingMinutesUI({
|
|
|
562
672
|
renderList();
|
|
563
673
|
if (state.detailError && state.detail === null) renderDetail(null);
|
|
564
674
|
setStatus(state.meetings.length ? `${state.meetings.length}件の会議を読み込みました。` : '保存された会議はありません。');
|
|
675
|
+
if (restoreFocus && typeof listPane.focus === 'function') listPane.focus({ preventScroll: true });
|
|
565
676
|
} catch (error) {
|
|
677
|
+
if (!isCurrentRequest()) return state.meetings;
|
|
566
678
|
state.listError = loadErrorMessage(error, '会議一覧を読み込めませんでした。');
|
|
567
679
|
const preserve = preserveLoadedStateOnFailure(error);
|
|
568
680
|
const hadDetail = Boolean(state.detail) || state.meetingId !== null;
|
|
569
681
|
if (!preserve) {
|
|
570
682
|
state.meetings = [];
|
|
571
683
|
if (hadDetail) {
|
|
684
|
+
detailRequestGeneration += 1;
|
|
572
685
|
state.detail = null;
|
|
573
686
|
state.meetingId = null;
|
|
574
687
|
state.minutesId = null;
|
|
@@ -580,12 +693,15 @@ export function createMeetingMinutesUI({
|
|
|
580
693
|
if (!preserve && hadDetail) renderDetail(null);
|
|
581
694
|
setStatus(state.listError, 'error');
|
|
582
695
|
} finally {
|
|
583
|
-
state.loading = false;
|
|
696
|
+
if (isCurrentRequest()) state.loading = false;
|
|
584
697
|
}
|
|
585
698
|
return state.meetings;
|
|
586
699
|
}
|
|
587
700
|
|
|
588
701
|
async function loadDetail(meetingId) {
|
|
702
|
+
const requestGeneration = ++detailRequestGeneration;
|
|
703
|
+
const isCurrentRequest = () => requestGeneration === detailRequestGeneration
|
|
704
|
+
&& state.meetingId === meetingId;
|
|
589
705
|
state.meetingId = meetingId;
|
|
590
706
|
state.minutesId = null;
|
|
591
707
|
state.detailError = null;
|
|
@@ -593,12 +709,14 @@ export function createMeetingMinutesUI({
|
|
|
593
709
|
setStatus('会議詳細を読み込んでいます。');
|
|
594
710
|
try {
|
|
595
711
|
const detail = await call(`/${encodeURIComponent(meetingId)}`);
|
|
712
|
+
if (!isCurrentRequest()) return state.detail;
|
|
596
713
|
if (!isRecord(detail) || !isRecord(detail.meeting) || !Array.isArray(detail.minutes) || !Array.isArray(detail.versions)) throw new Error('会議詳細の形式を確認できません。');
|
|
597
714
|
state.detail = detail;
|
|
598
715
|
state.detailError = null;
|
|
599
716
|
renderDetail(detail);
|
|
600
717
|
setStatus('会議詳細を読み込みました。');
|
|
601
718
|
} catch (error) {
|
|
719
|
+
if (!isCurrentRequest()) return state.detail;
|
|
602
720
|
const preserved = preserveLoadedStateOnFailure(error) && state.detail?.meeting?.meeting_id === meetingId
|
|
603
721
|
? state.detail
|
|
604
722
|
: null;
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* Constraint links are only read here; there is no replace method.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { normalizeObjectiveReadback } from './objective-readback-contract.js';
|
|
14
|
+
|
|
13
15
|
export const OBJECTIVE_EDITOR_HTTP_PORT_VERSION = 'brainbase.objective-editor-http-port.v1';
|
|
14
16
|
|
|
15
17
|
/** Top-level fields the Foundation route rejects with `authority_field_in_body`. */
|
|
@@ -93,7 +95,7 @@ export function createObjectiveEditorHttpPort({
|
|
|
93
95
|
: typeof globalThis.fetch === 'function' ? (path, init) => globalThis.fetch(path, init) : null;
|
|
94
96
|
const base = String(basePath).replace(/\/+$/u, '');
|
|
95
97
|
|
|
96
|
-
async function call(path, init) {
|
|
98
|
+
async function call(path, init, { allowObjectiveReadback = false, expected } = {}) {
|
|
97
99
|
if (!request) throw new ObjectiveEditorHttpError({ code: 'network_error', status: 0, message: ERROR_MESSAGES.network_error });
|
|
98
100
|
let response;
|
|
99
101
|
try {
|
|
@@ -107,7 +109,14 @@ export function createObjectiveEditorHttpPort({
|
|
|
107
109
|
} catch {
|
|
108
110
|
payload = null;
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
// Detail GETs use a typed envelope for both a canonical record and a
|
|
113
|
+
// confirmed miss/read failure. Accept that envelope even when its HTTP
|
|
114
|
+
// status is 404/5xx; every other non-2xx response remains an error.
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
const readback = allowObjectiveReadback ? normalizeObjectiveReadback(payload, expected) : null;
|
|
117
|
+
if (readback) return readback;
|
|
118
|
+
throw errorFromResponse(response.status, payload);
|
|
119
|
+
}
|
|
111
120
|
if (payload === null) throw new ObjectiveEditorHttpError({ code: 'invalid_response', status: response.status, message: ERROR_MESSAGES.invalid_response });
|
|
112
121
|
return payload;
|
|
113
122
|
}
|
|
@@ -128,13 +137,17 @@ export function createObjectiveEditorHttpPort({
|
|
|
128
137
|
return payload;
|
|
129
138
|
},
|
|
130
139
|
async readObjective(id, _context, revision) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
const expected = { id, revision };
|
|
141
|
+
const payload = await call(withRevision(objectivePath(id), revision), undefined, { allowObjectiveReadback: true, expected });
|
|
142
|
+
const readback = normalizeObjectiveReadback(payload, expected);
|
|
143
|
+
if (!readback) {
|
|
144
|
+
throw new ObjectiveEditorHttpError({
|
|
145
|
+
code: 'invalid_response',
|
|
146
|
+
status: 200,
|
|
147
|
+
message: ERROR_MESSAGES.invalid_response,
|
|
148
|
+
});
|
|
137
149
|
}
|
|
150
|
+
return readback;
|
|
138
151
|
},
|
|
139
152
|
checkObjectiveReadiness(id, _context, revision) {
|
|
140
153
|
return call(withRevision(objectivePath(id, '/readiness'), revision));
|