Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fresh 0.2.0 pluggable storage and formatting - #18

Merged
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0
Aug 4, 2026
Merged

Fresh 0.2.0 pluggable storage and formatting#18
zekageri merged 164 commits into
mainfrom
feature/pluggable-storage-0.2.0

Conversation

@zekageri

@zekagerizekageri commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns Fresh 0.2.0 around owned storage backends and a direct application-file facade, and adds a destructive whole-volume formatting lifecycle.

Public API

  • FreshConfig contains database behavior only
  • Explicit backends are passed to db.init(path, config, storage) and become Fresh-owned
  • db.init(path, config) remains as a convenient default-LittleFS overload
  • Application files use db.storage() directly
  • Adds FreshInitResult as the initialization result type
  • Adds Fresh::format() for explicitly destructive whole-volume formatting
Fresh db;
FreshInitResult initialized = db.init("/fresh");
if (!initialized) return;
FreshResult formatted = db.format();
if (!formatted) return;
// The same Fresh instance is now an empty initialized database.
db.createModel("settings");

Destructive formatting

format() deletes the Fresh database, application files accessed through db.storage(), and unrelated files on the same configured filesystem. It is intentionally different from dropAllModels().

The operation:

  • blocks new database and application-storage operations
  • cancels backup work and stops background sync without a final write
  • closes and invalidates tracked FreshFile handles
  • formats the complete backend
  • invalidates retained FreshModel handles
  • rebuilds the database root and an empty durable manifest
  • restarts the sync task and preserves existing storage facades after success
  • fails closed when an error occurs after native formatting begins

LittleFS, SDSPI, SDMMC, and eMMC use their ESP-IDF native format paths. Custom backends remain unsupported by default and can opt in with supportsFormat() and formatBackend().

Built-in storage

  • FreshLittleFSStorage through ESP-IDF LittleFS VFS
  • FreshSDStorage through ESP-IDF SDSPI or SDMMC
  • FreshEMMCStorage with 1-, 4-, and 8-bit SDMMC configuration
  • Fresh-owned custom FreshStorage and FreshFileBackend implementations

Fresh does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Application files and lifecycle

  • db.storage() exposes open/read/write, complete-file helpers, existence and size queries, recursive directory creation, listing, removal, rename, and capacity diagnostics
  • The configured Fresh database root is protected from application operations
  • Move-only FreshFile handles retain explicit sync(), close(), and syncAndClose() durability boundaries
  • Short writes and read, sync, close, capacity, and existence-query failures are propagated
  • Open application files cause deinit() to return FreshStatus::Busy
  • Backup archives can use the same selected backend without a separate filesystem wrapper

Regression coverage

Adds StorageFormatRegressionTest with a custom in-memory backend covering:

  • unsupported formatting without lifecycle disruption
  • complete-volume deletion
  • stale file and model handle invalidation
  • retained storage-facade usability after success
  • durable empty database bootstrap
  • continued persistence after formatting and reinitialization
  • repeated formatting
  • native formatter failure and fail-closed behavior

Validation

The implementation passed structural source checks for balanced delimiters and the production-source audit patterns. The repository CI build matrix is the authoritative compilation validation for ESP32, ESP32-C3, ESP32-S3, and ESP32-P4 and will run on this PR update.

Hardware qualification

Physical validation remains required for whole-volume formatting and remounting on representative LittleFS, SDSPI, SDMMC, and eMMC hardware, including absent/full media, card removal, power loss, and board-specific power/reset sequencing.

@zekagerizekageri changed the title Fresh 0.2.0 pluggable storageFresh 0.2.0 pluggable storage and formattingAug 4, 2026
@zekageri
zekageri marked this pull request as draft August 4, 2026 07:42
@zekageri
zekageri marked this pull request as ready for review August 4, 2026 13:36
@zekageri
zekageri merged commit 705f74c into mainAug 4, 2026
13 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@zekageri