# Alpine.js `Alpine.global()` → `Alpine.store()` Migration Guide - Part 1 ## 🔴 Critical Issue Identified **Problem**: Your codebase uses `Alpine.global()` which **DOES NOT EXIST** in Alpine.js v3.15.8. This is causing the error: ``` Uncaught TypeError: p.global is not a function ``` ## 📋 Root Cause Analysis 1. **Invalid API Usage**: `Alpine.global()` is not a valid Alpine.js v3 method 2. **26 Occurrences**: Found across 26 TypeScript files 3. **Breaking Impact**: Theme switcher and all Alpine namespaces are broken ## ✅ Solution Overview Replace all `Alpine.global()` calls with `Alpine.store()`, the correct Alpine.js v3 API for registering global utilities. **Current Pattern (Broken)**: ```typescript Alpine.global("namespace", { function1: () => { ... }, function2: () => { ... } }); ``` **New Pattern (Correct)**: ```typescript Alpine.store("namespace", { function1: () => { ... }, function2: () => { ... } }); ``` **Template Usage Changes**: - **Old**: `@click="namespace.function()"` → `@click="$store.namespace.function()"` --- ## 📁 Files Requiring Changes (26 TypeScript files) ### Step 1: Update TypeScript Files For each file, replace `Alpine.global()` with `Alpine.store()`: | File | Line | Namespace | Action | |------|------|-----------|--------| | `admin.ts` | 399 | `admin` | Replace `Alpine.global` with `Alpine.store` | | `api.ts` | 102 | `api` | Replace `Alpine.global` with `Alpine.store` | | `api-explorer-docs.ts` | 135 | `apiExplorerDoc` | Replace `Alpine.global` with `Alpine.store` | | `bookshelf.ts` | 199 | `bookshelf` | Replace `Alpine.global` with `Alpine.store` | | `collection-rules.ts` | 411 | `collectionRules` | Replace `Alpine.global` with `Alpine.store` | | `collections.ts` | 917 | `collections` | Replace `Alpine.global` with `Alpine.store` | | `conflicts.ts` | 219 | `conflicts` | Replace `Alpine.global` with `Alpine.store` | | `device-management.ts` | 559 | `devices` | Replace `Alpine.global` with `Alpine.store` | | `docs.ts` | 96, 103 | `docs` | Replace `Alpine.global` with `Alpine.store` (2 occurrences) | | `header.ts` | 35 | `header` | Replace `Alpine.global` with `Alpine.store` | | `index.ts` | 46 | `index` | Replace `Alpine.global` with `Alpine.store` | | `library.ts` | 683 | `library` | Replace `Alpine.global` with `Alpine.store` | | `linking.ts` | 204 | `linking` | Replace `Alpine.global` with `Alpine.store` | | `login.ts` | 25 | `login` | Replace `Alpine.global` with `Alpine.store` | | `password_validation.ts` | 192 | `validation` | Replace `Alpine.global` with `Alpine.store` | | `profile.ts` | 44 | `profile` | Replace `Alpine.global` with `Alpine.store` | | `profile-modal.ts` | 29 | `profileModal` | Replace `Alpine.global` with `Alpine.store` | | `queue.ts` | 167 | `queue` | Replace `Alpine.global` with `Alpine.store` | | `register.ts` | 14 | `register` | Replace `Alpine.global` with `Alpine.store` | | `search.ts` | 310 | `search` | Replace `Alpine.global` with `Alpine.store` | | `themeDropdown.ts` | 24 | `themeDropdown` | Replace `Alpine.global` with `Alpine.store` | | `toast.ts` | 229 | `showToast` | Replace `Alpine.global` with `Alpine.store` | | `toast-error.ts` | 33 | `toastError` | Replace `Alpine.global` with `Alpine.store` | | `unlinked_books.ts` | 430 | `unlinkedBooks` | Replace `Alpine.global` with `Alpine.store` | | `woodPaneling.ts` | 85 | `woodPaneling` | Replace `Alpine.global` with `Alpine.store` | ### Step 2: Update Template Files For each `.templ` file that uses Alpine namespaces, update the syntax: **Old Syntax**: ```html
``` **New Syntax**: ```html
``` **Templates Requiring Updates** (to be identified by searching for namespace usage): - All templates using `@click="admin."` - All templates using `@click="api."` - All templates using `@click="header."` - All templates using `@click="showToast."` - All templates using any other namespace from the list above --- ## 🔧 Detailed Step-by-Step Instructions ### Phase 1: Update TypeScript Source Files #### Step 1.1: Backup Current State ```bash cd /home/nymusicman/Code/bookhoard git add -A git commit -m "Backup before Alpine.global() → Alpine.store() migration" ``` #### Step 1.2: Find All `Alpine.global` Occurrences ```bash cd /home/nymusicman/Code/bookhoard/web/src grep -rn "Alpine\.global" . | tee alpine-global-occurrences.txt ``` Expected output: 26 occurrences across 26 files #### Step 1.3: Replace All `Alpine.global` with `Alpine.store` **Option A: Manual Replacement (Recommended for Understanding)** For each file in the table above: 1. Open the file 2. Find the `Alpine.global()` call 3. Replace `Alpine.global` with `Alpine.store` 4. Save the file **Example** (`web/src/toast.ts:229`): ```typescript // BEFORE (Line 229): Alpine.global("showToast", { error: (message: string, duration?: number) => showToast(message, "error", duration), success: (message: string, duration?: number) => showToast(message, "success", duration), info: (message: string, duration?: number) => showToast(message, "info", duration), }); // AFTER (Line 229): Alpine.store("showToast", { error: (message: string, duration?: number) => showToast(message, "error", duration), success: (message: string, duration?: number) => showToast(message, "success", duration), info: (message: string, duration?: number) => showToast(message, "info", duration), }); ``` **Option B: Automated Replacement (Faster)** ```bash cd /home/nymusicman/Code/bookhoard/web/src find . -name "*.ts" -type f -exec sed -i 's/Alpine\.global(/Alpine.store(/g' {} \; ``` ⚠️ **Warning**: Automated replacement will change all 26 occurrences at once. Verify with grep first: ```bash grep -rn "Alpine\.global" . # Should return nothing after replacement grep -rn "Alpine\.store" . # Should show all 26 occurrences ``` #### Step 1.4: Verify Changes ```bash cd /home/nymusicman/Code/bookhoard/web/src grep -c "Alpine\.store" *.ts | grep -v ":0" ``` Expected: Each of the 26 files should show `1` occurrence (except `docs.ts` which should show `2`) #### Step 1.5: Rebuild TypeScript ```bash cd /home/nymusicman/Code/bookhoard/web npm run build:ts ``` Expected output: ``` > bookhoard@1.0.0 build:ts > cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && esbuild web/src/main.ts --bundle --outfile=web/static/main.js --sourcemap --target=es2020 --minify web/static/main.js 167.9kb web/static/main.js.map 538.7kb ⚡ Done in 18ms ``` --- ### Phase 2: Update Template Files #### Step 2.1: Find All Namespace Usage in Templates ```bash cd /home/nymusicman/Code/bookhoard/templates grep -rn '@click="' . ``` This will show all click handlers that use namespaces. Look for patterns like: - `@click="admin.deleteConfirm"` - `@click="api.getUrl"` - `@click="header.changeThemeTo"` - `@click="showToast.error"` - etc. #### Step 2.2: Create Mapping Document Create a text file with the old → new namespace mappings: ``` admin.function() → $store.admin.function() api.function() → $store.api.function() apiExplorerDoc.function() → $store.apiExplorerDoc.function() bookshelf.function() → $store.bookshelf.function() collectionRules.function() → $store.collectionRules.function() collections.function() → $store.collections.function() conflicts.function() → $store.conflicts.function() devices.function() → $store.devices.function() docs.function() → $store.docs.function() header.function() → $store.header.function() index.function() → $store.index.function() library.function() → $store.library.function() linking.function() → $store.linking.function() login.function() → $store.login.function() validation.function() → $store.validation.function() profile.function() → $store.profile.function() profileModal.function() → $store.profileModal.function() queue.function() → $store.queue.function() register.function() → $store.register.function() search.function() → $store.search.function() themeDropdown.function() → $store.themeDropdown.function() showToast.function() → $store.showToast.function() toastError.function() → $store.toastError.function() unlinkedBooks.function() → $store.unlinkedBooks.function() woodPaneling.function() → $store.woodPaneling.function() ``` #### Step 2.3: Update Templates (Iterative Approach) **For each namespace found in templates:** 1. **Search for all usages**: ```bash cd /home/nymusicman/Code/bookhoard/templates grep -rn 'namespace\.' . | grep "@click" ``` Replace `namespace` with the actual namespace name (e.g., `admin`, `header`, etc.) 2. **Replace in each file** manually or using find/replace: - Find: `@click="namespace.functionName"` - Replace: `@click="$store.namespace.functionName"` **Example for `header.templ`:** **Before**: ```templ @click="header.changeThemeTo('wood-light')" @click="header.logout()" ``` **After**: ```templ @click="$store.header.changeThemeTo('wood-light')" @click="$store.header.logout()" ``` #### Step 2.4: Critical Template Priority Update templates in this order (most critical first): 1. **`header.templ`** - Theme switcher (currently broken) 2. **`index.templ`** - Main page 3. **`library.templ`** - Core functionality 4. **`collections.templ`** - Collection management 5. **`bookshelf.templ`** - Book display 6. **`api_explorer.templ`** - API testing 7. **`admin.templ`** - Admin panel 8. **`profile.templ`** - User profile 9. **`login.templ`** - Authentication 10. **`register.templ`** - Registration 11. **All other templates** with namespace usage --- ### Phase 3: Test the Migration #### Step 3.1: Rebuild Container ```bash cd /home/nymusicman/Code/bookhoard docker-compose build ``` #### Step 3.2: Restart Container ```bash docker-compose down docker-compose up -d ``` #### Step 3.3: Verify Bundle ```bash podman exec bookhoard grep -c "\.store(" /root/web/static/main.js ``` Expected: Should show all `Alpine.store()` calls (not `Alpine.global()`) #### Step 3.4: Test Theme Switcher (Critical) 1. Open `http://localhost:8765` in browser 2. Hard refresh: `Ctrl+Shift+R` 3. Open browser console (F12) 4. Click theme dropdown 5. Select a theme 6. **Expected**: Theme changes without error 7. **Expected Console**: ```javascript Alpine.store('showToast') // Should return object, not undefined Alpine.store('header') // Should return object, not undefined ``` #### Step 3.5: Verify No Errors Check browser console for: - ✅ No `p.global is not a function` errors - ✅ No `Uncaught TypeError` messages - ✅ Alpine version shows: `3.15.8` - ✅ Stores are accessible via `$store` --- ## 📊 Testing Checklist After completing the migration, verify: - [ ] No `Alpine.global` in any `.ts` file - [ ] All 26 `Alpine.store` registrations present - [ ] Bundle size approximately 168KB - [ ] Browser console shows no errors - [ ] Theme switcher works correctly - [ ] All dropdown menus open/close properly - [ ] Toast notifications display correctly - [ ] All forms submit without errors - [ ] All click handlers work as expected - [ ] Alpine directives (`x-show`, `@click`, etc.) work correctly --- ## 🐛 Troubleshooting ### Error: "Cannot read property 'function' of undefined" **Cause**: Template still using old syntax `namespace.function()` **Fix**: Change to `$store.namespace.function()` ### Error: "Alpine.store is not a function" **Cause**: TypeScript build didn't complete or using cached main.js **Fix**: Run `npm run build:ts` and rebuild container ### Error: "p.global is not a function" (still) **Cause**: Some files still have `Alpine.global()` **Fix**: Run `grep -rn "Alpine\.global" web/src` to find remaining occurrences ### Theme switcher still broken **Cause**: `header.templ` not updated with new syntax **Fix**: Update all `@click="header."` to `@click="$store.header."` --- ## 📝 Next Steps (After This Guide) **Part 2** will cover: 1. Advanced Alpine store patterns 2. Reactive state management with stores 3. Testing strategies for all templates 4. Performance optimization 5. Rollback plan if needed --- ## 🔗 References - [Alpine.js Stores Documentation](https://alpinejs.dev/globals/alpine_store.html) - [Alpine.js Magic Properties ($store)](https://alpinejs.dev/magics/$store.html) - [Migration from Alpine v2 to v3](https://alpinejs.dev/upgrade-guide) --- **Created**: 2026-03-09 **Alpine.js Version**: 3.15.8 **Status**: Ready for Implementation