Repository navigation
Chore/zpl escape and cache limits #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
0fd4c48
fix(registry): sanitize serial content for ^SN/^SF emit
u8array 368255d
fix(cache): cap image/font byte size and validate MIME at the loader
u8array 7a559a4
refactor(lib): dedupe localStorage hydration/setItem patterns
u8array 0113e23
fix(lib): skip null reads in hydrateLocalStoragePrefix
u8array 87b6b96
fix(lib): harden hydrateLocalStoragePrefix against external/concurren…
u8array File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { describe, it, expect, vi } from "vitest"; | ||
| import { | ||
| hydrateLocalStoragePrefix, | ||
| safeLocalStorageSet, | ||
| } from "./localStorageBucket"; | ||
|
|
||
| // The vitest setup's localStorage shim retains keys after `clear()` (sets to | ||
| // undefined), so cross-test ghost keys would leak into hydration. Each test | ||
| // uses a unique prefix instead of relying on `clear`. | ||
|
|
||
| describe("hydrateLocalStoragePrefix", () => { | ||
| it("forwards parsed entries that match the prefix", () => { | ||
| localStorage.setItem("hyd-a-1", JSON.stringify({ id: "1" })); | ||
| localStorage.setItem("hyd-a-2", JSON.stringify({ id: "2" })); | ||
| localStorage.setItem("other-x", JSON.stringify({ id: "x" })); | ||
|
|
||
| const seen: { id: string }[] = []; | ||
| hydrateLocalStoragePrefix<{ id: string }>("hyd-a-", (e) => seen.push(e)); | ||
|
|
||
| expect(seen.map((e) => e.id).sort()).toEqual(["1", "2"]); | ||
| }); | ||
|
|
||
| it("silently drops corrupt JSON", () => { | ||
| localStorage.setItem("hyd-b-good", JSON.stringify({ id: "good" })); | ||
| localStorage.setItem("hyd-b-bad", "{not-json"); | ||
|
|
||
| const seen: { id: string }[] = []; | ||
| hydrateLocalStoragePrefix<{ id: string }>("hyd-b-", (e) => seen.push(e)); | ||
|
|
||
| expect(seen).toEqual([{ id: "good" }]); | ||
| }); | ||
| }); | ||
|
|
||
| describe("safeLocalStorageSet", () => { | ||
| it("writes the value when storage accepts it", () => { | ||
| safeLocalStorageSet("sls-write-key", "v"); | ||
| expect(localStorage.getItem("sls-write-key")).toBe("v"); | ||
| }); | ||
|
|
||
| it("swallows quota errors", () => { | ||
| const spy = vi | ||
| .spyOn(localStorage, "setItem") | ||
| .mockImplementation(() => { | ||
| throw new Error("QuotaExceededError"); | ||
| }); | ||
| try { | ||
| expect(() => safeLocalStorageSet("sls-quota-key", "v")).not.toThrow(); | ||
| } finally { | ||
| spy.mockRestore(); | ||
| } | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /** | ||
| * Prefix-keyed localStorage helpers shared by the image / font caches. | ||
| * | ||
| * Both caches persist data-URL-bearing entries so reloads survive, but they | ||
| * must never crash the app: quota exhaustion and corrupt JSON are routine. | ||
| * These helpers centralise the two failure modes (quota on write, corrupt | ||
| * entries on hydration) so each cache module stays focused on its domain. | ||
| */ | ||
|
|
||
| /** | ||
| * Iterate every localStorage entry whose key starts with `prefix`, parse it | ||
| * as JSON, and forward the parsed value to `accept`. Corrupt entries are | ||
| * silently dropped — runtime hydration must never throw. | ||
| */ | ||
| export function hydrateLocalStoragePrefix<T>( | ||
| prefix: string, | ||
| accept: (entry: T) => void, | ||
| ): void { | ||
| for (let i = 0; i < localStorage.length; i++) { | ||
| const key = localStorage.key(i); | ||
| if (!key?.startsWith(prefix)) continue; | ||
| const raw = localStorage.getItem(key); | ||
| if (raw === null) continue; | ||
| try { | ||
| accept(JSON.parse(raw) as T); | ||
| } catch { | ||
| // ignore corrupt entries | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Best-effort `setItem` that swallows quota errors. Callers keep an | ||
| * in-memory copy as the authoritative state; localStorage is only the | ||
| * cross-reload survival mechanism. | ||
| */ | ||
| export function safeLocalStorageSet(key: string, value: string): void { | ||
| try { | ||
| localStorage.setItem(key, value); | ||
| } catch { | ||
| // localStorage full — caller's in-memory copy stays authoritative | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.