A beautiful, type-safe storage library for Elm applications. Simple, elegant, and powerful.
Just like the Ipê tree that blooms magnificently, this library aims to make your Elm applications flourish with elegant and reliable storage capabilities.
elm install heitorgandolfi/ipe-elmport module Main exposing (..)
import Ipe exposing (StorageType(..), Error(..))
import Json.Encode as Encode
import Json.Decode as Decode
-- Required ports
port saveToStorage : Encode.Value -> Cmd msg
port loadFromStorage : Encode.Value -> Cmd msg
port removeFromStorage : Encode.Value -> Cmd msg
port receiveStorageResult : (Encode.Value -> msg) -> Sub msg
-- Helper functions to connect Ipê with your ports
saveToIpe : StorageType -> String -> Encode.Value -> Cmd msg
saveToIpe storageType key value =
Ipe.save storageType key value |> saveToStorage
loadFromIpe : StorageType -> String -> Cmd msg
loadFromIpe storageType key =
Ipe.load storageType key |> loadFromStorage
removeFromIpe : StorageType -> String -> Cmd msg
removeFromIpe storageType key =
Ipe.remove storageType key |> removeFromStorageCopy this code to your JavaScript file and call setupIpePorts(app) after initializing your Elm app:
function setupIpePorts(app) {
app.ports.saveToStorage.subscribe((payload) => {
try {
const { storageType, key, value } = payload;
const data = JSON.stringify(value);
if (storageType === "local") {
localStorage.setItem(key, data);
} else {
sessionStorage.setItem(key, data);
}
} catch (e) {
console.error("Ipê (save): Failed to save to storage.", e);
}
});
app.ports.loadFromStorage.subscribe((payload) => {
try {
const { storageType, key } = payload;
let data = null;
if (storageType === "local") {
data = localStorage.getItem(key);
} else {
data = sessionStorage.getItem(key);
}
let parsed = null;
if (data !== null) {
try {
parsed = JSON.parse(data);
} catch (parseError) {
parsed = data;
}
}
app.ports.receiveStorageResult.send({ key, data: parsed });
} catch (e) {
console.error("Ipê (load): Failed to load from storage.", e);
}
});
app.ports.removeFromStorage.subscribe((payload) => {
try {
const { storageType, key } = payload;
if (storageType === "local") {
localStorage.removeItem(key);
} else {
sessionStorage.removeItem(key);
}
} catch (e) {
console.error("Ipê (remove): Failed to remove from storage.", e);
}
});
}
// Initialize your Elm app and connect the ports
var app = Elm.Main.init({ node: document.getElementById('app') });
setupIpePorts(app);type Msg
= SaveUser
| LoadUser
| StorageReceived Encode.Value
type alias User = { name : String, email : String }
-- Save data
saveUser : User -> Cmd Msg
saveUser user =
saveToIpe Local "current-user" (encodeUser user)
-- Load data
loadUser : Cmd Msg
loadUser =
loadFromIpe Local "current-user"
-- Remove data
removeUser : Cmd Msg
removeUser =
removeFromIpe Local "current-user"
-- Subscribe to storage events
subscriptions : Model -> Sub Msg
subscriptions _ =
receiveStorageResult StorageReceived
-- Handle storage responses
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
StorageReceived value ->
case Decode.decodeValue (Decode.field "key" Decode.string) value of
Ok "current-user" ->
handleUserData value model
_ ->
(model, Cmd.none)
-- ... other cases
-- Handle user data with Ipê's decoder
handleUserData : Encode.Value -> Model -> (Model, Cmd Msg)
handleUserData value model =
case Ipe.decodeStorageResult userDecoder value of
Ok user ->
({ model | user = Just user }, Cmd.none)
Err (Ipe.DecodeError error) ->
({ model | error = Just ("Decode error: " ++ Decode.errorToString error) }, Cmd.none)
Err Ipe.NotFound ->
({ model | user = Nothing }, Cmd.none)type StorageType
= Local -- localStorage
| Session -- sessionStoragetype Error
= DecodeError Decode.Error -- JSON decoding failed
| NotFound -- Key not found in storagesave : StorageType -> String -> Encode.Value -> Encode.ValueCreate a save command payload.
-- Create save payload
savePayload = Ipe.save Local "username" (Encode.string "john")
-- Use with your port
savePayload |> saveToStorageload : StorageType -> String -> Encode.ValueCreate a load command payload.
-- Create load payload
loadPayload = Ipe.load Local "username"
-- Use with your port
loadPayload |> loadFromStorageremove : StorageType -> String -> Encode.ValueCreate a remove command payload.
-- Create remove payload
removePayload = Ipe.remove Local "username"
-- Use with your port
removePayload |> removeFromStoragedecodeStorageResult : Decode.Decoder a -> Encode.Value -> Result Error aDecode storage results from JavaScript.
-- Handle storage response
case Ipe.decodeStorageResult userDecoder storageValue of
Ok user ->
-- Successfully decoded user
Err (DecodeError error) ->
-- Failed to decode
Err NotFound ->
-- Key not found in storagestorageTypeToString : StorageType -> StringConvert storage type to string (for advanced usage).
Ipe.storageTypeToString Local -- "local"
Ipe.storageTypeToString Session -- "session"Check out the /example directory for a complete, beautiful example application that demonstrates:
cd example
elm make Main.elm --output=main.js
# Open index.html in your browserstorage.js)The JavaScript bridge is essential for Ipê to work. It handles the actual browser storage operations and communicates with your Elm application through ports.
setupIpePorts function to your projectipe-elm/
├── src/
│ └── Ipe.elm # Main library module
├── example/
│ ├── Main.elm # Example application
│ ├── index.html # Example HTML with styling
│ └── main.js # Compiled example (generated)
├── js/
│ └── storage.js # JavaScript bridge
├── elm.json # Package configuration
└── README.md # This fileContributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
git clone https://github.com/heitorgandolfi/ipe-elm.git
cd ipe-elm
elm make src/Ipe.elmBSD 3-Clause - see LICENSE for details.
Made with 🌸 by the Elm community Bringing the beauty of Ipê flowers to your Elm applications