lowdata 0.1.0 → 0.2.0

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.
Files changed (42) hide show
  1. package/README.md +37 -4
  2. package/dist/{client-CqO3Y1J5.d.cts → client-BmsR0Pf6.d.cts} +8 -2
  3. package/dist/{client-BvdWSYIM.d.ts → client-DSKoYUj9.d.ts} +8 -2
  4. package/dist/forms.cjs +29 -11
  5. package/dist/forms.cjs.map +1 -1
  6. package/dist/forms.d.cts +4 -4
  7. package/dist/forms.d.ts +4 -4
  8. package/dist/forms.js +29 -11
  9. package/dist/forms.js.map +1 -1
  10. package/dist/index.cjs +29 -11
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +4 -4
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +29 -11
  15. package/dist/index.js.map +1 -1
  16. package/dist/media.cjs +1 -1
  17. package/dist/media.cjs.map +1 -1
  18. package/dist/media.d.cts +2 -2
  19. package/dist/media.d.ts +2 -2
  20. package/dist/media.js +1 -1
  21. package/dist/media.js.map +1 -1
  22. package/dist/network.cjs +29 -11
  23. package/dist/network.cjs.map +1 -1
  24. package/dist/network.d.cts +7 -6
  25. package/dist/network.d.ts +7 -6
  26. package/dist/network.js +29 -11
  27. package/dist/network.js.map +1 -1
  28. package/dist/{progressiveImage-BhY_K8Dj.d.ts → progressiveImage-BOdic4dJ.d.ts} +1 -1
  29. package/dist/{progressiveImage-BWNdewbi.d.cts → progressiveImage-CDpz_rv3.d.cts} +1 -1
  30. package/dist/react.cjs +29 -11
  31. package/dist/react.cjs.map +1 -1
  32. package/dist/react.d.cts +4 -4
  33. package/dist/react.d.ts +4 -4
  34. package/dist/react.js +29 -11
  35. package/dist/react.js.map +1 -1
  36. package/dist/{retry-C3zL9T5Z.d.cts → retry-BnuESos0.d.cts} +1 -1
  37. package/dist/{retry-D6DfKGOi.d.ts → retry-CnvZhHan.d.ts} +1 -1
  38. package/dist/{types-Bn1BAcch.d.ts → types-1ha2HIfv.d.ts} +1 -1
  39. package/dist/{types-CZDYS-fB.d.cts → types-Cbm1nNDO.d.cts} +1 -1
  40. package/dist/{types--FRrBa-i.d.cts → types-D6fjl8Wy.d.cts} +22 -1
  41. package/dist/{types--FRrBa-i.d.ts → types-D6fjl8Wy.d.ts} +22 -1
  42. package/package.json +22 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/events.ts","../src/core/connection.ts","../src/media/qualityPolicy.ts","../src/media/compressImage.ts","../src/media/progressiveImage.ts"],"names":[],"mappings":"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","file":"media.cjs","sourcesContent":["/** Tiny typed pub-sub. No Node `EventEmitter` dependency, works in any JS runtime. */\nexport class Emitter<T> {\n private listeners = new Set<(value: T) => void>();\n\n subscribe(listener: (value: T) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n emit(value: T): void {\n for (const listener of this.listeners) {\n listener(value);\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n\n get size(): number {\n return this.listeners.size;\n }\n}\n","import { Emitter } from './events.js';\nimport type {\n ConnectionInfo,\n ConnectionListener,\n ConnectionQuality,\n Unsubscribe,\n} from './types.js';\n\n/**\n * The Network Information API (`navigator.connection`) is Chromium-only and not part of the\n * standard DOM lib types, hence this local shape instead of a global augmentation.\n */\ninterface NetworkInformationLike {\n effectiveType?: '4g' | '3g' | '2g' | 'slow-2g';\n downlink?: number;\n rtt?: number;\n saveData?: boolean;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ntype NavigatorWithConnection = Navigator & { connection?: NetworkInformationLike };\n\nexport interface ConnectionMonitorOptions {\n /**\n * Opt-in latency probe URL used to detect 'slow' connections on browsers without the Network\n * Information API (Safari, Firefox). Never probed automatically on a timer — only once on\n * startup and once per reconnect — because every probe costs a little data.\n */\n pingUrl?: string;\n /** Round-trip time above which the ping probe classifies the connection as 'slow'. Default 600ms. */\n slowRttThresholdMs?: number;\n /** downlink (Mbps) below which the Network Information API classifies as 'slow'. Default 0.5. */\n slowDownlinkMbps?: number;\n /** Timeout for the ping probe itself, in ms. Default 5000. */\n pingTimeoutMs?: number;\n}\n\nconst DEFAULT_SLOW_RTT_MS = 600;\nconst DEFAULT_SLOW_DOWNLINK_MBPS = 0.5;\nconst DEFAULT_PING_TIMEOUT_MS = 5000;\n\nfunction hasNavigator(): boolean {\n return typeof navigator !== 'undefined';\n}\n\n/**\n * Tracks connection quality (online / slow / offline) using the best signal available:\n * `navigator.onLine` + online/offline events as the universal baseline, `navigator.connection`\n * where present, and an optional opt-in ping probe as a cross-browser fallback for 'slow'.\n */\nexport class ConnectionMonitor {\n private emitter = new Emitter<ConnectionInfo>();\n private current: ConnectionInfo;\n private probedRttMs: number | undefined;\n private readonly options: Required<\n Pick<ConnectionMonitorOptions, 'slowRttThresholdMs' | 'slowDownlinkMbps' | 'pingTimeoutMs'>\n > &\n Pick<ConnectionMonitorOptions, 'pingUrl'>;\n private disposed = false;\n private onlineHandler?: () => void;\n private offlineHandler?: () => void;\n private connectionChangeHandler?: () => void;\n\n constructor(options: ConnectionMonitorOptions = {}) {\n this.options = {\n pingUrl: options.pingUrl,\n slowRttThresholdMs: options.slowRttThresholdMs ?? DEFAULT_SLOW_RTT_MS,\n slowDownlinkMbps: options.slowDownlinkMbps ?? DEFAULT_SLOW_DOWNLINK_MBPS,\n pingTimeoutMs: options.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS,\n };\n this.current = this.computeInfo();\n\n if (hasNavigator() && typeof window !== 'undefined') {\n this.onlineHandler = () => {\n this.refresh();\n void this.probeNow();\n };\n this.offlineHandler = () => this.refresh();\n window.addEventListener('online', this.onlineHandler);\n window.addEventListener('offline', this.offlineHandler);\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn?.addEventListener) {\n this.connectionChangeHandler = () => this.refresh();\n conn.addEventListener('change', this.connectionChangeHandler);\n }\n }\n\n if (this.options.pingUrl) {\n void this.probeNow();\n }\n }\n\n getStatus(): ConnectionInfo {\n return this.current;\n }\n\n subscribe(listener: ConnectionListener): Unsubscribe {\n return this.emitter.subscribe(listener);\n }\n\n /** Manually re-run the opt-in ping probe (no-op if no `pingUrl` was configured). */\n async probeNow(): Promise<ConnectionInfo> {\n if (!this.options.pingUrl || !hasNavigator() || navigator.onLine === false) {\n return this.current;\n }\n const start = Date.now();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.options.pingTimeoutMs);\n try {\n await fetch(this.options.pingUrl, {\n method: 'HEAD',\n cache: 'no-store',\n signal: controller.signal,\n });\n this.probedRttMs = Date.now() - start;\n } catch {\n // A failed probe tells us nothing reliable about latency; leave prior measurement as-is.\n } finally {\n clearTimeout(timer);\n }\n return this.refresh();\n }\n\n destroy(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (typeof window !== 'undefined') {\n if (this.onlineHandler) window.removeEventListener('online', this.onlineHandler);\n if (this.offlineHandler) window.removeEventListener('offline', this.offlineHandler);\n }\n if (hasNavigator() && this.connectionChangeHandler) {\n const conn = (navigator as NavigatorWithConnection).connection;\n conn?.removeEventListener?.('change', this.connectionChangeHandler);\n }\n this.emitter.clear();\n }\n\n private refresh(): ConnectionInfo {\n this.current = this.computeInfo();\n this.emitter.emit(this.current);\n return this.current;\n }\n\n private computeInfo(): ConnectionInfo {\n if (!hasNavigator()) {\n // Server/SSR context: assume the best case rather than block anything.\n return { quality: 'online', online: true };\n }\n\n const online = navigator.onLine !== false;\n if (!online) {\n return { quality: 'offline', online: false };\n }\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn) {\n const info: ConnectionInfo = {\n quality: 'online',\n online: true,\n effectiveType: conn.effectiveType,\n downlinkMbps: conn.downlink,\n rttMs: conn.rtt,\n saveData: conn.saveData,\n };\n const isSlow =\n conn.saveData === true ||\n conn.effectiveType === '2g' ||\n conn.effectiveType === 'slow-2g' ||\n (typeof conn.downlink === 'number' && conn.downlink < this.options.slowDownlinkMbps) ||\n (typeof conn.rtt === 'number' && conn.rtt > this.options.slowRttThresholdMs);\n return { ...info, quality: isSlow ? 'slow' : 'online' };\n }\n\n // No Network Information API: fall back to the opt-in ping probe measurement, if any.\n const quality: ConnectionQuality =\n typeof this.probedRttMs === 'number' && this.probedRttMs > this.options.slowRttThresholdMs\n ? 'slow'\n : 'online';\n return {\n quality,\n online: true,\n rttMs: this.probedRttMs,\n };\n }\n}\n\nlet sharedMonitor: ConnectionMonitor | undefined;\n\nfunction getSharedMonitor(): ConnectionMonitor {\n if (!sharedMonitor) sharedMonitor = new ConnectionMonitor();\n return sharedMonitor;\n}\n\n/** Convenience one-shot read of connection quality, without owning a `LowdataClient`. */\nexport function getConnectionQuality(): ConnectionInfo {\n return getSharedMonitor().getStatus();\n}\n\n/** Convenience subscription to connection changes, without owning a `LowdataClient`. */\nexport function onConnectionChange(listener: ConnectionListener): Unsubscribe {\n return getSharedMonitor().subscribe(listener);\n}\n","import type { ConnectionQuality } from '../core/types.js';\nimport type { ImageCompressionOptions } from './types.js';\n\nexport type CompressionPreset = Required<Pick<ImageCompressionOptions, 'maxWidth' | 'quality'>> &\n Pick<ImageCompressionOptions, 'targetSizeKB'>;\n\n/**\n * Aggressive-but-usable presets by connection quality. 'offline' is deliberately the smallest —\n * an image compressed while offline is headed straight into the persistent queue, where every\n * extra KB counts against storage quota and, eventually, the user's data bundle.\n */\nconst PRESETS: Record<ConnectionQuality, CompressionPreset> = {\n online: { maxWidth: 1600, quality: 0.72 },\n slow: { maxWidth: 1000, quality: 0.5, targetSizeKB: 150 },\n offline: { maxWidth: 800, quality: 0.4, targetSizeKB: 80 },\n};\n\nexport function presetForQuality(quality: ConnectionQuality): CompressionPreset {\n return PRESETS[quality];\n}\n","import { getConnectionQuality } from '../core/connection.js';\nimport { presetForQuality } from './qualityPolicy.js';\nimport type { CompressedImageResult, ImageCompressionOptions } from './types.js';\n\nconst DEFAULT_MAX_WIDTH = 1280;\nconst DEFAULT_QUALITY = 0.6;\nconst DEFAULT_MIME: NonNullable<ImageCompressionOptions['mimeType']> = 'image/jpeg';\nconst MIN_QUALITY = 0.35;\nconst QUALITY_STEP = 0.1;\nconst MAX_QUALITY_ITERATIONS = 5;\n\ntype ImageSource = ImageBitmap | HTMLImageElement;\n\nasync function loadImageSource(file: File | Blob): Promise<ImageSource> {\n if (typeof createImageBitmap === 'function') {\n return createImageBitmap(file);\n }\n // Fallback for environments without createImageBitmap (older Safari).\n return new Promise<HTMLImageElement>((resolve, reject) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve(img);\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('lowdata: failed to load image for compression'));\n };\n img.src = url;\n });\n}\n\nfunction computeTargetDimensions(\n sourceWidth: number,\n sourceHeight: number,\n maxWidth: number,\n maxHeight?: number,\n): { width: number; height: number } {\n let width = sourceWidth;\n let height = sourceHeight;\n\n if (width > maxWidth) {\n height = Math.round((height * maxWidth) / width);\n width = maxWidth;\n }\n if (maxHeight && height > maxHeight) {\n width = Math.round((width * maxHeight) / height);\n height = maxHeight;\n }\n return { width, height };\n}\n\nfunction drawToCanvas(source: ImageSource, width: number, height: number): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('lowdata: canvas 2D context is not available in this environment');\n ctx.drawImage(source, 0, 0, width, height);\n return canvas;\n}\n\nfunction canvasToBlob(canvas: HTMLCanvasElement, mimeType: string, quality: number): Promise<Blob> {\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('lowdata: canvas failed to produce an image blob'));\n },\n mimeType,\n quality,\n );\n });\n}\n\n/**\n * Client-side image compression via the Canvas API: downscales to a max dimension and re-encodes\n * at a target quality, optionally iterating quality further down to hit a byte budget. No\n * dependency — avoids pulling in a native/wasm image library, keeping the `lowdata/media` subpath\n * tiny. Known v1 limitation: EXIF orientation can be lost on re-encode in some browsers.\n */\nexport async function compressImage(\n file: File | Blob,\n options: ImageCompressionOptions = {},\n): Promise<CompressedImageResult> {\n const preset = options.connectionAware\n ? presetForQuality(getConnectionQuality().quality)\n : undefined;\n const maxWidth = options.maxWidth ?? preset?.maxWidth ?? DEFAULT_MAX_WIDTH;\n const maxHeight = options.maxHeight;\n const mimeType = options.mimeType ?? DEFAULT_MIME;\n const targetSizeKB = options.targetSizeKB ?? preset?.targetSizeKB;\n let quality = options.quality ?? preset?.quality ?? DEFAULT_QUALITY;\n\n const source = await loadImageSource(file);\n const { width, height } = computeTargetDimensions(\n source.width,\n source.height,\n maxWidth,\n maxHeight,\n );\n const canvas = drawToCanvas(source, width, height);\n if ('close' in source && typeof source.close === 'function') source.close();\n\n let blob = await canvasToBlob(canvas, mimeType, quality);\n\n if (targetSizeKB) {\n const targetBytes = targetSizeKB * 1024;\n let iterations = 0;\n while (\n blob.size > targetBytes &&\n quality > MIN_QUALITY &&\n iterations < MAX_QUALITY_ITERATIONS\n ) {\n quality = Math.max(MIN_QUALITY, quality - QUALITY_STEP);\n blob = await canvasToBlob(canvas, mimeType, quality);\n iterations++;\n }\n }\n\n return { blob, width, height, sizeBytes: blob.size, quality };\n}\n","import { Emitter } from '../core/events.js';\nimport type { Unsubscribe } from '../core/types.js';\n\nexport interface ProgressiveImageState {\n src: string;\n isLoaded: boolean;\n}\n\nexport interface ProgressiveImageLoaderOptions {\n src: string;\n placeholder: string;\n}\n\nexport interface ProgressiveImageLoader {\n subscribe(callback: (state: ProgressiveImageState) => void): Unsubscribe;\n getState(): ProgressiveImageState;\n destroy(): void;\n}\n\n/**\n * Blur-up / placeholder helper: reports the (typically tiny, inlined) placeholder immediately,\n * preloads the full-size image in the background, and reports the swap once it's ready.\n * Framework-agnostic — `lowdata/react`'s `useProgressiveImage` is a thin hook over this.\n */\nexport function createProgressiveImageLoader(\n options: ProgressiveImageLoaderOptions,\n): ProgressiveImageLoader {\n const emitter = new Emitter<ProgressiveImageState>();\n let state: ProgressiveImageState = { src: options.placeholder, isLoaded: false };\n let disposed = false;\n\n const img = typeof Image !== 'undefined' ? new Image() : undefined;\n if (img) {\n img.onload = () => {\n if (disposed) return;\n state = { src: options.src, isLoaded: true };\n emitter.emit(state);\n };\n img.src = options.src;\n } else {\n // No Image constructor available (SSR) — nothing to preload; report the target src directly.\n state = { src: options.src, isLoaded: true };\n }\n\n return {\n subscribe: (callback) => emitter.subscribe(callback),\n getState: () => state,\n destroy: () => {\n disposed = true;\n if (img) img.onload = null;\n emitter.clear();\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/core/events.ts","../src/core/connection.ts","../src/media/qualityPolicy.ts","../src/media/compressImage.ts","../src/media/progressiveImage.ts"],"names":[],"mappings":"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","file":"media.cjs","sourcesContent":["/** Tiny typed pub-sub. No Node `EventEmitter` dependency, works in any JS runtime. */\nexport class Emitter<T> {\n private listeners = new Set<(value: T) => void>();\n\n subscribe(listener: (value: T) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n emit(value: T): void {\n for (const listener of this.listeners) {\n listener(value);\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n\n get size(): number {\n return this.listeners.size;\n }\n}\n","import { Emitter } from './events.js';\nimport type {\n ConnectionInfo,\n ConnectionListener,\n ConnectionQuality,\n Unsubscribe,\n} from './types.js';\n\n/**\n * The Network Information API (`navigator.connection`) is Chromium-only and not part of the\n * standard DOM lib types, hence this local shape instead of a global augmentation.\n */\ninterface NetworkInformationLike {\n effectiveType?: '4g' | '3g' | '2g' | 'slow-2g';\n downlink?: number;\n rtt?: number;\n saveData?: boolean;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ntype NavigatorWithConnection = Navigator & { connection?: NetworkInformationLike };\n\nexport interface ConnectionMonitorOptions {\n /**\n * Opt-in latency probe URL used to detect 'slow' connections on browsers without the Network\n * Information API (Safari, Firefox). Never probed automatically on a timer — only once on\n * startup and once per reconnect — because every probe costs a little data.\n */\n pingUrl?: string;\n /** Round-trip time above which the ping probe classifies the connection as 'slow'. Default 600ms. */\n slowRttThresholdMs?: number;\n /** downlink (Mbps) below which the Network Information API classifies as 'slow'. Default 0.5. */\n slowDownlinkMbps?: number;\n /** Timeout for the ping probe itself, in ms. Default 5000. */\n pingTimeoutMs?: number;\n}\n\nconst DEFAULT_SLOW_RTT_MS = 600;\nconst DEFAULT_SLOW_DOWNLINK_MBPS = 0.5;\nconst DEFAULT_PING_TIMEOUT_MS = 5000;\n\nfunction hasNavigator(): boolean {\n return typeof navigator !== 'undefined';\n}\n\n/**\n * Tracks connection quality (online / slow / offline) using the best signal available:\n * `navigator.onLine` + online/offline events as the universal baseline, `navigator.connection`\n * where present, and an optional opt-in ping probe as a cross-browser fallback for 'slow'.\n */\nexport class ConnectionMonitor {\n private emitter = new Emitter<ConnectionInfo>();\n private current: ConnectionInfo;\n private probedRttMs: number | undefined;\n private readonly options: Required<\n Pick<ConnectionMonitorOptions, 'slowRttThresholdMs' | 'slowDownlinkMbps' | 'pingTimeoutMs'>\n > &\n Pick<ConnectionMonitorOptions, 'pingUrl'>;\n private disposed = false;\n private onlineHandler?: () => void;\n private offlineHandler?: () => void;\n private connectionChangeHandler?: () => void;\n\n constructor(options: ConnectionMonitorOptions = {}) {\n this.options = {\n pingUrl: options.pingUrl,\n slowRttThresholdMs: options.slowRttThresholdMs ?? DEFAULT_SLOW_RTT_MS,\n slowDownlinkMbps: options.slowDownlinkMbps ?? DEFAULT_SLOW_DOWNLINK_MBPS,\n pingTimeoutMs: options.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS,\n };\n this.current = this.computeInfo();\n\n if (hasNavigator() && typeof window !== 'undefined') {\n this.onlineHandler = () => {\n this.refresh();\n void this.probeNow();\n };\n this.offlineHandler = () => this.refresh();\n window.addEventListener('online', this.onlineHandler);\n window.addEventListener('offline', this.offlineHandler);\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn?.addEventListener) {\n this.connectionChangeHandler = () => this.refresh();\n conn.addEventListener('change', this.connectionChangeHandler);\n }\n }\n\n if (this.options.pingUrl) {\n void this.probeNow();\n }\n }\n\n getStatus(): ConnectionInfo {\n return this.current;\n }\n\n subscribe(listener: ConnectionListener): Unsubscribe {\n return this.emitter.subscribe(listener);\n }\n\n /** Manually re-run the opt-in ping probe (no-op if no `pingUrl` was configured). */\n async probeNow(): Promise<ConnectionInfo> {\n if (!this.options.pingUrl || !hasNavigator() || navigator.onLine === false) {\n return this.current;\n }\n const start = Date.now();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.options.pingTimeoutMs);\n try {\n await fetch(this.options.pingUrl, {\n method: 'HEAD',\n cache: 'no-store',\n signal: controller.signal,\n });\n this.probedRttMs = Date.now() - start;\n } catch {\n // A failed probe tells us nothing reliable about latency; leave prior measurement as-is.\n } finally {\n clearTimeout(timer);\n }\n return this.refresh();\n }\n\n destroy(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (typeof window !== 'undefined') {\n if (this.onlineHandler) window.removeEventListener('online', this.onlineHandler);\n if (this.offlineHandler) window.removeEventListener('offline', this.offlineHandler);\n }\n if (hasNavigator() && this.connectionChangeHandler) {\n const conn = (navigator as NavigatorWithConnection).connection;\n conn?.removeEventListener?.('change', this.connectionChangeHandler);\n }\n this.emitter.clear();\n }\n\n private refresh(): ConnectionInfo {\n this.current = this.computeInfo();\n this.emitter.emit(this.current);\n return this.current;\n }\n\n private computeInfo(): ConnectionInfo {\n if (!hasNavigator()) {\n // Server/SSR context: assume the best case rather than block anything.\n return { quality: 'online', online: true };\n }\n\n const online = navigator.onLine !== false;\n if (!online) {\n return { quality: 'offline', online: false };\n }\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn) {\n const info: ConnectionInfo = {\n quality: 'online',\n online: true,\n effectiveType: conn.effectiveType,\n downlinkMbps: conn.downlink,\n rttMs: conn.rtt,\n saveData: conn.saveData,\n };\n const isSlow =\n conn.saveData === true ||\n conn.effectiveType === '2g' ||\n conn.effectiveType === 'slow-2g' ||\n (typeof conn.downlink === 'number' && conn.downlink < this.options.slowDownlinkMbps) ||\n (typeof conn.rtt === 'number' && conn.rtt > this.options.slowRttThresholdMs);\n return { ...info, quality: isSlow ? 'slow' : 'online' };\n }\n\n // No Network Information API: fall back to the opt-in ping probe measurement, if any.\n const quality: ConnectionQuality =\n typeof this.probedRttMs === 'number' && this.probedRttMs > this.options.slowRttThresholdMs\n ? 'slow'\n : 'online';\n return {\n quality,\n online: true,\n rttMs: this.probedRttMs,\n };\n }\n}\n\nlet sharedMonitor: ConnectionMonitor | undefined;\n\nfunction getSharedMonitor(): ConnectionMonitor {\n if (!sharedMonitor) sharedMonitor = new ConnectionMonitor();\n return sharedMonitor;\n}\n\n/** Convenience one-shot read of connection quality, without owning a `LowdataClient`. */\nexport function getConnectionQuality(): ConnectionInfo {\n return getSharedMonitor().getStatus();\n}\n\n/** Convenience subscription to connection changes, without owning a `LowdataClient`. */\nexport function onConnectionChange(listener: ConnectionListener): Unsubscribe {\n return getSharedMonitor().subscribe(listener);\n}\n","import type { ConnectionQuality } from '../core/types.js';\nimport type { ImageCompressionOptions } from './types.js';\n\nexport type CompressionPreset = Required<Pick<ImageCompressionOptions, 'maxWidth' | 'quality'>> &\n Pick<ImageCompressionOptions, 'targetSizeKB'>;\n\n/**\n * Aggressive-but-usable presets by connection quality. 'offline' is deliberately the smallest —\n * an image compressed while offline is headed straight into the persistent queue, where every\n * extra KB counts against storage quota and, eventually, the user's data bundle.\n */\nconst PRESETS: Record<ConnectionQuality, CompressionPreset> = {\n online: { maxWidth: 1600, quality: 0.72 },\n slow: { maxWidth: 1000, quality: 0.5, targetSizeKB: 150 },\n offline: { maxWidth: 800, quality: 0.4, targetSizeKB: 80 },\n};\n\nexport function presetForQuality(quality: ConnectionQuality): CompressionPreset {\n return PRESETS[quality];\n}\n","import { getConnectionQuality } from '../core/connection.js';\nimport { presetForQuality } from './qualityPolicy.js';\nimport type { CompressedImageResult, ImageCompressionOptions } from './types.js';\n\nconst DEFAULT_MAX_WIDTH = 1280;\nconst DEFAULT_QUALITY = 0.6;\nconst DEFAULT_MIME: NonNullable<ImageCompressionOptions['mimeType']> = 'image/jpeg';\nconst MIN_QUALITY = 0.35;\nconst QUALITY_STEP = 0.1;\nconst MAX_QUALITY_ITERATIONS = 5;\n\ntype ImageSource = ImageBitmap | HTMLImageElement;\n\nasync function loadImageSource(file: File | Blob): Promise<ImageSource> {\n if (typeof createImageBitmap === 'function') {\n // `imageOrientation: 'from-image'` is the spec-defined way to force EXIF-orientation-correct\n // decoding regardless of a given browser's default — without it, a photo taken sideways on a\n // phone can come out of `drawImage()` still sideways, since canvas pixel data (unlike an\n // `<img>` element's own on-screen rendering) doesn't reliably apply EXIF orientation itself.\n return createImageBitmap(file, { imageOrientation: 'from-image' });\n }\n // Fallback for environments without createImageBitmap (older Safari).\n return new Promise<HTMLImageElement>((resolve, reject) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve(img);\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('lowdata: failed to load image for compression'));\n };\n img.src = url;\n });\n}\n\nfunction computeTargetDimensions(\n sourceWidth: number,\n sourceHeight: number,\n maxWidth: number,\n maxHeight?: number,\n): { width: number; height: number } {\n let width = sourceWidth;\n let height = sourceHeight;\n\n if (width > maxWidth) {\n height = Math.round((height * maxWidth) / width);\n width = maxWidth;\n }\n if (maxHeight && height > maxHeight) {\n width = Math.round((width * maxHeight) / height);\n height = maxHeight;\n }\n return { width, height };\n}\n\nfunction drawToCanvas(source: ImageSource, width: number, height: number): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('lowdata: canvas 2D context is not available in this environment');\n ctx.drawImage(source, 0, 0, width, height);\n return canvas;\n}\n\nfunction canvasToBlob(canvas: HTMLCanvasElement, mimeType: string, quality: number): Promise<Blob> {\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('lowdata: canvas failed to produce an image blob'));\n },\n mimeType,\n quality,\n );\n });\n}\n\n/**\n * Client-side image compression via the Canvas API: downscales to a max dimension and re-encodes\n * at a target quality, optionally iterating quality further down to hit a byte budget. No\n * dependency — avoids pulling in a native/wasm image library, keeping the `lowdata/media` subpath\n * tiny. Known v1 limitation: EXIF orientation can be lost on re-encode in some browsers.\n */\nexport async function compressImage(\n file: File | Blob,\n options: ImageCompressionOptions = {},\n): Promise<CompressedImageResult> {\n const preset = options.connectionAware\n ? presetForQuality(getConnectionQuality().quality)\n : undefined;\n const maxWidth = options.maxWidth ?? preset?.maxWidth ?? DEFAULT_MAX_WIDTH;\n const maxHeight = options.maxHeight;\n const mimeType = options.mimeType ?? DEFAULT_MIME;\n const targetSizeKB = options.targetSizeKB ?? preset?.targetSizeKB;\n let quality = options.quality ?? preset?.quality ?? DEFAULT_QUALITY;\n\n const source = await loadImageSource(file);\n const { width, height } = computeTargetDimensions(\n source.width,\n source.height,\n maxWidth,\n maxHeight,\n );\n const canvas = drawToCanvas(source, width, height);\n if ('close' in source && typeof source.close === 'function') source.close();\n\n let blob = await canvasToBlob(canvas, mimeType, quality);\n\n if (targetSizeKB) {\n const targetBytes = targetSizeKB * 1024;\n let iterations = 0;\n while (\n blob.size > targetBytes &&\n quality > MIN_QUALITY &&\n iterations < MAX_QUALITY_ITERATIONS\n ) {\n quality = Math.max(MIN_QUALITY, quality - QUALITY_STEP);\n blob = await canvasToBlob(canvas, mimeType, quality);\n iterations++;\n }\n }\n\n return { blob, width, height, sizeBytes: blob.size, quality };\n}\n","import { Emitter } from '../core/events.js';\nimport type { Unsubscribe } from '../core/types.js';\n\nexport interface ProgressiveImageState {\n src: string;\n isLoaded: boolean;\n}\n\nexport interface ProgressiveImageLoaderOptions {\n src: string;\n placeholder: string;\n}\n\nexport interface ProgressiveImageLoader {\n subscribe(callback: (state: ProgressiveImageState) => void): Unsubscribe;\n getState(): ProgressiveImageState;\n destroy(): void;\n}\n\n/**\n * Blur-up / placeholder helper: reports the (typically tiny, inlined) placeholder immediately,\n * preloads the full-size image in the background, and reports the swap once it's ready.\n * Framework-agnostic — `lowdata/react`'s `useProgressiveImage` is a thin hook over this.\n */\nexport function createProgressiveImageLoader(\n options: ProgressiveImageLoaderOptions,\n): ProgressiveImageLoader {\n const emitter = new Emitter<ProgressiveImageState>();\n let state: ProgressiveImageState = { src: options.placeholder, isLoaded: false };\n let disposed = false;\n\n const img = typeof Image !== 'undefined' ? new Image() : undefined;\n if (img) {\n img.onload = () => {\n if (disposed) return;\n state = { src: options.src, isLoaded: true };\n emitter.emit(state);\n };\n img.src = options.src;\n } else {\n // No Image constructor available (SSR) — nothing to preload; report the target src directly.\n state = { src: options.src, isLoaded: true };\n }\n\n return {\n subscribe: (callback) => emitter.subscribe(callback),\n getState: () => state,\n destroy: () => {\n disposed = true;\n if (img) img.onload = null;\n emitter.clear();\n },\n };\n}\n"]}
package/dist/media.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as ConnectionQuality } from './types--FRrBa-i.cjs';
2
- export { P as ProgressiveImageLoader, a as ProgressiveImageLoaderOptions, b as ProgressiveImageState, c as createProgressiveImageLoader } from './progressiveImage-BWNdewbi.cjs';
1
+ import { b as ConnectionQuality } from './types-D6fjl8Wy.cjs';
2
+ export { P as ProgressiveImageLoader, a as ProgressiveImageLoaderOptions, b as ProgressiveImageState, c as createProgressiveImageLoader } from './progressiveImage-CDpz_rv3.cjs';
3
3
 
4
4
  interface ImageCompressionOptions {
5
5
  /** Longest edge in pixels; the image is downscaled proportionally to fit. Default 1280. */
package/dist/media.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as ConnectionQuality } from './types--FRrBa-i.js';
2
- export { P as ProgressiveImageLoader, a as ProgressiveImageLoaderOptions, b as ProgressiveImageState, c as createProgressiveImageLoader } from './progressiveImage-BhY_K8Dj.js';
1
+ import { b as ConnectionQuality } from './types-D6fjl8Wy.js';
2
+ export { P as ProgressiveImageLoader, a as ProgressiveImageLoaderOptions, b as ProgressiveImageState, c as createProgressiveImageLoader } from './progressiveImage-BOdic4dJ.js';
3
3
 
4
4
  interface ImageCompressionOptions {
5
5
  /** Longest edge in pixels; the image is downscaled proportionally to fit. Default 1280. */
package/dist/media.js CHANGED
@@ -162,7 +162,7 @@ var QUALITY_STEP = 0.1;
162
162
  var MAX_QUALITY_ITERATIONS = 5;
163
163
  async function loadImageSource(file) {
164
164
  if (typeof createImageBitmap === "function") {
165
- return createImageBitmap(file);
165
+ return createImageBitmap(file, { imageOrientation: "from-image" });
166
166
  }
167
167
  return new Promise((resolve, reject) => {
168
168
  const url = URL.createObjectURL(file);
package/dist/media.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/events.ts","../src/core/connection.ts","../src/media/qualityPolicy.ts","../src/media/compressImage.ts","../src/media/progressiveImage.ts"],"names":[],"mappings":";AACO,IAAM,UAAN,MAAiB;AAAA,EAAjB,WAAA,GAAA;AACL,IAAA,IAAA,CAAQ,SAAA,uBAAgB,GAAA,EAAwB;AAAA,EAAA;AAAA,EAEhD,UAAU,QAAA,EAA0C;AAClD,IAAA,IAAA,CAAK,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC3B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,IAChC,CAAA;AAAA,EACF;AAAA,EAEA,KAAK,KAAA,EAAgB;AACnB,IAAA,KAAA,MAAW,QAAA,IAAY,KAAK,SAAA,EAAW;AACrC,MAAA,QAAA,CAAS,KAAK,CAAA;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,UAAU,KAAA,EAAM;AAAA,EACvB;AAAA,EAEA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,SAAA,CAAU,IAAA;AAAA,EACxB;AACF,CAAA;;;ACcA,IAAM,mBAAA,GAAsB,GAAA;AAC5B,IAAM,0BAAA,GAA6B,GAAA;AACnC,IAAM,uBAAA,GAA0B,GAAA;AAEhC,SAAS,YAAA,GAAwB;AAC/B,EAAA,OAAO,OAAO,SAAA,KAAc,WAAA;AAC9B;AAOO,IAAM,oBAAN,MAAwB;AAAA,EAa7B,WAAA,CAAY,OAAA,GAAoC,EAAC,EAAG;AAZpD,IAAA,IAAA,CAAQ,OAAA,GAAU,IAAI,OAAA,EAAwB;AAO9C,IAAA,IAAA,CAAQ,QAAA,GAAW,KAAA;AA3DrB,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAiEI,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,kBAAA,EAAA,CAAoB,EAAA,GAAA,OAAA,CAAQ,kBAAA,KAAR,IAAA,GAAA,EAAA,GAA8B,mBAAA;AAAA,MAClD,gBAAA,EAAA,CAAkB,EAAA,GAAA,OAAA,CAAQ,gBAAA,KAAR,IAAA,GAAA,EAAA,GAA4B,0BAAA;AAAA,MAC9C,aAAA,EAAA,CAAe,EAAA,GAAA,OAAA,CAAQ,aAAA,KAAR,IAAA,GAAA,EAAA,GAAyB;AAAA,KAC1C;AACA,IAAA,IAAA,CAAK,OAAA,GAAU,KAAK,WAAA,EAAY;AAEhC,IAAA,IAAI,YAAA,EAAa,IAAK,OAAO,MAAA,KAAW,WAAA,EAAa;AACnD,MAAA,IAAA,CAAK,gBAAgB,MAAM;AACzB,QAAA,IAAA,CAAK,OAAA,EAAQ;AACb,QAAA,KAAK,KAAK,QAAA,EAAS;AAAA,MACrB,CAAA;AACA,MAAA,IAAA,CAAK,cAAA,GAAiB,MAAM,IAAA,CAAK,OAAA,EAAQ;AACzC,MAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,IAAA,CAAK,aAAa,CAAA;AACpD,MAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,cAAc,CAAA;AAEtD,MAAA,MAAM,OAAQ,SAAA,CAAsC,UAAA;AACpD,MAAA,IAAI,6BAAM,gBAAA,EAAkB;AAC1B,QAAA,IAAA,CAAK,uBAAA,GAA0B,MAAM,IAAA,CAAK,OAAA,EAAQ;AAClD,QAAA,IAAA,CAAK,gBAAA,CAAiB,QAAA,EAAU,IAAA,CAAK,uBAAuB,CAAA;AAAA,MAC9D;AAAA,IACF;AAEA,IAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AACxB,MAAA,KAAK,KAAK,QAAA,EAAS;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,SAAA,GAA4B;AAC1B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA,EAEA,UAAU,QAAA,EAA2C;AACnD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,QAAQ,CAAA;AAAA,EACxC;AAAA;AAAA,EAGA,MAAM,QAAA,GAAoC;AACxC,IAAA,IAAI,CAAC,KAAK,OAAA,CAAQ,OAAA,IAAW,CAAC,YAAA,EAAa,IAAK,SAAA,CAAU,MAAA,KAAW,KAAA,EAAO;AAC1E,MAAA,OAAO,IAAA,CAAK,OAAA;AAAA,IACd;AACA,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,UAAA,CAAW,OAAM,EAAG,IAAA,CAAK,QAAQ,aAAa,CAAA;AAC7E,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS;AAAA,QAChC,MAAA,EAAQ,MAAA;AAAA,QACR,KAAA,EAAO,UAAA;AAAA,QACP,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AACD,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAAA,IAClC,CAAA,CAAA,MAAQ;AAAA,IAER,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AACA,IAAA,OAAO,KAAK,OAAA,EAAQ;AAAA,EACtB;AAAA,EAEA,OAAA,GAAgB;AA7HlB,IAAA,IAAA,EAAA;AA8HI,IAAA,IAAI,KAAK,QAAA,EAAU;AACnB,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,IAAI,KAAK,aAAA,EAAe,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,KAAK,aAAa,CAAA;AAC/E,MAAA,IAAI,KAAK,cAAA,EAAgB,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,KAAK,cAAc,CAAA;AAAA,IACpF;AACA,IAAA,IAAI,YAAA,EAAa,IAAK,IAAA,CAAK,uBAAA,EAAyB;AAClD,MAAA,MAAM,OAAQ,SAAA,CAAsC,UAAA;AACpD,MAAA,CAAA,EAAA,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAM,mBAAA,KAAN,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,IAAA,EAA4B,QAAA,EAAU,IAAA,CAAK,uBAAA,CAAA;AAAA,IAC7C;AACA,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAAA,EACrB;AAAA,EAEQ,OAAA,GAA0B;AAChC,IAAA,IAAA,CAAK,OAAA,GAAU,KAAK,WAAA,EAAY;AAChC,IAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA;AAC9B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA,EAEQ,WAAA,GAA8B;AACpC,IAAA,IAAI,CAAC,cAAa,EAAG;AAEnB,MAAA,OAAO,EAAE,OAAA,EAAS,QAAA,EAAU,MAAA,EAAQ,IAAA,EAAK;AAAA,IAC3C;AAEA,IAAA,MAAM,MAAA,GAAS,UAAU,MAAA,KAAW,KAAA;AACpC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,EAAE,OAAA,EAAS,SAAA,EAAW,MAAA,EAAQ,KAAA,EAAM;AAAA,IAC7C;AAEA,IAAA,MAAM,OAAQ,SAAA,CAAsC,UAAA;AACpD,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,IAAA,GAAuB;AAAA,QAC3B,OAAA,EAAS,QAAA;AAAA,QACT,MAAA,EAAQ,IAAA;AAAA,QACR,eAAe,IAAA,CAAK,aAAA;AAAA,QACpB,cAAc,IAAA,CAAK,QAAA;AAAA,QACnB,OAAO,IAAA,CAAK,GAAA;AAAA,QACZ,UAAU,IAAA,CAAK;AAAA,OACjB;AACA,MAAA,MAAM,MAAA,GACJ,IAAA,CAAK,QAAA,KAAa,IAAA,IAClB,IAAA,CAAK,aAAA,KAAkB,IAAA,IACvB,IAAA,CAAK,aAAA,KAAkB,SAAA,IACtB,OAAO,IAAA,CAAK,QAAA,KAAa,YAAY,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,gBAAA,IAClE,OAAO,IAAA,CAAK,GAAA,KAAQ,QAAA,IAAY,IAAA,CAAK,GAAA,GAAM,IAAA,CAAK,OAAA,CAAQ,kBAAA;AAC3D,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAS,MAAA,GAAS,SAAS,QAAA,EAAS;AAAA,IACxD;AAGA,IAAA,MAAM,OAAA,GACJ,OAAO,IAAA,CAAK,WAAA,KAAgB,QAAA,IAAY,KAAK,WAAA,GAAc,IAAA,CAAK,OAAA,CAAQ,kBAAA,GACpE,MAAA,GACA,QAAA;AACN,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA,MAAA,EAAQ,IAAA;AAAA,MACR,OAAO,IAAA,CAAK;AAAA,KACd;AAAA,EACF;AACF,CAAA;AAEA,IAAI,aAAA;AAEJ,SAAS,gBAAA,GAAsC;AAC7C,EAAA,IAAI,CAAC,aAAA,EAAe,aAAA,GAAgB,IAAI,iBAAA,EAAkB;AAC1D,EAAA,OAAO,aAAA;AACT;AAGO,SAAS,oBAAA,GAAuC;AACrD,EAAA,OAAO,gBAAA,GAAmB,SAAA,EAAU;AACtC;;;AC3LA,IAAM,OAAA,GAAwD;AAAA,EAC5D,MAAA,EAAQ,EAAE,QAAA,EAAU,IAAA,EAAM,SAAS,IAAA,EAAK;AAAA,EACxC,MAAM,EAAE,QAAA,EAAU,KAAM,OAAA,EAAS,GAAA,EAAK,cAAc,GAAA,EAAI;AAAA,EACxD,SAAS,EAAE,QAAA,EAAU,KAAK,OAAA,EAAS,GAAA,EAAK,cAAc,EAAA;AACxD,CAAA;AAEO,SAAS,iBAAiB,OAAA,EAA+C;AAC9E,EAAA,OAAO,QAAQ,OAAO,CAAA;AACxB;;;ACfA,IAAM,iBAAA,GAAoB,IAAA;AAC1B,IAAM,eAAA,GAAkB,GAAA;AACxB,IAAM,YAAA,GAAiE,YAAA;AACvE,IAAM,WAAA,GAAc,IAAA;AACpB,IAAM,YAAA,GAAe,GAAA;AACrB,IAAM,sBAAA,GAAyB,CAAA;AAI/B,eAAe,gBAAgB,IAAA,EAAyC;AACtE,EAAA,IAAI,OAAO,sBAAsB,UAAA,EAAY;AAC3C,IAAA,OAAO,kBAAkB,IAAI,CAAA;AAAA,EAC/B;AAEA,EAAA,OAAO,IAAI,OAAA,CAA0B,CAAC,OAAA,EAAS,MAAA,KAAW;AACxD,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,eAAA,CAAgB,IAAI,CAAA;AACpC,IAAA,MAAM,GAAA,GAAM,IAAI,KAAA,EAAM;AACtB,IAAA,GAAA,CAAI,SAAS,MAAM;AACjB,MAAA,GAAA,CAAI,gBAAgB,GAAG,CAAA;AACvB,MAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,IACb,CAAA;AACA,IAAA,GAAA,CAAI,UAAU,MAAM;AAClB,MAAA,GAAA,CAAI,gBAAgB,GAAG,CAAA;AACvB,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,+CAA+C,CAAC,CAAA;AAAA,IACnE,CAAA;AACA,IAAA,GAAA,CAAI,GAAA,GAAM,GAAA;AAAA,EACZ,CAAC,CAAA;AACH;AAEA,SAAS,uBAAA,CACP,WAAA,EACA,YAAA,EACA,QAAA,EACA,SAAA,EACmC;AACnC,EAAA,IAAI,KAAA,GAAQ,WAAA;AACZ,EAAA,IAAI,MAAA,GAAS,YAAA;AAEb,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,MAAA,GAAS,IAAA,CAAK,KAAA,CAAO,MAAA,GAAS,QAAA,GAAY,KAAK,CAAA;AAC/C,IAAA,KAAA,GAAQ,QAAA;AAAA,EACV;AACA,EAAA,IAAI,SAAA,IAAa,SAAS,SAAA,EAAW;AACnC,IAAA,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAO,KAAA,GAAQ,SAAA,GAAa,MAAM,CAAA;AAC/C,IAAA,MAAA,GAAS,SAAA;AAAA,EACX;AACA,EAAA,OAAO,EAAE,OAAO,MAAA,EAAO;AACzB;AAEA,SAAS,YAAA,CAAa,MAAA,EAAqB,KAAA,EAAe,MAAA,EAAmC;AAC3F,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,EAAA,MAAA,CAAO,KAAA,GAAQ,KAAA;AACf,EAAA,MAAA,CAAO,MAAA,GAAS,MAAA;AAChB,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAClC,EAAA,IAAI,CAAC,GAAA,EAAK,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAC3F,EAAA,GAAA,CAAI,SAAA,CAAU,MAAA,EAAQ,CAAA,EAAG,CAAA,EAAG,OAAO,MAAM,CAAA;AACzC,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,YAAA,CAAa,MAAA,EAA2B,QAAA,EAAkB,OAAA,EAAgC;AACjG,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAA,CAAO,MAAA;AAAA,MACL,CAAC,IAAA,KAAS;AACR,QAAA,IAAI,IAAA,UAAc,IAAI,CAAA;AAAA,aACjB,MAAA,CAAO,IAAI,KAAA,CAAM,iDAAiD,CAAC,CAAA;AAAA,MAC1E,CAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAQA,eAAsB,aAAA,CACpB,IAAA,EACA,OAAA,GAAmC,EAAC,EACJ;AArFlC,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAsFE,EAAA,MAAM,SAAS,OAAA,CAAQ,eAAA,GACnB,iBAAiB,oBAAA,EAAqB,CAAE,OAAO,CAAA,GAC/C,MAAA;AACJ,EAAA,MAAM,YAAW,EAAA,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,QAAA,KAAR,IAAA,GAAA,EAAA,GAAoB,MAAA,IAAA,IAAA,GAAA,MAAA,GAAA,MAAA,CAAQ,aAA5B,IAAA,GAAA,EAAA,GAAwC,iBAAA;AACzD,EAAA,MAAM,YAAY,OAAA,CAAQ,SAAA;AAC1B,EAAA,MAAM,QAAA,GAAA,CAAW,EAAA,GAAA,OAAA,CAAQ,QAAA,KAAR,IAAA,GAAA,EAAA,GAAoB,YAAA;AACrC,EAAA,MAAM,YAAA,GAAA,CAAe,EAAA,GAAA,OAAA,CAAQ,YAAA,KAAR,IAAA,GAAA,EAAA,GAAwB,MAAA,IAAA,IAAA,GAAA,MAAA,GAAA,MAAA,CAAQ,YAAA;AACrD,EAAA,IAAI,WAAU,EAAA,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,OAAA,KAAR,IAAA,GAAA,EAAA,GAAmB,MAAA,IAAA,IAAA,GAAA,MAAA,GAAA,MAAA,CAAQ,YAA3B,IAAA,GAAA,EAAA,GAAsC,eAAA;AAEpD,EAAA,MAAM,MAAA,GAAS,MAAM,eAAA,CAAgB,IAAI,CAAA;AACzC,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAO,GAAI,uBAAA;AAAA,IACxB,MAAA,CAAO,KAAA;AAAA,IACP,MAAA,CAAO,MAAA;AAAA,IACP,QAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,MAAA,EAAQ,KAAA,EAAO,MAAM,CAAA;AACjD,EAAA,IAAI,WAAW,MAAA,IAAU,OAAO,OAAO,KAAA,KAAU,UAAA,SAAmB,KAAA,EAAM;AAE1E,EAAA,IAAI,IAAA,GAAO,MAAM,YAAA,CAAa,MAAA,EAAQ,UAAU,OAAO,CAAA;AAEvD,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,MAAM,cAAc,YAAA,GAAe,IAAA;AACnC,IAAA,IAAI,UAAA,GAAa,CAAA;AACjB,IAAA,OACE,KAAK,IAAA,GAAO,WAAA,IACZ,OAAA,GAAU,WAAA,IACV,aAAa,sBAAA,EACb;AACA,MAAA,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,OAAA,GAAU,YAAY,CAAA;AACtD,MAAA,IAAA,GAAO,MAAM,YAAA,CAAa,MAAA,EAAQ,QAAA,EAAU,OAAO,CAAA;AACnD,MAAA,UAAA,EAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,QAAQ,SAAA,EAAW,IAAA,CAAK,MAAM,OAAA,EAAQ;AAC9D;;;AClGO,SAAS,6BACd,OAAA,EACwB;AACxB,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,EAA+B;AACnD,EAAA,IAAI,QAA+B,EAAE,GAAA,EAAK,OAAA,CAAQ,WAAA,EAAa,UAAU,KAAA,EAAM;AAC/E,EAAA,IAAI,QAAA,GAAW,KAAA;AAEf,EAAA,MAAM,MAAM,OAAO,KAAA,KAAU,WAAA,GAAc,IAAI,OAAM,GAAI,MAAA;AACzD,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,GAAA,CAAI,SAAS,MAAM;AACjB,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,KAAA,GAAQ,EAAE,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,UAAU,IAAA,EAAK;AAC3C,MAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,IACpB,CAAA;AACA,IAAA,GAAA,CAAI,MAAM,OAAA,CAAQ,GAAA;AAAA,EACpB,CAAA,MAAO;AAEL,IAAA,KAAA,GAAQ,EAAE,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,UAAU,IAAA,EAAK;AAAA,EAC7C;AAEA,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,CAAC,QAAA,KAAa,OAAA,CAAQ,UAAU,QAAQ,CAAA;AAAA,IACnD,UAAU,MAAM,KAAA;AAAA,IAChB,SAAS,MAAM;AACb,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,IAAI,GAAA,MAAS,MAAA,GAAS,IAAA;AACtB,MAAA,OAAA,CAAQ,KAAA,EAAM;AAAA,IAChB;AAAA,GACF;AACF","file":"media.js","sourcesContent":["/** Tiny typed pub-sub. No Node `EventEmitter` dependency, works in any JS runtime. */\nexport class Emitter<T> {\n private listeners = new Set<(value: T) => void>();\n\n subscribe(listener: (value: T) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n emit(value: T): void {\n for (const listener of this.listeners) {\n listener(value);\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n\n get size(): number {\n return this.listeners.size;\n }\n}\n","import { Emitter } from './events.js';\nimport type {\n ConnectionInfo,\n ConnectionListener,\n ConnectionQuality,\n Unsubscribe,\n} from './types.js';\n\n/**\n * The Network Information API (`navigator.connection`) is Chromium-only and not part of the\n * standard DOM lib types, hence this local shape instead of a global augmentation.\n */\ninterface NetworkInformationLike {\n effectiveType?: '4g' | '3g' | '2g' | 'slow-2g';\n downlink?: number;\n rtt?: number;\n saveData?: boolean;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ntype NavigatorWithConnection = Navigator & { connection?: NetworkInformationLike };\n\nexport interface ConnectionMonitorOptions {\n /**\n * Opt-in latency probe URL used to detect 'slow' connections on browsers without the Network\n * Information API (Safari, Firefox). Never probed automatically on a timer — only once on\n * startup and once per reconnect — because every probe costs a little data.\n */\n pingUrl?: string;\n /** Round-trip time above which the ping probe classifies the connection as 'slow'. Default 600ms. */\n slowRttThresholdMs?: number;\n /** downlink (Mbps) below which the Network Information API classifies as 'slow'. Default 0.5. */\n slowDownlinkMbps?: number;\n /** Timeout for the ping probe itself, in ms. Default 5000. */\n pingTimeoutMs?: number;\n}\n\nconst DEFAULT_SLOW_RTT_MS = 600;\nconst DEFAULT_SLOW_DOWNLINK_MBPS = 0.5;\nconst DEFAULT_PING_TIMEOUT_MS = 5000;\n\nfunction hasNavigator(): boolean {\n return typeof navigator !== 'undefined';\n}\n\n/**\n * Tracks connection quality (online / slow / offline) using the best signal available:\n * `navigator.onLine` + online/offline events as the universal baseline, `navigator.connection`\n * where present, and an optional opt-in ping probe as a cross-browser fallback for 'slow'.\n */\nexport class ConnectionMonitor {\n private emitter = new Emitter<ConnectionInfo>();\n private current: ConnectionInfo;\n private probedRttMs: number | undefined;\n private readonly options: Required<\n Pick<ConnectionMonitorOptions, 'slowRttThresholdMs' | 'slowDownlinkMbps' | 'pingTimeoutMs'>\n > &\n Pick<ConnectionMonitorOptions, 'pingUrl'>;\n private disposed = false;\n private onlineHandler?: () => void;\n private offlineHandler?: () => void;\n private connectionChangeHandler?: () => void;\n\n constructor(options: ConnectionMonitorOptions = {}) {\n this.options = {\n pingUrl: options.pingUrl,\n slowRttThresholdMs: options.slowRttThresholdMs ?? DEFAULT_SLOW_RTT_MS,\n slowDownlinkMbps: options.slowDownlinkMbps ?? DEFAULT_SLOW_DOWNLINK_MBPS,\n pingTimeoutMs: options.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS,\n };\n this.current = this.computeInfo();\n\n if (hasNavigator() && typeof window !== 'undefined') {\n this.onlineHandler = () => {\n this.refresh();\n void this.probeNow();\n };\n this.offlineHandler = () => this.refresh();\n window.addEventListener('online', this.onlineHandler);\n window.addEventListener('offline', this.offlineHandler);\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn?.addEventListener) {\n this.connectionChangeHandler = () => this.refresh();\n conn.addEventListener('change', this.connectionChangeHandler);\n }\n }\n\n if (this.options.pingUrl) {\n void this.probeNow();\n }\n }\n\n getStatus(): ConnectionInfo {\n return this.current;\n }\n\n subscribe(listener: ConnectionListener): Unsubscribe {\n return this.emitter.subscribe(listener);\n }\n\n /** Manually re-run the opt-in ping probe (no-op if no `pingUrl` was configured). */\n async probeNow(): Promise<ConnectionInfo> {\n if (!this.options.pingUrl || !hasNavigator() || navigator.onLine === false) {\n return this.current;\n }\n const start = Date.now();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.options.pingTimeoutMs);\n try {\n await fetch(this.options.pingUrl, {\n method: 'HEAD',\n cache: 'no-store',\n signal: controller.signal,\n });\n this.probedRttMs = Date.now() - start;\n } catch {\n // A failed probe tells us nothing reliable about latency; leave prior measurement as-is.\n } finally {\n clearTimeout(timer);\n }\n return this.refresh();\n }\n\n destroy(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (typeof window !== 'undefined') {\n if (this.onlineHandler) window.removeEventListener('online', this.onlineHandler);\n if (this.offlineHandler) window.removeEventListener('offline', this.offlineHandler);\n }\n if (hasNavigator() && this.connectionChangeHandler) {\n const conn = (navigator as NavigatorWithConnection).connection;\n conn?.removeEventListener?.('change', this.connectionChangeHandler);\n }\n this.emitter.clear();\n }\n\n private refresh(): ConnectionInfo {\n this.current = this.computeInfo();\n this.emitter.emit(this.current);\n return this.current;\n }\n\n private computeInfo(): ConnectionInfo {\n if (!hasNavigator()) {\n // Server/SSR context: assume the best case rather than block anything.\n return { quality: 'online', online: true };\n }\n\n const online = navigator.onLine !== false;\n if (!online) {\n return { quality: 'offline', online: false };\n }\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn) {\n const info: ConnectionInfo = {\n quality: 'online',\n online: true,\n effectiveType: conn.effectiveType,\n downlinkMbps: conn.downlink,\n rttMs: conn.rtt,\n saveData: conn.saveData,\n };\n const isSlow =\n conn.saveData === true ||\n conn.effectiveType === '2g' ||\n conn.effectiveType === 'slow-2g' ||\n (typeof conn.downlink === 'number' && conn.downlink < this.options.slowDownlinkMbps) ||\n (typeof conn.rtt === 'number' && conn.rtt > this.options.slowRttThresholdMs);\n return { ...info, quality: isSlow ? 'slow' : 'online' };\n }\n\n // No Network Information API: fall back to the opt-in ping probe measurement, if any.\n const quality: ConnectionQuality =\n typeof this.probedRttMs === 'number' && this.probedRttMs > this.options.slowRttThresholdMs\n ? 'slow'\n : 'online';\n return {\n quality,\n online: true,\n rttMs: this.probedRttMs,\n };\n }\n}\n\nlet sharedMonitor: ConnectionMonitor | undefined;\n\nfunction getSharedMonitor(): ConnectionMonitor {\n if (!sharedMonitor) sharedMonitor = new ConnectionMonitor();\n return sharedMonitor;\n}\n\n/** Convenience one-shot read of connection quality, without owning a `LowdataClient`. */\nexport function getConnectionQuality(): ConnectionInfo {\n return getSharedMonitor().getStatus();\n}\n\n/** Convenience subscription to connection changes, without owning a `LowdataClient`. */\nexport function onConnectionChange(listener: ConnectionListener): Unsubscribe {\n return getSharedMonitor().subscribe(listener);\n}\n","import type { ConnectionQuality } from '../core/types.js';\nimport type { ImageCompressionOptions } from './types.js';\n\nexport type CompressionPreset = Required<Pick<ImageCompressionOptions, 'maxWidth' | 'quality'>> &\n Pick<ImageCompressionOptions, 'targetSizeKB'>;\n\n/**\n * Aggressive-but-usable presets by connection quality. 'offline' is deliberately the smallest —\n * an image compressed while offline is headed straight into the persistent queue, where every\n * extra KB counts against storage quota and, eventually, the user's data bundle.\n */\nconst PRESETS: Record<ConnectionQuality, CompressionPreset> = {\n online: { maxWidth: 1600, quality: 0.72 },\n slow: { maxWidth: 1000, quality: 0.5, targetSizeKB: 150 },\n offline: { maxWidth: 800, quality: 0.4, targetSizeKB: 80 },\n};\n\nexport function presetForQuality(quality: ConnectionQuality): CompressionPreset {\n return PRESETS[quality];\n}\n","import { getConnectionQuality } from '../core/connection.js';\nimport { presetForQuality } from './qualityPolicy.js';\nimport type { CompressedImageResult, ImageCompressionOptions } from './types.js';\n\nconst DEFAULT_MAX_WIDTH = 1280;\nconst DEFAULT_QUALITY = 0.6;\nconst DEFAULT_MIME: NonNullable<ImageCompressionOptions['mimeType']> = 'image/jpeg';\nconst MIN_QUALITY = 0.35;\nconst QUALITY_STEP = 0.1;\nconst MAX_QUALITY_ITERATIONS = 5;\n\ntype ImageSource = ImageBitmap | HTMLImageElement;\n\nasync function loadImageSource(file: File | Blob): Promise<ImageSource> {\n if (typeof createImageBitmap === 'function') {\n return createImageBitmap(file);\n }\n // Fallback for environments without createImageBitmap (older Safari).\n return new Promise<HTMLImageElement>((resolve, reject) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve(img);\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('lowdata: failed to load image for compression'));\n };\n img.src = url;\n });\n}\n\nfunction computeTargetDimensions(\n sourceWidth: number,\n sourceHeight: number,\n maxWidth: number,\n maxHeight?: number,\n): { width: number; height: number } {\n let width = sourceWidth;\n let height = sourceHeight;\n\n if (width > maxWidth) {\n height = Math.round((height * maxWidth) / width);\n width = maxWidth;\n }\n if (maxHeight && height > maxHeight) {\n width = Math.round((width * maxHeight) / height);\n height = maxHeight;\n }\n return { width, height };\n}\n\nfunction drawToCanvas(source: ImageSource, width: number, height: number): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('lowdata: canvas 2D context is not available in this environment');\n ctx.drawImage(source, 0, 0, width, height);\n return canvas;\n}\n\nfunction canvasToBlob(canvas: HTMLCanvasElement, mimeType: string, quality: number): Promise<Blob> {\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('lowdata: canvas failed to produce an image blob'));\n },\n mimeType,\n quality,\n );\n });\n}\n\n/**\n * Client-side image compression via the Canvas API: downscales to a max dimension and re-encodes\n * at a target quality, optionally iterating quality further down to hit a byte budget. No\n * dependency — avoids pulling in a native/wasm image library, keeping the `lowdata/media` subpath\n * tiny. Known v1 limitation: EXIF orientation can be lost on re-encode in some browsers.\n */\nexport async function compressImage(\n file: File | Blob,\n options: ImageCompressionOptions = {},\n): Promise<CompressedImageResult> {\n const preset = options.connectionAware\n ? presetForQuality(getConnectionQuality().quality)\n : undefined;\n const maxWidth = options.maxWidth ?? preset?.maxWidth ?? DEFAULT_MAX_WIDTH;\n const maxHeight = options.maxHeight;\n const mimeType = options.mimeType ?? DEFAULT_MIME;\n const targetSizeKB = options.targetSizeKB ?? preset?.targetSizeKB;\n let quality = options.quality ?? preset?.quality ?? DEFAULT_QUALITY;\n\n const source = await loadImageSource(file);\n const { width, height } = computeTargetDimensions(\n source.width,\n source.height,\n maxWidth,\n maxHeight,\n );\n const canvas = drawToCanvas(source, width, height);\n if ('close' in source && typeof source.close === 'function') source.close();\n\n let blob = await canvasToBlob(canvas, mimeType, quality);\n\n if (targetSizeKB) {\n const targetBytes = targetSizeKB * 1024;\n let iterations = 0;\n while (\n blob.size > targetBytes &&\n quality > MIN_QUALITY &&\n iterations < MAX_QUALITY_ITERATIONS\n ) {\n quality = Math.max(MIN_QUALITY, quality - QUALITY_STEP);\n blob = await canvasToBlob(canvas, mimeType, quality);\n iterations++;\n }\n }\n\n return { blob, width, height, sizeBytes: blob.size, quality };\n}\n","import { Emitter } from '../core/events.js';\nimport type { Unsubscribe } from '../core/types.js';\n\nexport interface ProgressiveImageState {\n src: string;\n isLoaded: boolean;\n}\n\nexport interface ProgressiveImageLoaderOptions {\n src: string;\n placeholder: string;\n}\n\nexport interface ProgressiveImageLoader {\n subscribe(callback: (state: ProgressiveImageState) => void): Unsubscribe;\n getState(): ProgressiveImageState;\n destroy(): void;\n}\n\n/**\n * Blur-up / placeholder helper: reports the (typically tiny, inlined) placeholder immediately,\n * preloads the full-size image in the background, and reports the swap once it's ready.\n * Framework-agnostic — `lowdata/react`'s `useProgressiveImage` is a thin hook over this.\n */\nexport function createProgressiveImageLoader(\n options: ProgressiveImageLoaderOptions,\n): ProgressiveImageLoader {\n const emitter = new Emitter<ProgressiveImageState>();\n let state: ProgressiveImageState = { src: options.placeholder, isLoaded: false };\n let disposed = false;\n\n const img = typeof Image !== 'undefined' ? new Image() : undefined;\n if (img) {\n img.onload = () => {\n if (disposed) return;\n state = { src: options.src, isLoaded: true };\n emitter.emit(state);\n };\n img.src = options.src;\n } else {\n // No Image constructor available (SSR) — nothing to preload; report the target src directly.\n state = { src: options.src, isLoaded: true };\n }\n\n return {\n subscribe: (callback) => emitter.subscribe(callback),\n getState: () => state,\n destroy: () => {\n disposed = true;\n if (img) img.onload = null;\n emitter.clear();\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/core/events.ts","../src/core/connection.ts","../src/media/qualityPolicy.ts","../src/media/compressImage.ts","../src/media/progressiveImage.ts"],"names":[],"mappings":"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","file":"media.js","sourcesContent":["/** Tiny typed pub-sub. No Node `EventEmitter` dependency, works in any JS runtime. */\nexport class Emitter<T> {\n private listeners = new Set<(value: T) => void>();\n\n subscribe(listener: (value: T) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n emit(value: T): void {\n for (const listener of this.listeners) {\n listener(value);\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n\n get size(): number {\n return this.listeners.size;\n }\n}\n","import { Emitter } from './events.js';\nimport type {\n ConnectionInfo,\n ConnectionListener,\n ConnectionQuality,\n Unsubscribe,\n} from './types.js';\n\n/**\n * The Network Information API (`navigator.connection`) is Chromium-only and not part of the\n * standard DOM lib types, hence this local shape instead of a global augmentation.\n */\ninterface NetworkInformationLike {\n effectiveType?: '4g' | '3g' | '2g' | 'slow-2g';\n downlink?: number;\n rtt?: number;\n saveData?: boolean;\n addEventListener?: (type: 'change', listener: () => void) => void;\n removeEventListener?: (type: 'change', listener: () => void) => void;\n}\n\ntype NavigatorWithConnection = Navigator & { connection?: NetworkInformationLike };\n\nexport interface ConnectionMonitorOptions {\n /**\n * Opt-in latency probe URL used to detect 'slow' connections on browsers without the Network\n * Information API (Safari, Firefox). Never probed automatically on a timer — only once on\n * startup and once per reconnect — because every probe costs a little data.\n */\n pingUrl?: string;\n /** Round-trip time above which the ping probe classifies the connection as 'slow'. Default 600ms. */\n slowRttThresholdMs?: number;\n /** downlink (Mbps) below which the Network Information API classifies as 'slow'. Default 0.5. */\n slowDownlinkMbps?: number;\n /** Timeout for the ping probe itself, in ms. Default 5000. */\n pingTimeoutMs?: number;\n}\n\nconst DEFAULT_SLOW_RTT_MS = 600;\nconst DEFAULT_SLOW_DOWNLINK_MBPS = 0.5;\nconst DEFAULT_PING_TIMEOUT_MS = 5000;\n\nfunction hasNavigator(): boolean {\n return typeof navigator !== 'undefined';\n}\n\n/**\n * Tracks connection quality (online / slow / offline) using the best signal available:\n * `navigator.onLine` + online/offline events as the universal baseline, `navigator.connection`\n * where present, and an optional opt-in ping probe as a cross-browser fallback for 'slow'.\n */\nexport class ConnectionMonitor {\n private emitter = new Emitter<ConnectionInfo>();\n private current: ConnectionInfo;\n private probedRttMs: number | undefined;\n private readonly options: Required<\n Pick<ConnectionMonitorOptions, 'slowRttThresholdMs' | 'slowDownlinkMbps' | 'pingTimeoutMs'>\n > &\n Pick<ConnectionMonitorOptions, 'pingUrl'>;\n private disposed = false;\n private onlineHandler?: () => void;\n private offlineHandler?: () => void;\n private connectionChangeHandler?: () => void;\n\n constructor(options: ConnectionMonitorOptions = {}) {\n this.options = {\n pingUrl: options.pingUrl,\n slowRttThresholdMs: options.slowRttThresholdMs ?? DEFAULT_SLOW_RTT_MS,\n slowDownlinkMbps: options.slowDownlinkMbps ?? DEFAULT_SLOW_DOWNLINK_MBPS,\n pingTimeoutMs: options.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS,\n };\n this.current = this.computeInfo();\n\n if (hasNavigator() && typeof window !== 'undefined') {\n this.onlineHandler = () => {\n this.refresh();\n void this.probeNow();\n };\n this.offlineHandler = () => this.refresh();\n window.addEventListener('online', this.onlineHandler);\n window.addEventListener('offline', this.offlineHandler);\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn?.addEventListener) {\n this.connectionChangeHandler = () => this.refresh();\n conn.addEventListener('change', this.connectionChangeHandler);\n }\n }\n\n if (this.options.pingUrl) {\n void this.probeNow();\n }\n }\n\n getStatus(): ConnectionInfo {\n return this.current;\n }\n\n subscribe(listener: ConnectionListener): Unsubscribe {\n return this.emitter.subscribe(listener);\n }\n\n /** Manually re-run the opt-in ping probe (no-op if no `pingUrl` was configured). */\n async probeNow(): Promise<ConnectionInfo> {\n if (!this.options.pingUrl || !hasNavigator() || navigator.onLine === false) {\n return this.current;\n }\n const start = Date.now();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.options.pingTimeoutMs);\n try {\n await fetch(this.options.pingUrl, {\n method: 'HEAD',\n cache: 'no-store',\n signal: controller.signal,\n });\n this.probedRttMs = Date.now() - start;\n } catch {\n // A failed probe tells us nothing reliable about latency; leave prior measurement as-is.\n } finally {\n clearTimeout(timer);\n }\n return this.refresh();\n }\n\n destroy(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (typeof window !== 'undefined') {\n if (this.onlineHandler) window.removeEventListener('online', this.onlineHandler);\n if (this.offlineHandler) window.removeEventListener('offline', this.offlineHandler);\n }\n if (hasNavigator() && this.connectionChangeHandler) {\n const conn = (navigator as NavigatorWithConnection).connection;\n conn?.removeEventListener?.('change', this.connectionChangeHandler);\n }\n this.emitter.clear();\n }\n\n private refresh(): ConnectionInfo {\n this.current = this.computeInfo();\n this.emitter.emit(this.current);\n return this.current;\n }\n\n private computeInfo(): ConnectionInfo {\n if (!hasNavigator()) {\n // Server/SSR context: assume the best case rather than block anything.\n return { quality: 'online', online: true };\n }\n\n const online = navigator.onLine !== false;\n if (!online) {\n return { quality: 'offline', online: false };\n }\n\n const conn = (navigator as NavigatorWithConnection).connection;\n if (conn) {\n const info: ConnectionInfo = {\n quality: 'online',\n online: true,\n effectiveType: conn.effectiveType,\n downlinkMbps: conn.downlink,\n rttMs: conn.rtt,\n saveData: conn.saveData,\n };\n const isSlow =\n conn.saveData === true ||\n conn.effectiveType === '2g' ||\n conn.effectiveType === 'slow-2g' ||\n (typeof conn.downlink === 'number' && conn.downlink < this.options.slowDownlinkMbps) ||\n (typeof conn.rtt === 'number' && conn.rtt > this.options.slowRttThresholdMs);\n return { ...info, quality: isSlow ? 'slow' : 'online' };\n }\n\n // No Network Information API: fall back to the opt-in ping probe measurement, if any.\n const quality: ConnectionQuality =\n typeof this.probedRttMs === 'number' && this.probedRttMs > this.options.slowRttThresholdMs\n ? 'slow'\n : 'online';\n return {\n quality,\n online: true,\n rttMs: this.probedRttMs,\n };\n }\n}\n\nlet sharedMonitor: ConnectionMonitor | undefined;\n\nfunction getSharedMonitor(): ConnectionMonitor {\n if (!sharedMonitor) sharedMonitor = new ConnectionMonitor();\n return sharedMonitor;\n}\n\n/** Convenience one-shot read of connection quality, without owning a `LowdataClient`. */\nexport function getConnectionQuality(): ConnectionInfo {\n return getSharedMonitor().getStatus();\n}\n\n/** Convenience subscription to connection changes, without owning a `LowdataClient`. */\nexport function onConnectionChange(listener: ConnectionListener): Unsubscribe {\n return getSharedMonitor().subscribe(listener);\n}\n","import type { ConnectionQuality } from '../core/types.js';\nimport type { ImageCompressionOptions } from './types.js';\n\nexport type CompressionPreset = Required<Pick<ImageCompressionOptions, 'maxWidth' | 'quality'>> &\n Pick<ImageCompressionOptions, 'targetSizeKB'>;\n\n/**\n * Aggressive-but-usable presets by connection quality. 'offline' is deliberately the smallest —\n * an image compressed while offline is headed straight into the persistent queue, where every\n * extra KB counts against storage quota and, eventually, the user's data bundle.\n */\nconst PRESETS: Record<ConnectionQuality, CompressionPreset> = {\n online: { maxWidth: 1600, quality: 0.72 },\n slow: { maxWidth: 1000, quality: 0.5, targetSizeKB: 150 },\n offline: { maxWidth: 800, quality: 0.4, targetSizeKB: 80 },\n};\n\nexport function presetForQuality(quality: ConnectionQuality): CompressionPreset {\n return PRESETS[quality];\n}\n","import { getConnectionQuality } from '../core/connection.js';\nimport { presetForQuality } from './qualityPolicy.js';\nimport type { CompressedImageResult, ImageCompressionOptions } from './types.js';\n\nconst DEFAULT_MAX_WIDTH = 1280;\nconst DEFAULT_QUALITY = 0.6;\nconst DEFAULT_MIME: NonNullable<ImageCompressionOptions['mimeType']> = 'image/jpeg';\nconst MIN_QUALITY = 0.35;\nconst QUALITY_STEP = 0.1;\nconst MAX_QUALITY_ITERATIONS = 5;\n\ntype ImageSource = ImageBitmap | HTMLImageElement;\n\nasync function loadImageSource(file: File | Blob): Promise<ImageSource> {\n if (typeof createImageBitmap === 'function') {\n // `imageOrientation: 'from-image'` is the spec-defined way to force EXIF-orientation-correct\n // decoding regardless of a given browser's default — without it, a photo taken sideways on a\n // phone can come out of `drawImage()` still sideways, since canvas pixel data (unlike an\n // `<img>` element's own on-screen rendering) doesn't reliably apply EXIF orientation itself.\n return createImageBitmap(file, { imageOrientation: 'from-image' });\n }\n // Fallback for environments without createImageBitmap (older Safari).\n return new Promise<HTMLImageElement>((resolve, reject) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve(img);\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('lowdata: failed to load image for compression'));\n };\n img.src = url;\n });\n}\n\nfunction computeTargetDimensions(\n sourceWidth: number,\n sourceHeight: number,\n maxWidth: number,\n maxHeight?: number,\n): { width: number; height: number } {\n let width = sourceWidth;\n let height = sourceHeight;\n\n if (width > maxWidth) {\n height = Math.round((height * maxWidth) / width);\n width = maxWidth;\n }\n if (maxHeight && height > maxHeight) {\n width = Math.round((width * maxHeight) / height);\n height = maxHeight;\n }\n return { width, height };\n}\n\nfunction drawToCanvas(source: ImageSource, width: number, height: number): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('lowdata: canvas 2D context is not available in this environment');\n ctx.drawImage(source, 0, 0, width, height);\n return canvas;\n}\n\nfunction canvasToBlob(canvas: HTMLCanvasElement, mimeType: string, quality: number): Promise<Blob> {\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('lowdata: canvas failed to produce an image blob'));\n },\n mimeType,\n quality,\n );\n });\n}\n\n/**\n * Client-side image compression via the Canvas API: downscales to a max dimension and re-encodes\n * at a target quality, optionally iterating quality further down to hit a byte budget. No\n * dependency — avoids pulling in a native/wasm image library, keeping the `lowdata/media` subpath\n * tiny. Known v1 limitation: EXIF orientation can be lost on re-encode in some browsers.\n */\nexport async function compressImage(\n file: File | Blob,\n options: ImageCompressionOptions = {},\n): Promise<CompressedImageResult> {\n const preset = options.connectionAware\n ? presetForQuality(getConnectionQuality().quality)\n : undefined;\n const maxWidth = options.maxWidth ?? preset?.maxWidth ?? DEFAULT_MAX_WIDTH;\n const maxHeight = options.maxHeight;\n const mimeType = options.mimeType ?? DEFAULT_MIME;\n const targetSizeKB = options.targetSizeKB ?? preset?.targetSizeKB;\n let quality = options.quality ?? preset?.quality ?? DEFAULT_QUALITY;\n\n const source = await loadImageSource(file);\n const { width, height } = computeTargetDimensions(\n source.width,\n source.height,\n maxWidth,\n maxHeight,\n );\n const canvas = drawToCanvas(source, width, height);\n if ('close' in source && typeof source.close === 'function') source.close();\n\n let blob = await canvasToBlob(canvas, mimeType, quality);\n\n if (targetSizeKB) {\n const targetBytes = targetSizeKB * 1024;\n let iterations = 0;\n while (\n blob.size > targetBytes &&\n quality > MIN_QUALITY &&\n iterations < MAX_QUALITY_ITERATIONS\n ) {\n quality = Math.max(MIN_QUALITY, quality - QUALITY_STEP);\n blob = await canvasToBlob(canvas, mimeType, quality);\n iterations++;\n }\n }\n\n return { blob, width, height, sizeBytes: blob.size, quality };\n}\n","import { Emitter } from '../core/events.js';\nimport type { Unsubscribe } from '../core/types.js';\n\nexport interface ProgressiveImageState {\n src: string;\n isLoaded: boolean;\n}\n\nexport interface ProgressiveImageLoaderOptions {\n src: string;\n placeholder: string;\n}\n\nexport interface ProgressiveImageLoader {\n subscribe(callback: (state: ProgressiveImageState) => void): Unsubscribe;\n getState(): ProgressiveImageState;\n destroy(): void;\n}\n\n/**\n * Blur-up / placeholder helper: reports the (typically tiny, inlined) placeholder immediately,\n * preloads the full-size image in the background, and reports the swap once it's ready.\n * Framework-agnostic — `lowdata/react`'s `useProgressiveImage` is a thin hook over this.\n */\nexport function createProgressiveImageLoader(\n options: ProgressiveImageLoaderOptions,\n): ProgressiveImageLoader {\n const emitter = new Emitter<ProgressiveImageState>();\n let state: ProgressiveImageState = { src: options.placeholder, isLoaded: false };\n let disposed = false;\n\n const img = typeof Image !== 'undefined' ? new Image() : undefined;\n if (img) {\n img.onload = () => {\n if (disposed) return;\n state = { src: options.src, isLoaded: true };\n emitter.emit(state);\n };\n img.src = options.src;\n } else {\n // No Image constructor available (SSR) — nothing to preload; report the target src directly.\n state = { src: options.src, isLoaded: true };\n }\n\n return {\n subscribe: (callback) => emitter.subscribe(callback),\n getState: () => state,\n destroy: () => {\n disposed = true;\n if (img) img.onload = null;\n emitter.clear();\n },\n };\n}\n"]}
package/dist/network.cjs CHANGED
@@ -272,16 +272,23 @@ function getSharedDb() {
272
272
  }
273
273
  return sharedDbPromise;
274
274
  }
275
- function createDbFallbackAccessor(getDb) {
275
+ function createDbFallbackAccessor(getDb, onError) {
276
276
  let dbAvailable = true;
277
277
  return {
278
278
  async run(fn, fallback) {
279
279
  if (!dbAvailable) return fallback();
280
+ let db;
280
281
  try {
281
- const db = await getDb();
282
- return await fn(db);
283
- } catch {
282
+ db = await getDb();
283
+ } catch (error) {
284
284
  dbAvailable = false;
285
+ onError == null ? void 0 : onError(error, { scope: "db-open" });
286
+ return fallback();
287
+ }
288
+ try {
289
+ return await fn(db);
290
+ } catch (error) {
291
+ onError == null ? void 0 : onError(error, { scope: "db-operation" });
285
292
  return fallback();
286
293
  }
287
294
  },
@@ -307,9 +314,9 @@ var LowdataRequestError = class extends Error {
307
314
  var STORE = "queue";
308
315
  var PRIORITY_ORDER = { high: 0, normal: 1, low: 2 };
309
316
  var RequestQueue = class {
310
- constructor(getDb) {
317
+ constructor(getDb, onError) {
311
318
  this.memory = /* @__PURE__ */ new Map();
312
- this.accessor = createDbFallbackAccessor(getDb);
319
+ this.accessor = createDbFallbackAccessor(getDb, onError);
313
320
  }
314
321
  isPersistent() {
315
322
  return this.accessor.isPersistent();
@@ -624,12 +631,16 @@ var SyncManager = class {
624
631
  * or safety poll simply tries again.
625
632
  */
626
633
  async drain() {
627
- var _a, _b, _c, _d;
634
+ var _a, _b, _c, _d, _e, _f;
628
635
  if (this.draining || this.disposed) return;
629
636
  if (this.opts.connection.getStatus().quality === "offline") return;
630
637
  this.draining = true;
631
638
  try {
632
- const db = await this.opts.getDb().catch(() => void 0);
639
+ const db = await this.opts.getDb().catch((error) => {
640
+ var _a2, _b2;
641
+ (_b2 = (_a2 = this.opts).onError) == null ? void 0 : _b2.call(_a2, error, { scope: "db-open" });
642
+ return void 0;
643
+ });
633
644
  const lock = await acquireSyncLock(db, SYNC_LOCK_NAME, this.ownerId);
634
645
  if (!lock) return;
635
646
  let succeeded = 0;
@@ -652,7 +663,8 @@ var SyncManager = class {
652
663
  await lock.release().catch(() => {
653
664
  });
654
665
  }
655
- } catch {
666
+ } catch (error) {
667
+ (_f = (_e = this.opts).onError) == null ? void 0 : _f.call(_e, error, { scope: "sync" });
656
668
  } finally {
657
669
  this.draining = false;
658
670
  }
@@ -738,6 +750,9 @@ var MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"
738
750
  function defaultShouldQueueOffline({ method }) {
739
751
  return MUTATING_METHODS.has(method);
740
752
  }
753
+ var defaultOnError = (error, { scope }) => {
754
+ console.warn(`lowdata: internal error (${scope})`, error);
755
+ };
741
756
  function normalizeHeaders(headers) {
742
757
  if (!headers) return void 0;
743
758
  if (headers instanceof Headers) {
@@ -766,15 +781,18 @@ var LowdataClient = class {
766
781
  this.config = config;
767
782
  this.syncEmitter = new Emitter();
768
783
  this.destroyed = false;
784
+ var _a;
785
+ const onError = (_a = config.onError) != null ? _a : defaultOnError;
769
786
  this.monitor = new ConnectionMonitor(config.connection);
770
- this.requestQueue = new RequestQueue(() => getSharedDb());
787
+ this.requestQueue = new RequestQueue(() => getSharedDb(), onError);
771
788
  this.syncManager = new SyncManager({
772
789
  queue: this.requestQueue,
773
790
  connection: this.monitor,
774
791
  getDb: () => getSharedDb(),
775
792
  retryConfig: config.retry,
776
793
  syncConcurrency: config.syncConcurrency,
777
- onEvent: (event) => this.syncEmitter.emit(event)
794
+ onEvent: (event) => this.syncEmitter.emit(event),
795
+ onError
778
796
  });
779
797
  this.connection = {
780
798
  getStatus: () => this.monitor.getStatus(),