function mergeEligibility(detail) { if (!detail || detail.state !== 'open' || detail.merged) { return { allowed: false, reason: 'Pull request is not open' }; } if (detail.draft) return { allowed: false, reason: 'Draft pull requests cannot be merged' }; if (!detail.mergeable) return { allowed: false, reason: 'Resolve conflicts before merging' }; if (detail.ci_state !== 'success') { return { allowed: false, reason: 'CI must succeed before merging' }; } if (!detail.head_sha) return { allowed: false, reason: 'Current head is unavailable' }; return { allowed: true, reason: 'Ready to merge' }; } function createPullSheet({ fetchJson, storage }) { let commentRequest = null; let mergeRequest = null; const pathFor = item => 'api/v1/repos/' + String(item.repository || '').split('/') .map(encodeURIComponent).join('/') + '/pulls/' + encodeURIComponent(item.number); const draftKey = item => 'stackchain.pull-comment.v1:' + item.repository + '#' + item.number; return { load(item) { return fetchJson(pathFor(item) + '/detail', { headers: { Accept: 'application/json' } }); }, loadDraft(item) { try { return storage?.getItem(draftKey(item)) || ''; } catch (_error) { return ''; } }, saveDraft(item, body) { try { storage?.setItem(draftKey(item), body); } catch (_error) { /* The textarea remains the fallback. */ } }, comment(item, body) { if (commentRequest) return commentRequest; this.saveDraft(item, body); commentRequest = fetchJson(pathFor(item) + '/comments', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ body }), }).then(result => { try { storage?.removeItem(draftKey(item)); } catch (_error) { /* The upstream comment is authoritative. */ } return result; }).finally(() => { commentRequest = null; }); return commentRequest; }, merge(item, expectedHeadSha) { if (mergeRequest) return mergeRequest; mergeRequest = fetchJson(pathFor(item) + '/merge', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ expected_head_sha: expectedHeadSha }), }).then(result => { if (!result?.merged) throw new Error('Pull request merge was not confirmed.'); return result; }).finally(() => { mergeRequest = null; }); return mergeRequest; }, }; } createPullSheet.mergeEligibility = mergeEligibility; if (typeof module !== 'undefined' && module.exports) module.exports = createPullSheet;