v2.2.297: stocks 목록 클라우드 백업 — Sheets _backup 탭 자동 백업 + /stocks backup·restore
로컬 .astra/stocks.json 뿐이던 워치리스트를 spreadsheet _backup 탭에 원본 JSON 그대로 자동 백업(변경 시 30초 디바운스). 새 컴퓨터에서는 /stocks restore 한 번으로 복원. sheetsApi 에 ensureSheetTab/clearSheetRange 추가, writeStocksStore 성공 시 변경 리스너 발화로 모든 변경 경로 포착. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,28 @@ import type { Stock, StocksStore } from './types';
|
||||
|
||||
const STORE_REL_PATH = '.astra/stocks.json';
|
||||
|
||||
/**
|
||||
* 저장 성공 시 알림 리스너 (v2.2.297) — 모든 변경 경로(add/remove/update/check/
|
||||
* discover 자동편입/워처 가격갱신)가 writeStocksStore 한 곳을 지나므로, 여기서
|
||||
* fire 하면 Sheets 자동 백업이 변경을 빠짐없이 따라간다.
|
||||
* 리스너 예외는 삼킨다 — 백업 실패가 저장 자체를 깨면 안 됨.
|
||||
*/
|
||||
type StoreChangeListener = () => void;
|
||||
const changeListeners = new Set<StoreChangeListener>();
|
||||
|
||||
export function onStocksStoreChanged(listener: StoreChangeListener): { dispose(): void } {
|
||||
changeListeners.add(listener);
|
||||
return { dispose: () => { changeListeners.delete(listener); } };
|
||||
}
|
||||
|
||||
function fireStoreChanged(): void {
|
||||
for (const l of changeListeners) {
|
||||
try { l(); } catch (e: any) {
|
||||
logError('stocks store 변경 리스너 예외.', { error: e?.message ?? String(e) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getStocksFilePath(): string | null {
|
||||
const folders = vscode.workspace.workspaceFolders;
|
||||
if (!folders || folders.length === 0) return null;
|
||||
@@ -41,8 +63,12 @@ export function readStocksStore(): StocksStore {
|
||||
}
|
||||
}
|
||||
|
||||
/** Atomic write — tmp + rename. 워크스페이스 없으면 false 반환 (caller 가 안내). */
|
||||
export function writeStocksStore(store: StocksStore): boolean {
|
||||
/**
|
||||
* Atomic write — tmp + rename. 워크스페이스 없으면 false 반환 (caller 가 안내).
|
||||
* opts.silent: 변경 리스너 미발화 — restore 처럼 백업에서 방금 읽어온 내용을
|
||||
* 다시 백업하는 루프를 끊을 때만 사용.
|
||||
*/
|
||||
export function writeStocksStore(store: StocksStore, opts?: { silent?: boolean }): boolean {
|
||||
const filePath = getStocksFilePath();
|
||||
if (!filePath) {
|
||||
logError('워크스페이스 폴더가 없어 stocks.json 쓰기 불가.');
|
||||
@@ -54,6 +80,7 @@ export function writeStocksStore(store: StocksStore): boolean {
|
||||
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
||||
fs.writeFileSync(tmp, JSON.stringify(store, null, 2), 'utf-8');
|
||||
fs.renameSync(tmp, filePath);
|
||||
if (!opts?.silent) fireStoreChanged();
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
logError('stocks.json 쓰기 실패.', { filePath, error: e?.message ?? String(e) });
|
||||
|
||||
Reference in New Issue
Block a user