Cancel vault rereading without persisting a false document error
This commit is contained in:
@@ -545,3 +545,13 @@ Teklif Asistanı dosya okuması görünümün yaşam süresi sinyalini kullanır
|
||||
Testler: bekleyen OCR sırasında iptalde tek sayfa ve temizlik; geciken sayfa ediniminde temizlik; geç sonuçta iptal callback'inin ikinci kez çağrılmaması; normal tamamlanma sonrasında dinleyici temizliği. Mevcut gerçek 45 sayfalık PDF okuma testi de geçti. Gerçek tarayıcıda Tesseract çalışanı sırasında iptal kabulü bu turda yapılmadı.
|
||||
|
||||
Sonuç: Tüm 122 test ve üretim derlemesi geçti. Büyük paket uyarıları sürüyor. Planın kalan kapsamı açık; canlı yayın yapılmadı.
|
||||
|
||||
### D01 / D06 — Depodan yeniden okumada iptal ve özel metin koruması
|
||||
|
||||
Kayıtlı belge okuyucusu isteğe bağlı iptal sinyalini indirme, yanıt gövdesi ve PDF/OCR çıkarımına taşır. Başlangıç oturumu sabitlenir; belge metni döndürülmeden ve PDF işlemine geçilmeden güncel oturum kontrol edilir. DocumentVault yeniden çıkarımı ekranın yaşam süresi sinyalini kullanır; ekran kapanınca okuma durdurulur.
|
||||
|
||||
Yeniden çıkarımda kullanıcı iptali gerçek okuma hatası gibi kaydedilmez. İptal ve hesap değişimi sonrası `read-error` veya ortak kalem yayınına devam edilmez. Ağ/dosya hatasının mevcut sürüm kontrollü hata kaydı davranışı korunur.
|
||||
|
||||
Test: gerçek yardımcı fonksiyon üzerinden iptal edilen DOCX okumasında yalnız bir metin isteği, sıfır hata kaydı/kalem yayını; hesap değişimi sonrasında gelen özel Office metninin reddi. Diğer belge ekranları ve çalışan Office sunucu çıkarımının iptali bu turda tamamlanmadı; tarayıcıdan depo kapanış kabulü açık.
|
||||
|
||||
Sonuç: Tüm 124 test ve üretim derlemesi geçti. Büyük paket uyarıları sürüyor. Plan henüz tamamlanmadı; canlı yayın yapılmadı.
|
||||
|
||||
@@ -21,9 +21,10 @@ export const DocumentVault = () => {
|
||||
|
||||
const DocumentVaultSession = ({signedIn}) => {
|
||||
const live = useRef(true);
|
||||
const readLifetime = useRef(new AbortController());
|
||||
const session = useRef(sessionGuard(getToken));
|
||||
const current = () => live.current && session.current();
|
||||
useEffect(() => { live.current = true; return () => { live.current = false; }; }, []);
|
||||
useEffect(() => { live.current = true; readLifetime.current = new AbortController(); return () => { live.current = false; readLifetime.current.abort(); }; }, []);
|
||||
const useOwnedState = (initial) => {
|
||||
const [value, setValue] = useState(initial);
|
||||
const setOwned = useCallback((next) => { if (current()) setValue(next); }, []);
|
||||
@@ -47,7 +48,7 @@ const DocumentVaultSession = ({signedIn}) => {
|
||||
if (!current()) return;
|
||||
setBusy(true); setHata(null); setIslemeBilgi(null);
|
||||
try {
|
||||
const result = await retryDocumentExtraction(document);
|
||||
const result = await retryDocumentExtraction(document,{signal:readLifetime.current.signal});
|
||||
setIslemeBilgi(`${document.ad}: ${result.sayi || 0} kalem çıkarıldı.`);
|
||||
} catch (error) { setHata(error.message); }
|
||||
finally {
|
||||
|
||||
@@ -1,35 +1,45 @@
|
||||
import {awaitWithAbort} from './abortable.js';
|
||||
import { getToken } from './auth.js';
|
||||
import { reportDocumentReadError } from './account.js';
|
||||
|
||||
export const readableDocument = (name) => /\.(pdf|txt|md|docx|xlsx|zip)$/i.test(String(name || ''));
|
||||
|
||||
export async function inspectStoredDocument(document) {
|
||||
export async function inspectStoredDocument(document,{signal}={}) {
|
||||
const token=getToken();
|
||||
const check=()=>{signal?.throwIfAborted();if(!token || token!==getToken())throw new Error('Oturum değişti; belgeyi yeniden açın.');};
|
||||
check();
|
||||
if (!readableDocument(document.ad)) throw new Error('Bu dosya biçiminden metin çıkarımı desteklenmiyor.');
|
||||
const office = /\.(docx|xlsx|zip)$/i.test(document.ad);
|
||||
const response = await fetch(`/api/me/documents/${office ? 'text' : 'download'}?id=${encodeURIComponent(document.id)}`, {
|
||||
headers: {Authorization:`Bearer ${getToken()}`},
|
||||
signal, headers: {Authorization:`Bearer ${token}`},
|
||||
});
|
||||
if (office) {
|
||||
const result = await response.json().catch(() => ({}));
|
||||
const result = await awaitWithAbort(response.json(),signal);
|
||||
check();
|
||||
if (!response.ok || !result.success) throw new Error(result.error || 'Belge metni alınamadı.');
|
||||
return {text:result.metin,format:document.ad.split('.').pop()};
|
||||
}
|
||||
if (!response.ok) throw new Error('Belge indirilemedi.');
|
||||
const blob = await response.blob();
|
||||
const blob = await awaitWithAbort(response.blob(),signal);
|
||||
check();
|
||||
const { extractText } = await import('./pdfText');
|
||||
return extractText(new File([blob],document.ad,{type:document.mime || 'application/octet-stream'}));
|
||||
check();
|
||||
const result=await extractText(new File([blob],document.ad,{type:document.mime || 'application/octet-stream'}),{signal});
|
||||
check();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function readStoredDocument(document) { return (await inspectStoredDocument(document)).text; }
|
||||
export async function readStoredDocument(document,options) { return (await inspectStoredDocument(document,options)).text; }
|
||||
|
||||
export async function retryDocumentExtraction(document) {
|
||||
export async function retryDocumentExtraction(document,{signal}={}) {
|
||||
if (document.tur !== 'ihale' || !document.ikn) throw new Error('İhaleye bağlı ihale dokümanı gerekli.');
|
||||
const token = getToken();
|
||||
const checkSession = () => { if (!token || token !== getToken()) throw new Error('Oturum değişti; belgeyi yeniden açın.'); };
|
||||
const checkSession = () => { signal?.throwIfAborted(); if (!token || token !== getToken()) throw new Error('Oturum değişti; belgeyi yeniden açın.'); };
|
||||
checkSession();
|
||||
let text;
|
||||
try { text = await readStoredDocument(document); }
|
||||
try { text = await readStoredDocument(document,{signal}); }
|
||||
catch (error) {
|
||||
if(error.name === 'AbortError') throw error;
|
||||
checkSession();
|
||||
try { await reportDocumentReadError(document.id,document.isleme); }
|
||||
catch { throw new Error(`${error.message} (hata durumu kaydedilemedi; listeyi yenileyin)`); }
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import {test} from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {retryDocumentExtraction,inspectStoredDocument} from '../src/services/storedDocument.js';
|
||||
test('cancelling stored document reading never writes a read error or publishes items',async()=>{
|
||||
const storage=Object.getOwnPropertyDescriptor(globalThis,'localStorage'),original=globalThis.fetch;
|
||||
Object.defineProperty(globalThis,'localStorage',{configurable:true,value:{getItem:()=>'qa-token'}});
|
||||
const c=new AbortController();const calls=[];
|
||||
try {
|
||||
globalThis.fetch=async(url,{signal})=>{calls.push(url);return new Promise((resolve,reject)=>{
|
||||
signal.addEventListener('abort',()=>reject(new DOMException('Aborted','AbortError')),{once:true});c.abort();
|
||||
});};
|
||||
await assert.rejects(retryDocumentExtraction({id:'doc',ad:'source.docx',tur:'ihale',ikn:'2026/123'},{signal:c.signal}),{name:'AbortError'});
|
||||
assert.deepEqual(calls,['/api/me/documents/text?id=doc']);
|
||||
} finally {globalThis.fetch=original;if(storage)Object.defineProperty(globalThis,'localStorage',storage);else delete globalThis.localStorage;}
|
||||
});
|
||||
test('late private office text is discarded when account changes',async()=>{
|
||||
const storage=Object.getOwnPropertyDescriptor(globalThis,'localStorage'),original=globalThis.fetch;let token='first';
|
||||
Object.defineProperty(globalThis,'localStorage',{configurable:true,value:{getItem:()=>token}});
|
||||
try {
|
||||
globalThis.fetch=async()=>{token='second';return Response.json({success:true,metin:'Private source'});};
|
||||
await assert.rejects(inspectStoredDocument({id:'doc',ad:'source.docx'}),/Oturum değişti/);
|
||||
} finally {globalThis.fetch=original;if(storage)Object.defineProperty(globalThis,'localStorage',storage);else delete globalThis.localStorage;}
|
||||
});
|
||||
Reference in New Issue
Block a user