@ttt-productions/upload-core 0.7.6 → 0.7.7

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.
@@ -14,5 +14,7 @@ export declare function removeUploadSession(id: string): void;
14
14
  export declare function subscribeUploadSession(id: string, fn: Listener): () => void;
15
15
  /** Subscribe to changes in the *list* (new/removed sessions, rehydrate). */
16
16
  export declare function subscribeUploadSessionsList(fn: ListListener): () => void;
17
+ /** Test-only: empties every session, listener, and persistence binding. Never call in production code. */
18
+ export declare function __resetUploadStore(): void;
17
19
  export {};
18
20
  //# sourceMappingURL=upload-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload-store.d.ts","sourceRoot":"","sources":["../../src/utils/upload-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAEvF,KAAK,QAAQ,GAAG,CAAC,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAChD,KAAK,YAAY,GAAG,MAAM,IAAI,CAAC;AAQ/B,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,+BAA+B,GAAG,IAAI,QAE1F;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,EAC5B,EAAE,EAAE,MAAM,KACP,IAAI,CAAC;AAIV,wBAAgB,uCAAuC,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,QAE9F;AA4BD,wBAAgB,sBAAsB,WA4BrC;AAED,wDAAwD;AACxD,wBAAsB,uBAAuB,kBAgB5C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE3E;AAED,wBAAgB,kBAAkB,IAAI,kBAAkB,EAAE,CAEzD;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,QA+CtE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,QAK7C;AAMD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,MAAM,IAAI,CAiB3E;AAED,4EAA4E;AAC5E,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,IAAI,CAGxE"}
1
+ {"version":3,"file":"upload-store.d.ts","sourceRoot":"","sources":["../../src/utils/upload-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAEvF,KAAK,QAAQ,GAAG,CAAC,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAChD,KAAK,YAAY,GAAG,MAAM,IAAI,CAAC;AAQ/B,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,+BAA+B,GAAG,IAAI,QAE1F;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,EAC5B,EAAE,EAAE,MAAM,KACP,IAAI,CAAC;AAIV,wBAAgB,uCAAuC,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,QAE9F;AA4BD,wBAAgB,sBAAsB,WA4BrC;AAED,wDAAwD;AACxD,wBAAsB,uBAAuB,kBAgB5C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE3E;AAED,wBAAgB,kBAAkB,IAAI,kBAAkB,EAAE,CAEzD;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,QA+CtE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,QAK7C;AAMD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,MAAM,IAAI,CAiB3E;AAED,4EAA4E;AAC5E,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,IAAI,CAGxE;AAED,0GAA0G;AAC1G,wBAAgB,kBAAkB,IAAI,IAAI,CAMzC"}
@@ -159,4 +159,12 @@ export function subscribeUploadSessionsList(fn) {
159
159
  listListeners.add(fn);
160
160
  return () => listListeners.delete(fn);
161
161
  }
162
+ /** Test-only: empties every session, listener, and persistence binding. Never call in production code. */
163
+ export function __resetUploadStore() {
164
+ sessions.clear();
165
+ listeners.clear();
166
+ listListeners.clear();
167
+ persistence = null;
168
+ persistenceErrorHandler = null;
169
+ }
162
170
  //# sourceMappingURL=upload-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload-store.js","sourceRoot":"","sources":["../../src/utils/upload-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { now } from '@ttt-productions/firebase-helpers';\nimport type { UploadSessionState, UploadSessionPersistenceAdapter } from '../types.js';\n\ntype Listener = (s: UploadSessionState) => void;\ntype ListListener = () => void;\n\nconst sessions = new Map<string, UploadSessionState>();\nconst listeners = new Map<string, Set<Listener>>();\nconst listListeners = new Set<ListListener>();\n\nlet persistence: UploadSessionPersistenceAdapter | null = null;\n\nexport function setUploadSessionPersistence(adapter: UploadSessionPersistenceAdapter | null) {\n persistence = adapter;\n}\n\nexport type PersistenceErrorHandler = (\n err: unknown,\n op: 'set' | 'remove' | 'get',\n id: string,\n) => void;\n\nlet persistenceErrorHandler: PersistenceErrorHandler | null = null;\n\nexport function setUploadSessionPersistenceErrorHandler(handler: PersistenceErrorHandler | null) {\n persistenceErrorHandler = handler;\n}\n\nasync function persistUpsert(state: UploadSessionState) {\n try {\n await persistence?.set(state.id, state);\n } catch (err) {\n console.error('[upload-store] Failed to persist session:', state.id, err);\n persistenceErrorHandler?.(err, 'set', state.id);\n }\n}\n\nasync function persistRemove(id: string) {\n try {\n await persistence?.remove(id);\n } catch (err) {\n console.error('[upload-store] Failed to remove persisted session:', id, err);\n persistenceErrorHandler?.(err, 'remove', id);\n }\n}\n\nfunction notifyList() {\n for (const fn of listListeners) fn();\n}\n\nconst MAX_SESSIONS = 100;\nconst SUCCESS_TTL_MS = 24 * 60 * 60 * 1000; // 24h\nconst ERROR_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7d\n\nexport function pruneOldUploadSessions() {\n const t = now();\n const all = listUploadSessions();\n\n let pruned = 0;\n\n for (const s of all) {\n if (s.status === 'success' && t - s.updatedAt > SUCCESS_TTL_MS) {\n removeUploadSession(s.id);\n pruned++;\n } else if (\n (s.status === 'error' || s.status === 'canceled') &&\n t - s.updatedAt > ERROR_TTL_MS\n ) {\n removeUploadSession(s.id);\n pruned++;\n }\n }\n\n const remaining = listUploadSessions();\n if (remaining.length > MAX_SESSIONS) {\n for (const s of remaining.slice(MAX_SESSIONS)) {\n removeUploadSession(s.id);\n pruned++;\n }\n }\n\n return pruned;\n}\n\n/** Load persisted sessions into the in-memory store. */\nexport async function rehydrateUploadSessions() {\n if (!persistence) return;\n\n const ids = await persistence.listIds();\n for (const id of ids) {\n try {\n const s = await persistence.get(id);\n if (s) sessions.set(id, s);\n } catch (err) {\n console.error('[upload-store] Failed to read persisted session:', id, err);\n persistenceErrorHandler?.(err, 'get', id);\n }\n }\n\n pruneOldUploadSessions();\n notifyList();\n}\n\nexport function getUploadSession(id: string): UploadSessionState | undefined {\n return sessions.get(id);\n}\n\nexport function listUploadSessions(): UploadSessionState[] {\n return Array.from(sessions.values()).sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nexport function upsertUploadSession(\n partial: Partial<UploadSessionState> & Pick<UploadSessionState, 'id'>,\n) {\n const prev = sessions.get(partial.id);\n\n // Guard against out-of-order async updates.\n if (prev && partial.updatedAt != null && partial.updatedAt < prev.updatedAt) return;\n\n const prevStatus = prev?.status ?? 'idle';\n const nextStatus = partial.status ?? prevStatus;\n\n const isTerminal = (s: string) => s === 'success' || s === 'error' || s === 'canceled';\n const prevIsTerminal = isTerminal(prevStatus);\n const nextIsTerminal = isTerminal(nextStatus);\n\n // Once terminal, stay terminal. If we receive a terminal nextStatus first, accept it.\n const status = prevIsTerminal ? prevStatus : nextIsTerminal ? nextStatus : nextStatus;\n\n // Progress values should never go backwards.\n const transferred = Math.max(partial.transferred ?? 0, prev?.transferred ?? 0);\n const total = Math.max(partial.total ?? 0, prev?.total ?? 0);\n const percent = Math.max(partial.percent ?? 0, prev?.percent ?? 0);\n\n const version = (prev?.version ?? 0) + 1;\n\n const next: UploadSessionState = {\n id: partial.id,\n status,\n path: partial.path ?? prev?.path ?? '',\n version,\n transferred,\n total,\n percent,\n startedAt: partial.startedAt ?? prev?.startedAt ?? now(),\n updatedAt: partial.updatedAt ?? now(),\n error: partial.error ?? prev?.error,\n result: partial.result ?? prev?.result,\n };\n\n const wasMissing = !sessions.has(partial.id);\n\n sessions.set(partial.id, next);\n void persistUpsert(next);\n\n const ls = listeners.get(partial.id);\n if (ls) for (const fn of ls) fn(next);\n\n if (wasMissing) notifyList();\n}\n\nexport function removeUploadSession(id: string) {\n const existed = sessions.delete(id);\n clearUploadSessionListeners(id);\n void persistRemove(id);\n if (existed) notifyList();\n}\n\nfunction clearUploadSessionListeners(id: string) {\n if (listeners.has(id)) listeners.delete(id);\n}\n\nexport function subscribeUploadSession(id: string, fn: Listener): () => void {\n let set = listeners.get(id);\n if (!set) {\n set = new Set();\n listeners.set(id, set);\n }\n set.add(fn);\n\n const current = sessions.get(id);\n if (current) fn(current);\n\n return () => {\n const s = listeners.get(id);\n if (!s) return;\n s.delete(fn);\n if (s.size === 0) listeners.delete(id);\n };\n}\n\n/** Subscribe to changes in the *list* (new/removed sessions, rehydrate). */\nexport function subscribeUploadSessionsList(fn: ListListener): () => void {\n listListeners.add(fn);\n return () => listListeners.delete(fn);\n}\n"]}
1
+ {"version":3,"file":"upload-store.js","sourceRoot":"","sources":["../../src/utils/upload-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { now } from '@ttt-productions/firebase-helpers';\nimport type { UploadSessionState, UploadSessionPersistenceAdapter } from '../types.js';\n\ntype Listener = (s: UploadSessionState) => void;\ntype ListListener = () => void;\n\nconst sessions = new Map<string, UploadSessionState>();\nconst listeners = new Map<string, Set<Listener>>();\nconst listListeners = new Set<ListListener>();\n\nlet persistence: UploadSessionPersistenceAdapter | null = null;\n\nexport function setUploadSessionPersistence(adapter: UploadSessionPersistenceAdapter | null) {\n persistence = adapter;\n}\n\nexport type PersistenceErrorHandler = (\n err: unknown,\n op: 'set' | 'remove' | 'get',\n id: string,\n) => void;\n\nlet persistenceErrorHandler: PersistenceErrorHandler | null = null;\n\nexport function setUploadSessionPersistenceErrorHandler(handler: PersistenceErrorHandler | null) {\n persistenceErrorHandler = handler;\n}\n\nasync function persistUpsert(state: UploadSessionState) {\n try {\n await persistence?.set(state.id, state);\n } catch (err) {\n console.error('[upload-store] Failed to persist session:', state.id, err);\n persistenceErrorHandler?.(err, 'set', state.id);\n }\n}\n\nasync function persistRemove(id: string) {\n try {\n await persistence?.remove(id);\n } catch (err) {\n console.error('[upload-store] Failed to remove persisted session:', id, err);\n persistenceErrorHandler?.(err, 'remove', id);\n }\n}\n\nfunction notifyList() {\n for (const fn of listListeners) fn();\n}\n\nconst MAX_SESSIONS = 100;\nconst SUCCESS_TTL_MS = 24 * 60 * 60 * 1000; // 24h\nconst ERROR_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7d\n\nexport function pruneOldUploadSessions() {\n const t = now();\n const all = listUploadSessions();\n\n let pruned = 0;\n\n for (const s of all) {\n if (s.status === 'success' && t - s.updatedAt > SUCCESS_TTL_MS) {\n removeUploadSession(s.id);\n pruned++;\n } else if (\n (s.status === 'error' || s.status === 'canceled') &&\n t - s.updatedAt > ERROR_TTL_MS\n ) {\n removeUploadSession(s.id);\n pruned++;\n }\n }\n\n const remaining = listUploadSessions();\n if (remaining.length > MAX_SESSIONS) {\n for (const s of remaining.slice(MAX_SESSIONS)) {\n removeUploadSession(s.id);\n pruned++;\n }\n }\n\n return pruned;\n}\n\n/** Load persisted sessions into the in-memory store. */\nexport async function rehydrateUploadSessions() {\n if (!persistence) return;\n\n const ids = await persistence.listIds();\n for (const id of ids) {\n try {\n const s = await persistence.get(id);\n if (s) sessions.set(id, s);\n } catch (err) {\n console.error('[upload-store] Failed to read persisted session:', id, err);\n persistenceErrorHandler?.(err, 'get', id);\n }\n }\n\n pruneOldUploadSessions();\n notifyList();\n}\n\nexport function getUploadSession(id: string): UploadSessionState | undefined {\n return sessions.get(id);\n}\n\nexport function listUploadSessions(): UploadSessionState[] {\n return Array.from(sessions.values()).sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nexport function upsertUploadSession(\n partial: Partial<UploadSessionState> & Pick<UploadSessionState, 'id'>,\n) {\n const prev = sessions.get(partial.id);\n\n // Guard against out-of-order async updates.\n if (prev && partial.updatedAt != null && partial.updatedAt < prev.updatedAt) return;\n\n const prevStatus = prev?.status ?? 'idle';\n const nextStatus = partial.status ?? prevStatus;\n\n const isTerminal = (s: string) => s === 'success' || s === 'error' || s === 'canceled';\n const prevIsTerminal = isTerminal(prevStatus);\n const nextIsTerminal = isTerminal(nextStatus);\n\n // Once terminal, stay terminal. If we receive a terminal nextStatus first, accept it.\n const status = prevIsTerminal ? prevStatus : nextIsTerminal ? nextStatus : nextStatus;\n\n // Progress values should never go backwards.\n const transferred = Math.max(partial.transferred ?? 0, prev?.transferred ?? 0);\n const total = Math.max(partial.total ?? 0, prev?.total ?? 0);\n const percent = Math.max(partial.percent ?? 0, prev?.percent ?? 0);\n\n const version = (prev?.version ?? 0) + 1;\n\n const next: UploadSessionState = {\n id: partial.id,\n status,\n path: partial.path ?? prev?.path ?? '',\n version,\n transferred,\n total,\n percent,\n startedAt: partial.startedAt ?? prev?.startedAt ?? now(),\n updatedAt: partial.updatedAt ?? now(),\n error: partial.error ?? prev?.error,\n result: partial.result ?? prev?.result,\n };\n\n const wasMissing = !sessions.has(partial.id);\n\n sessions.set(partial.id, next);\n void persistUpsert(next);\n\n const ls = listeners.get(partial.id);\n if (ls) for (const fn of ls) fn(next);\n\n if (wasMissing) notifyList();\n}\n\nexport function removeUploadSession(id: string) {\n const existed = sessions.delete(id);\n clearUploadSessionListeners(id);\n void persistRemove(id);\n if (existed) notifyList();\n}\n\nfunction clearUploadSessionListeners(id: string) {\n if (listeners.has(id)) listeners.delete(id);\n}\n\nexport function subscribeUploadSession(id: string, fn: Listener): () => void {\n let set = listeners.get(id);\n if (!set) {\n set = new Set();\n listeners.set(id, set);\n }\n set.add(fn);\n\n const current = sessions.get(id);\n if (current) fn(current);\n\n return () => {\n const s = listeners.get(id);\n if (!s) return;\n s.delete(fn);\n if (s.size === 0) listeners.delete(id);\n };\n}\n\n/** Subscribe to changes in the *list* (new/removed sessions, rehydrate). */\nexport function subscribeUploadSessionsList(fn: ListListener): () => void {\n listListeners.add(fn);\n return () => listListeners.delete(fn);\n}\n\n/** Test-only: empties every session, listener, and persistence binding. Never call in production code. */\nexport function __resetUploadStore(): void {\n sessions.clear();\n listeners.clear();\n listListeners.clear();\n persistence = null;\n persistenceErrorHandler = null;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/upload-core",
3
- "version": "0.7.6",
3
+ "version": "0.7.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",
@@ -31,7 +31,7 @@
31
31
  "prepublishOnly": "npm run clean && npm run build"
32
32
  },
33
33
  "dependencies": {
34
- "@ttt-productions/firebase-helpers": "^0.12.13",
34
+ "@ttt-productions/firebase-helpers": "^0.12.14",
35
35
  "@ttt-productions/media-schemas": "^0.14.11"
36
36
  },
37
37
  "peerDependencies": {