appifio_app_storage_api___complete_ai_developer_guide:

  critical__this_is_usage_documentation:

    appstorageclient_is_pre_built_and_auto_injected_into_your_application:

    your_role:
      - "✅ **USE** existing methods"
      - "✅ **CALL** `appifio_client.appifio_*()` functions"
      - "❌ **NEVER** define `class AppStorageClient`"
      - "❌ **NEVER** rewrite `appifio_*` methods"

    ---

  global_variables___functions_auto_injected_by_system:

    1__global_configuration_object:

      info: "The system automatically injects this into every page:"

      javascript: |
        window.appifio = {
            global: {
                apps: {
                    url_name: "test-blog",                              // Current app URL name
                    api_url: "https://appifio.com/l/link/app_storage",  // API endpoint
                    site_url: "https://appifio.com/",                   // Base site URL
                    api_key: "encoded_api_key_here"                     // Encoded API key
                }
            }
        }

      usage:
      javascript: |
        // Get URL name
        const urlName = window.appifio.global.apps.url_name;
        
        // Get site URL
        const siteUrl = window.appifio.global.apps.site_url;
        
        // Get API URL
        const apiUrl = window.appifio.global.apps.api_url;

    2__appstorage_client_instance:

      info: "The system creates `appifio_client` as a getter property:"

      javascript: |
        // ✅ CORRECT - appifio_client is always available (never undefined)
        if (appifio_client && appifio_client.appifio_readFile) {
            const result = await appifio_client.appifio_readFile('blog.json');
        }
        
        // ❌ WRONG - Don't check typeof appifio_client === 'undefined'
        // It's a getter property, always exists (returns proxyClient if not ready)

      important: "`appifio_client` may return a proxy client that queues calls until the real client is ready. Always check for method existence, not the variable itself."

    3__global_helper_functions:

      info: "Available from `biolink_handler.php`:"

      javascript: |
        // Wait for AppStorage client to be ready
        await window.waitForAppStorage();
        
        // Callback when AppStorage is ready
        window.onAppStorageReady((client) => {
            console.log('Client ready:', client);
        });
        
        // Get client instance (may return proxy)
        const client = window.getAppStorageClient();
        
        // Get client with callback
        await window.withClient(async (client) => {
            await client.appifio_readFile('blog.json');
        });

      recommended_pattern:
      javascript: |
        // Wait for client to be ready
        try {
            if (typeof window.waitForAppStorage === 'function') {
                await window.waitForAppStorage();
            } else if (typeof window.onAppStorageReady === 'function') {
                await new Promise((resolve) => {
                    window.onAppStorageReady(() => resolve());
                });
            } else {
                // Fallback: wait for appifio_client methods
                let retries = 0;
                while ((!appifio_client || !appifio_client.appifio_readFile) && retries < 50) {
                    await new Promise(resolve => setTimeout(resolve, 100));
                    retries++;
                }
            }
        } catch (error) {
            console.warn('Error waiting for AppStorage:', error);
        }

    ---

  absolute_url_handling:

    critical__base_tag_issue:

      info: "The system uses a `<base>` tag that affects relative URLs. **Always use absolute URLs** for navigation links."

    helper_function__get_absolute_url:

      javascript: |
        function getAbsoluteUrl(path) {
            // Get site URL from global config
            let siteUrl = window.location.origin;
            if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
                siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, ''); // Remove trailing slash
            }
            
            // Get URL name
            let urlName = '';
            if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.url_name) {
                urlName = window.appifio.global.apps.url_name;
            } else {
                // Fallback: extract from current URL
                const pathParts = window.location.pathname.split('/').filter(p => p);
                urlName = pathParts[0] || '';
            }
            
            // Build absolute URL
            return siteUrl + '/' + urlName + '/' + path.replace(/^\//, '');
        }
        
        // Usage examples:
        const homeUrl = getAbsoluteUrl('');           // https://appifio.com/test-blog/
        const blogUrl = getAbsoluteUrl('my-blog');   // https://appifio.com/test-blog/my-blog
        const tagUrl = getAbsoluteUrl('tag/javascript'); // https://appifio.com/test-blog/tag/javascript

    helper_function__get_url_name:

      javascript: |
        function getUrlName() {
            if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.url_name) {
                return window.appifio.global.apps.url_name;
            }
            // Fallback: extract from URL
            const pathParts = window.location.pathname.split('/').filter(p => p);
            return pathParts[0] || '';
        }

    example__back_link_with_absolute_url:

      javascript: |
        function updateBackLink() {
            const backLink = document.getElementById('back-link');
            if (backLink) {
                const urlName = getUrlName();
                let siteUrl = window.location.origin;
                if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
                    siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, '');
                }
                // Use absolute path to avoid base tag issues
                backLink.href = siteUrl + '/' + urlName + '/';
            }
        }

    ---

  critical_concept_1__file_not_found_is_normal:

    understanding_first_time_usage:

      when_reading_a_file_that_doesn_t_exist_yet__the_api_returns__success__false:

      info: "This is **NORMAL** for first-time usage. You must handle this gracefully."

    correct_pattern__always_initialize_with_default_structure:

      javascript: |
        // ✅ CORRECT - Always start with default structure
        async function loadBlogs() {
            // 1. Initialize with default structure FIRST
            let blogIndex = {
                blogs: {},
                metadata: {
                    total: 0,
                    last_updated: ''
                }
            }
            
            // 2. Try to read existing file
            try {
                if (appifio_client && appifio_client.appifio_readFile) {
                    const result = await appifio_client.appifio_readFile('blog.json')
                    
                    // 3. Only use data if read was successful
                    if (result && result.success) {
                        blogIndex = JSON.parse(result.data.content)
                    } else {
                        // File doesn't exist yet - this is OK!
                        console.log('blog.json not found, using empty structure')
                    }
                }
            } catch (error) {
                // Also OK - file doesn't exist
                console.log('blog.json not found:', error.message)
            }
            
            // 4. Continue with blogIndex (empty or loaded)
            return blogIndex
        }

    wrong_pattern__don_t_assume_file_exists:

      javascript: |
        // ❌ WRONG - Will crash if file doesn't exist
        const result = await appifio_client.appifio_readFile('blog.json')
        const blogIndex = JSON.parse(result.data.content) // CRASH if result.success === false!

    ---

  critical_concept_2__routes_are_mandatory:

    understanding_the_route_system:

      important__the_system_only_reads__index_html__by_default:

      - "✅ URL: `/urlname/` → Reads: `index.html` (automatic)"
      - "❌ URL: `/urlname/blog-detail` → **NOTHING** (no file served)"
      - "✅ URL: `/urlname/blog-detail` → Reads: `blog/detail.html` (IF route exists)"

    why_routes_are_required:

      without_routes__only__index_html__works__all_other_urls_return_404:

      info: "To make ANY other page work, you MUST:"
      - "Create the HTML file in filesystem"
      - "Add route mapping to `routes.json`"

    route_system_architecture:

      code_block: |
        User visits: /urlname/my-blog-post
            ↓
        System checks: routes.json
            ↓
        Finds: "my-blog-post" → { "file": "blog/detail.html" }
            ↓
        Serves: blog/detail.html
            ↓
        blog/detail.html reads data from blog.json
            ↓
        Displays blog content

    critical__nested_route_paths:

      route_paths_can_contain_slashes_for_nested_routes:

      javascript: |
        // ✅ CORRECT - Nested routes work
        await appifio_client.appifio_addRoute('tag/javascript', 'blog/tag.html', 'tag');
        await appifio_client.appifio_addRoute('category/tech', 'blog/category.html', 'category');
        
        // URL: /urlname/tag/javascript → Loads blog/tag.html
        // URL: /urlname/category/tech → Loads blog/category.html

      the_routing_system_extracts_the_full_path_after__urlname:

      - "URL: `/test-blog/tag/3123`"
      - "Route path extracted: `tag/3123`"
      - "Matches route in `routes.json`: `\"tag/3123\"`"

    routes_json_structure:

      json: |
        {
          "routes": {
            "my-blog-post": {
              "file": "blog/detail.html",
              "type": "blog",
              "enabled": true
            },
            "tag/javascript": {
              "file": "blog/tag.html",
              "type": "tag",
              "enabled": true
            },
            "category/tech": {
              "file": "blog/category.html",
              "type": "category",
              "enabled": true
            }
          },
          "fallback": "index.html"
        }

    mandatory__always_create_routes:

      every_time_you_create_content__you_must_add_a_route:

      javascript: |
        // ❌ WRONG - No route created, URL won't work
        await appifio_client.appifio_writeFile('blog.json', blogData)
        // User visits /urlname/my-blog → 404 ERROR!
        
        // ✅ CORRECT - Route created, URL works
        await appifio_client.appifio_writeFile('blog.json', blogData)
        await appifio_client.appifio_addRoute('my-blog', 'blog/detail.html', 'blog')
        // User visits /urlname/my-blog → ✅ Works!

    ---

  available_api_methods:

    a__tempdb_storage_operations_data_files:

      info: "Store JSON data files like `blog.json`, `settings.json`:"

      javascript: |
        // Read file
        const result = await appifio_client.appifio_readFile('blog.json')
        if (result && result.success) {
            const data = JSON.parse(result.data.content)
        }
        
        // Write file (creates if doesn't exist, overwrites if exists)
        const saveResult = await appifio_client.appifio_writeFile('blog.json', JSON.stringify(data, null, 2))
        if (!saveResult.success) {
            console.error('Save failed:', saveResult.message)
        }
        
        // Delete file
        await appifio_client.appifio_deleteFile('blog.json')
        
        // List files
        const files = await appifio_client.appifio_listFiles()
        
        // File exists check
        const exists = await appifio_client.appifio_fileExists('blog.json')
        
        // Get file info
        const info = await appifio_client.appifio_getFileInfo('blog.json')

    b__filesystem_operations_html__routes:

      info: "Store HTML files, routes.json in filesystem:"

      javascript: |
        // Read from filesystem
        const result = await appifio_client.appifio_readFsFile('routes.json')
        if (result && result.success) {
            const routes = JSON.parse(result.data.content)
        }
        
        // Write to filesystem
        await appifio_client.appifio_writeFsFile('routes.json', JSON.stringify(routes, null, 2))
        
        // Delete from filesystem
        await appifio_client.appifio_deleteFsFile('file.html')
        
        // List filesystem files
        const fsFiles = await appifio_client.appifio_listFsFiles()
        
        // Get filesystem file info
        const fsInfo = await appifio_client.appifio_getFsFileInfo('routes.json')

    c__route_management_mandatory:

      javascript: |
        // Add route (REQUIRED for every content)
        const routeResult = await appifio_client.appifio_addRoute(
            'my-blog-post',        // Route path (can be nested: 'tag/javascript')
            'blog/detail.html',    // Target HTML file
            'blog'                 // Route type (optional, for categorization)
        )
        
        if (!routeResult.success) {
            console.warn('Failed to add route:', routeResult.message)
        }
        
        // Remove route (MANDATORY when deleting content)
        const removeResult = await appifio_client.appifio_removeRoute('my-blog-post')
        
        // Get all routes
        const routesResult = await appifio_client.appifio_getRoutes()
        if (routesResult.success && routesResult.data) {
            const routes = routesResult.data.routes || {}
            console.log('All routes:', routes)
        }

    d__helper_functions:

      javascript: |
        // Generate slug (supports Vietnamese)
        const slug = appifio_client.appifio_generateSlug("Bài viết về AI")
        // Returns: "bai-viet-ve-ai"
        
        // Format datetime
        const formatted = appifio_client.appifio_formatDateTime('2025-12-27 15:59:10', 'date')
        // Returns: "27 Dec 2025"
        
        // Format file size
        const size = appifio_client.appifio_formatFileSize(1024)
        // Returns: "1 KB"
        
        // Get route slug from current URL
        const currentSlug = appifio_client.appifio_getRouteSlug()
        // Returns: "my-blog-post" for URL /urlname/my-blog-post
        
        // Parse query parameters
        const params = appifio_client.appifio_getQueryParams()
        // Returns: { id: "123", page: "2" } for URL ?id=123&page=2

    e__upload_operations:

      javascript: |
        // Upload image
        const fileInput = document.getElementById('imageInput')
        const uploadResult = await appifio_client.appifio_uploadImage(fileInput.files[0])
        if (uploadResult.success) {
            console.log('Image URL:', uploadResult.data.url)
        }
        
        // Upload file
        const fileResult = await appifio_client.appifio_uploadFile(fileInput.files[0])

    f__auxiliary_operations_advanced:

      info: "These methods are available for more specific file manipulations."

      javascript: |
        // 1. Copy File
        // Copies 'source.json' to 'backup.json'
        const copyResult = await appifio_client.appifio_copyFile('source.json', 'backup.json')
        
        // 2. Rename File
        // Renames 'old_name.json' to 'new_name.json'
        const renameResult = await appifio_client.appifio_renameFile('old_name.json', 'new_name.json')
        
        // 3. Search Files
        // Search for files containing "keyword" in name or content
        const searchResult = await appifio_client.appifio_searchFiles('keyword')
        // Returns array of matching files
        
        // 4. Read Specific Field (JSON Optimization)
        // Only reads the "metadata.total" field from "blog.json" instead of downloading the whole file
        // Useful for large JSON files
        const fieldResult = await appifio_client.appifio_readField('blog.json', 'metadata.total')
        
        // 5. Write Specific Field (JSON Optimization)
        // Updates only "metadata.last_updated" in "blog.json"
        const updateFieldResult = await appifio_client.appifio_writeField('blog.json', 'metadata.last_updated', '2025-12-31')

    ---

  data_structure_pattern__single_index_file:

    critical_concept:

      use_one_file_to_store_all_entries_of_the_same_type:

    example__blog_system_with_tags_and_categories:

      structure:
      code_block: |
        TempDB Storage:
        ├── blog.json              ← ALL blogs stored here
        
        Filesystem:
        ├── routes.json            ← Route mappings (MANDATORY)
        ├── index.html            ← Main page
        └── blog/
            ├── detail.html       ← Blog detail page handler
            ├── tag.html          ← Tag listing page handler
            └── category.html     ← Category listing page handler

      blog_json_content:
      json: |
        {
          "blogs": {
            "1735302550000": {
              "id": "1735302550000",
              "title": "Bài viết về AI",
              "slug": "bai-viet-ve-ai",
              "author": "Admin",
              "content": "<p>Nội dung đầy đủ của bài viết...</p>",
              "excerpt": "Mô tả ngắn gọn...",
              "tags": ["AI", "Technology", "JavaScript"],
              "category": "Công nghệ",
              "created_at": "2025-12-27 15:59:10",
              "updated_at": "2025-12-27 15:59:10"
            }
          },
          "metadata": {
            "total": 1,
            "last_updated": "2025-12-27 15:59:10"
          }
        }

      routes_json_content_mandatory:
      json: |
        {
          "routes": {
            "bai-viet-ve-ai": {
              "file": "blog/detail.html",
              "type": "blog",
              "enabled": true
            },
            "tag/javascript": {
              "file": "blog/tag.html",
              "type": "tag",
              "enabled": true
            },
            "category/cong-nghe": {
              "file": "blog/category.html",
              "type": "category",
              "enabled": true
            }
          },
          "fallback": "index.html"
        }

    ---

  complete_implementation_examples:

    example_1__create_blog_with_tags_and_categories:

      javascript: |
        async function createBlog(title, author, content, tags = [], category = 'Uncategorized') {
            try {
                // STEP 1: Initialize with empty structure
                let blogIndex = {
                    blogs: {},
                    metadata: { total: 0, last_updated: '' }
                }
                
                // STEP 2: Try to read existing file
                try {
                    if (appifio_client && appifio_client.appifio_readFile) {
                        const result = await appifio_client.appifio_readFile('blog.json')
                        if (result && result.success) {
                            blogIndex = JSON.parse(result.data.content)
                        }
                    }
                } catch (error) {
                    console.log('Creating new blog.json')
                }
                
                // STEP 3: Generate ID and slug
                const blogId = Date.now().toString()
                const slug = appifio_client.appifio_generateSlug(title)
                
                // STEP 4: Create excerpt
                const tempDiv = document.createElement('div')
                tempDiv.innerHTML = content
                const textContent = tempDiv.textContent || tempDiv.innerText || ''
                const excerpt = textContent.substring(0, 150) + (textContent.length > 150 ? '...' : '')
                
                // STEP 5: Create blog entry
                const now = new Date().toISOString().slice(0, 19).replace('T', ' ')
                blogIndex.blogs[blogId] = {
                    id: blogId,
                    title: title,
                    slug: slug,
                    author: author,
                    content: content,
                    excerpt: excerpt,
                    tags: tags,
                    category: category,
                    created_at: now,
                    updated_at: now
                }
                
                // STEP 6: Update metadata
                blogIndex.metadata.total = Object.keys(blogIndex.blogs).length
                blogIndex.metadata.last_updated = now
                
                // STEP 7: Save to TempDB
                const saveResult = await appifio_client.appifio_writeFile(
                    'blog.json',
                    JSON.stringify(blogIndex, null, 2)
                )
                
                if (!saveResult.success) {
                    throw new Error('Failed to save: ' + saveResult.message)
                }
                
                // STEP 8: MANDATORY - Add route for blog post
                const routeResult = await appifio_client.appifio_addRoute(
                    slug,
                    'blog/detail.html',
                    'blog'
                )
                
                if (!routeResult.success) {
                    console.warn('Failed to add blog route:', routeResult.message)
                }
                
                // STEP 9: MANDATORY - Ensure routes for tags
                await ensureTagRoutes(tags)
                
                // STEP 10: MANDATORY - Ensure route for category
                await ensureCategoryRoute(category)
                
                console.log('Blog created successfully!')
                return { success: true, blog: blogIndex.blogs[blogId] }
                
            } catch (error) {
                console.error('Error creating blog:', error)
                return { success: false, error: error.message }
            }
        }
        
        // Helper: Ensure tag routes exist
        async function ensureTagRoutes(tags) {
            if (!tags || tags.length === 0) return
            
            if (!appifio_client || !appifio_client.appifio_addRoute) {
                console.warn('AppStorage client not available')
                return
            }
            
            for (const tag of tags) {
                if (!tag || tag.trim() === '') continue
                
                const tagSlug = appifio_client.appifio_generateSlug(tag)
                if (!tagSlug) continue
                
                const tagRoute = 'tag/' + tagSlug
                
                // Check if route already exists
                try {
                    const routesResult = await appifio_client.appifio_getRoutes()
                    if (routesResult.success && routesResult.data) {
                        const routes = routesResult.data.routes || {}
                        if (routes[tagRoute]) {
                            console.log('Tag route already exists:', tagRoute)
                            continue
                        }
                    }
                    
                    // Create route for tag
                    const routeResult = await appifio_client.appifio_addRoute(
                        tagRoute,
                        'blog/tag.html',
                        'tag'
                    )
                    
                    if (routeResult.success) {
                        console.log('Tag route created:', tagRoute)
                    } else {
                        console.warn('Failed to create tag route:', tagRoute, routeResult.message)
                    }
                } catch (error) {
                    console.error('Error creating tag route:', tag, error)
                }
            }
        }
        
        // Helper: Ensure category route exists
        async function ensureCategoryRoute(category) {
            if (!category || category.trim() === '') return
            
            if (!appifio_client || !appifio_client.appifio_addRoute) {
                console.warn('AppStorage client not available')
                return
            }
            
            const categorySlug = appifio_client.appifio_generateSlug(category)
            if (!categorySlug) return
            
            const categoryRoute = 'category/' + categorySlug
            
            // Check if route already exists
            try {
                const routesResult = await appifio_client.appifio_getRoutes()
                if (routesResult.success && routesResult.data) {
                    const routes = routesResult.data.routes || {}
                    if (routes[categoryRoute]) {
                        console.log('Category route already exists:', categoryRoute)
                        return
                    }
                }
                
                // Create route for category
                const routeResult = await appifio_client.appifio_addRoute(
                    categoryRoute,
                    'blog/category.html',
                    'category'
                )
                
                if (routeResult.success) {
                    console.log('Category route created:', categoryRoute)
                } else {
                    console.warn('Failed to create category route:', categoryRoute, routeResult.message)
                }
            } catch (error) {
                console.error('Error creating category route:', category, error)
            }
        }

    example_2__delete_blog_with_route_removal:

      javascript: |
        async function deleteBlog(blogId) {
            if (!confirm('Bạn có chắc muốn xóa bài viết này?')) {
                return
            }
            
            try {
                // STEP 1: Read current data
                if (!appifio_client || !appifio_client.appifio_readFile) {
                    throw new Error('AppStorage client not available')
                }
                
                const result = await appifio_client.appifio_readFile('blog.json')
                
                if (!result || !result.success) {
                    throw new Error('Blog index not found')
                }
                
                const blogIndex = JSON.parse(result.data.content)
                
                // STEP 2: Check if blog exists
                if (!blogIndex.blogs[blogId]) {
                    throw new Error('Blog not found')
                }
                
                // STEP 3: Get slug (for route removal)
                const slug = blogIndex.blogs[blogId].slug
                
                // STEP 4: Remove from index
                delete blogIndex.blogs[blogId]
                
                // STEP 5: Update metadata
                const now = new Date().toISOString().slice(0, 19).replace('T', ' ')
                blogIndex.metadata.total = Object.keys(blogIndex.blogs).length
                blogIndex.metadata.last_updated = now
                
                // STEP 6: Save to TempDB
                const saveResult = await appifio_client.appifio_writeFile(
                    'blog.json',
                    JSON.stringify(blogIndex, null, 2)
                )
                
                if (!saveResult.success) {
                    throw new Error('Failed to save: ' + saveResult.message)
                }
                
                // STEP 7: MANDATORY - Remove route
                if (appifio_client.appifio_removeRoute) {
                    const routeResult = await appifio_client.appifio_removeRoute(slug)
                    
                    if (!routeResult.success) {
                        console.warn('Route removal failed:', routeResult.message)
                    } else {
                        console.log('Route removed:', slug)
                    }
                }
                
                console.log('Blog deleted successfully')
                alert('Đã xóa bài viết thành công')
                
                return { success: true }
                
            } catch (error) {
                console.error('Error deleting blog:', error)
                alert('Lỗi: ' + error.message)
                return { success: false, error: error.message }
            }
        }

    example_3__blog_detail_page_blog_detail_html:

      html: |
        <!DOCTYPE html>
        <html lang="vi">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Blog Detail</title>
            <style>
                body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
                .blog-header { border-bottom: 2px solid #333; padding-bottom: 20px; margin-bottom: 20px; }
                .blog-meta { color: #666; font-size: 0.9em; margin-top: 10px; }
                .blog-content { margin-top: 30px; }
                .blog-tags { margin-top: 30px; }
                .tag { display: inline-block; background: #e0e0e0; padding: 5px 10px; margin-right: 5px; border-radius: 3px; }
                .loading { text-align: center; padding: 50px; }
                .error { color: red; text-align: center; padding: 50px; }
            </style>
        </head>
        <body>
            <div id="loading" class="loading">Đang tải...</div>
            <div id="error" class="error" style="display: none;">Không tìm thấy bài viết</div>
            
            <div id="blog-container" style="display: none;">
                <div class="blog-header">
                    <h1 id="blog-title"></h1>
                    <div class="blog-meta">
                        <span id="blog-author"></span> • 
                        <span id="blog-date"></span>
                    </div>
                </div>
                
                <div class="blog-content" id="blog-content"></div>
                
                <div class="blog-tags" id="blog-tags"></div>
                
                <div style="margin-top: 50px;">
                    <a id="back-link" href="#">← Quay lại trang chủ</a>
                </div>
            </div>
        
            <script>
            // Helper: Get URL name
            function getUrlName() {
                if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.url_name) {
                    return window.appifio.global.apps.url_name;
                }
                const pathParts = window.location.pathname.split('/').filter(p => p);
                return pathParts[0] || '';
            }
            
            // Helper: Update back link with absolute URL
            function updateBackLink() {
                const backLink = document.getElementById('back-link');
                if (backLink) {
                    const urlName = getUrlName();
                    let siteUrl = window.location.origin;
                    if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
                        siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, '');
                    }
                    // Use absolute path to avoid base tag issues
                    backLink.href = siteUrl + '/' + urlName + '/';
                }
            }
            
            // Wait for AppStorage to be ready
            document.addEventListener('DOMContentLoaded', async function() {
                try {
                    updateBackLink();
                    
                    // Wait for AppStorage
                    if (typeof window.waitForAppStorage === 'function') {
                        await window.waitForAppStorage();
                    } else if (typeof window.onAppStorageReady === 'function') {
                        await new Promise((resolve) => {
                            window.onAppStorageReady(() => resolve());
                        });
                    } else {
                        let retries = 0;
                        while ((!appifio_client || !appifio_client.appifio_readFile) && retries < 50) {
                            await new Promise(resolve => setTimeout(resolve, 100));
                            retries++;
                        }
                    }
                    
                    // Get slug from URL
                    // URL format: /urlname/slug
                    const pathParts = window.location.pathname.split('/').filter(p => p);
                    let slug = '';
                    
                    // Skip urlname, get the next part
                    if (pathParts.length >= 2) {
                        const secondPart = pathParts[1];
                        // If not 'tag' or 'category', it's a blog slug
                        if (secondPart !== 'tag' && secondPart !== 'category') {
                            slug = secondPart;
                        }
                    }
                    
                    console.log('Loading blog with slug:', slug);
                    
                    if (!slug) {
                        showError('Không tìm thấy bài viết');
                        return;
                    }
                    
                    // Load blog.json
                    if (!appifio_client || !appifio_client.appifio_readFile) {
                        showError('AppStorage không khả dụng');
                        return;
                    }
                    
                    const result = await appifio_client.appifio_readFile('blog.json');
                    
                    if (!result || !result.success) {
                        showError('Không tìm thấy dữ liệu blog');
                        return;
                    }
                    
                    const blogIndex = JSON.parse(result.data.content);
                    
                    // Find blog by slug
                    const blog = Object.values(blogIndex.blogs).find(b => b.slug === slug);
                    
                    if (!blog) {
                        showError('Không tìm thấy bài viết');
                        return;
                    }
                    
                    // Display blog
                    displayBlog(blog);
                    
                } catch (error) {
                    console.error('Error loading blog:', error);
                    showError('Lỗi: ' + error.message);
                }
            });
            
            function displayBlog(blog) {
                document.getElementById('loading').style.display = 'none';
                document.getElementById('blog-container').style.display = 'block';
                
                document.getElementById('blog-title').textContent = blog.title;
                document.getElementById('blog-author').textContent = 'Tác giả: ' + blog.author;
                
                // Format date
                let dateStr = blog.created_at;
                if (appifio_client && appifio_client.appifio_formatDateTime) {
                    dateStr = appifio_client.appifio_formatDateTime(blog.created_at, 'date');
                }
                document.getElementById('blog-date').textContent = dateStr;
                
                // Content
                document.getElementById('blog-content').innerHTML = blog.content;
                
                // Tags with links
                if (blog.tags && blog.tags.length > 0) {
                    const urlName = getUrlName();
                    let siteUrl = window.location.origin;
                    if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
                        siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, '');
                    }
                    
                    const tagsHtml = blog.tags.map(tag => {
                        const tagSlug = appifio_client.appifio_generateSlug(tag);
                        const tagUrl = siteUrl + '/' + urlName + '/tag/' + tagSlug;
                        return `<a href="${tagUrl}" class="tag">${escapeHtml(tag)}</a>`;
                    }).join('');
                    document.getElementById('blog-tags').innerHTML = 'Tags: ' + tagsHtml;
                }
            }
            
            function showError(message) {
                document.getElementById('loading').style.display = 'none';
                const errorDiv = document.getElementById('error');
                errorDiv.textContent = message;
                errorDiv.style.display = 'block';
            }
            
            function escapeHtml(text) {
                const div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            }
            </script>
        </body>
        </html>

    example_4__tag_listing_page_blog_tag_html:

      html: |
        <!DOCTYPE html>
        <html lang="vi">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Tag: JavaScript</title>
            <style>
                body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
                .blog-item { border-bottom: 1px solid #eee; padding: 20px 0; }
                .blog-item h3 { margin: 0 0 10px 0; }
                .blog-item a { text-decoration: none; color: #333; }
                .blog-meta { color: #666; font-size: 0.9em; }
                .loading { text-align: center; padding: 50px; }
                .error { color: red; text-align: center; padding: 50px; }
            </style>
        </head>
        <body>
            <div id="loading" class="loading">Đang tải...</div>
            <div id="error" class="error" style="display: none;"></div>
            
            <div id="content" style="display: none;">
                <h1 id="tag-title">Bài viết với thẻ</h1>
                <div id="blog-list"></div>
                <div style="margin-top: 30px;">
                    <a id="back-link" href="#">← Quay lại trang chủ</a>
                </div>
            </div>
        
            <script>
            // Helper functions
            function getUrlName() {
                if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.url_name) {
                    return window.appifio.global.apps.url_name;
                }
                const pathParts = window.location.pathname.split('/').filter(p => p);
                return pathParts[0] || '';
            }
            
            function updateBackLink() {
                const backLink = document.getElementById('back-link');
                if (backLink) {
                    const urlName = getUrlName();
                    let siteUrl = window.location.origin;
                    if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
                        siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, '');
                    }
                    backLink.href = siteUrl + '/' + urlName + '/';
                }
            }
            
            function generateSlug(text) {
                if (!text) return '';
                if (appifio_client && appifio_client.appifio_generateSlug) {
                    return appifio_client.appifio_generateSlug(text);
                }
                return text.toLowerCase()
                    .normalize('NFD')
                    .replace(/[\u0300-\u036f]/g, '')
                    .replace(/[^\w\s-]/g, '')
                    .replace(/\s+/g, '-')
                    .replace(/-+/g, '-')
                    .replace(/^-+|-+$/g, '');
            }
            
            function escapeHtml(text) {
                const div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            }
            
            // Main script
            document.addEventListener('DOMContentLoaded', async function() {
                try {
                    updateBackLink();
                    
                    // Get tag slug from URL
                    // URL format: /urlname/tag/tag-slug
                    const pathParts = window.location.pathname.split('/').filter(p => p);
                    let tagSlug = '';
                    
                    const tagIndex = pathParts.indexOf('tag');
                    if (tagIndex !== -1 && pathParts.length > tagIndex + 1) {
                        tagSlug = pathParts[tagIndex + 1];
                    }
                    
                    console.log('Loading blogs with tag slug:', tagSlug);
                    
                    if (!tagSlug) {
                        showError('Không tìm thấy thẻ');
                        return;
                    }
                    
                    // Wait for AppStorage
                    if (typeof window.waitForAppStorage === 'function') {
                        await window.waitForAppStorage();
                    } else if (typeof window.onAppStorageReady === 'function') {
                        await new Promise((resolve) => {
                            window.onAppStorageReady(() => resolve());
                        });
                    } else {
                        let retries = 0;
                        while ((!appifio_client || !appifio_client.appifio_readFile) && retries < 50) {
                            await new Promise(resolve => setTimeout(resolve, 100));
                            retries++;
                        }
                    }
                    
                    await loadTagBlogs(tagSlug);
                    
                } catch (error) {
                    console.error('Error:', error);
                    showError('Lỗi khi tải bài viết');
                }
            });
            
            async function loadTagBlogs(tagSlug) {
                try {
                    let blogIndex = { blogs: {}, metadata: { total: 0 } };
                    
                    if (appifio_client && appifio_client.appifio_readFile) {
                        const result = await appifio_client.appifio_readFile('blog.json');
                        
                        if (result && result.success) {
                            blogIndex = JSON.parse(result.data.content);
                        } else {
                            showError('Không tìm thấy dữ liệu blog');
                            return;
                        }
                    } else {
                        showError('AppStorage không khả dụng');
                        return;
                    }
                    
                    // Filter blogs by tag
                    const tagBlogs = Object.values(blogIndex.blogs).filter(blog => {
                        if (!blog.tags || !Array.isArray(blog.tags)) return false;
                        return blog.tags.some(tag => {
                            const tagSlugNormalized = generateSlug(tag);
                            return tagSlugNormalized === tagSlug;
                        });
                    });
                    
                    if (tagBlogs.length === 0) {
                        showError('Không tìm thấy bài viết nào với thẻ này');
                        return;
                    }
                    
                    // Get tag name from first blog
                    const firstBlog = tagBlogs[0];
                    const tagName = firstBlog.tags.find(tag => {
                        const tagSlugNormalized = generateSlug(tag);
                        return tagSlugNormalized === tagSlug;
                    });
                    
                    // Display blogs
                    displayTagBlogs(tagName, tagBlogs);
                    
                } catch (error) {
                    console.error('Error loading tag blogs:', error);
                    showError('Lỗi: ' + error.message);
                }
            }
            
            function displayTagBlogs(tagName, blogs) {
                document.getElementById('loading').style.display = 'none';
                document.getElementById('content').style.display = 'block';
                
                document.getElementById('tag-title').textContent = 'Bài viết với thẻ: ' + tagName;
                document.title = 'Thẻ: ' + tagName + ' - Blog';
                
                const blogList = document.getElementById('blog-list');
                const urlName = getUrlName();
                let siteUrl = window.location.origin;
                if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
                    siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, '');
                }
                
                blogs.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
                
                let html = '';
                blogs.forEach(blog => {
                    html += `
                        <div class="blog-item">
                            <h3><a href="${siteUrl}/${urlName}/${blog.slug}">${escapeHtml(blog.title)}</a></h3>
                            <div class="blog-meta">
                                Tác giả: ${escapeHtml(blog.author)} • 
                                ${formatDate(blog.created_at)}
                            </div>
                            <div>${escapeHtml(blog.excerpt || '')}</div>
                        </div>
                    `;
                });
                
                blogList.innerHTML = html;
            }
            
            function formatDate(dateString) {
                if (appifio_client && appifio_client.appifio_formatDateTime) {
                    return appifio_client.appifio_formatDateTime(dateString, 'date');
                }
                const date = new Date(dateString);
                return date.toLocaleDateString('vi-VN', {
                    day: '2-digit',
                    month: '2-digit',
                    year: 'numeric'
                });
            }
            
            function showError(message) {
                document.getElementById('loading').style.display = 'none';
                const errorDiv = document.getElementById('error');
                errorDiv.textContent = message;
                errorDiv.style.display = 'block';
            }
            </script>
        </body>
        </html>

    example_5__category_listing_page_blog_category_html:

      info: "Similar to tag.html, but filters by category instead of tags:"

      javascript: |
        // In blog/category.html, extract category slug from URL:
        // URL format: /urlname/category/category-slug
        const pathParts = window.location.pathname.split('/').filter(p => p);
        let categorySlug = '';
        
        const categoryIndex = pathParts.indexOf('category');
        if (categoryIndex !== -1 && pathParts.length > categoryIndex + 1) {
            categorySlug = pathParts[categoryIndex + 1];
        }
        
        // Filter blogs by category:
        const categoryBlogs = Object.values(blogIndex.blogs).filter(blog => {
            if (!blog.category) return false;
            const blogCategorySlug = generateSlug(blog.category);
            return blogCategorySlug === categorySlug;
        });

    ---

  critical_rules:

    always_do:

      - "**Initialize with default structure** before reading files"
      - "**Handle file not found gracefully** - it's normal for first-time"
      - "**Single Index File:** Store all blogs in ONE `blog.json` file"
      - "**Use ID as Key:** Structure as `{ blogs: { \"id\": {...} } }`"
      - "**Full Content:** Store complete blog content in `content` field"
      - "**Generate Slug:** Use `appifio_generateSlug()` for URL-friendly slugs"
      - "**Update Metadata:** Always update `metadata.total` and `metadata.last_updated`"
      - "**MANDATORY Routes:** Create route entry for EVERY blog, tag, and category"
      - "**Nested Routes:** Use `tag/slug` and `category/slug` format for nested routes"
      - "**Create Handler Files:** Blog detail/tag/category HTML must exist in filesystem"
      - "**Absolute URLs:** Always use absolute URLs for navigation (base tag issue)"
      - "**Remove Routes:** Always remove routes when deleting content"
      - "**Error Handling:** Use try-catch for all async operations"
      - "**Check Client:** Always check `appifio_client && appifio_client.methodName` before calling"

    never_do:

      - "❌ Assume file exists without checking"
      - "❌ Create separate files per blog (`blog-slug.json`)"
      - "❌ Store only metadata without full content"
      - "❌ Hardcode IDs"
      - "❌ Forget to update metadata section"
      - "❌ Skip route creation (URLs won't work!)"
      - "❌ Forget to create handler HTML files"
      - "❌ Use relative URLs for navigation (base tag breaks them)"
      - "❌ Forget to remove routes when deleting"
      - "❌ Check `typeof appifio_client === 'undefined'` (it's a getter, always exists)"
      - "❌ Define `class AppStorageClient`"
      - "❌ Rewrite `appifio_*` methods"

    ---

  storage_separation:

    tempdb_app_storage_table:
      purpose: "Store data files"
      use_for: "`blog.json`, `settings.json`, `user-data.json`"
      methods: "`appifio_readFile()`, `appifio_writeFile()`"

    filesystem_sync_data_urlname:
      purpose: "Store HTML files and configuration"
      use_for: "`routes.json`, `blog/detail.html`, `index.html`, `blog/tag.html`, `blog/category.html`"
      methods: "`appifio_readFsFile()`, `appifio_writeFsFile()`"

    ---

  complete_workflow:

    first_time_setup:
      code_block: |
        1. Create blog/detail.html, blog/tag.html, blog/category.html in filesystem
           ↓
        2. User creates first blog
           ↓
        3. blog.json is created (writeFile)
           ↓
        4. Route is added to routes.json for blog post
           ↓
        5. Routes are added for tags and category
           ↓
        6. System can now serve /urlname/blog-slug, /urlname/tag/tag-slug, /urlname/category/category-slug

    creating_a_blog:
      code_block: |
        1. Read blog.json (or use empty structure if not found)
        2. Generate ID (timestamp)
        3. Generate slug from title
        4. Add new entry to blogs object
        5. Update metadata
        6. Save blog.json to TempDB
        7. Add route to routes.json for blog post (MANDATORY)
        8. Ensure routes exist for all tags (tag/tag-slug format)
        9. Ensure route exists for category (category/category-slug format)
        10. User can now visit /urlname/slug, /urlname/tag/tag-slug, /urlname/category/category-slug

    displaying_a_blog:
      code_block: |
        User visits: /urlname/my-blog-slug
            ↓
        System reads: routes.json
            ↓
        Finds: "my-blog-slug" → "blog/detail.html"
            ↓
        Serves: blog/detail.html
            ↓
        blog/detail.html reads: blog.json (TempDB)
            ↓
        Finds blog with slug: "my-blog-slug"
            ↓
        Displays: blog content

    displaying_tag_category:
      code_block: |
        User visits: /urlname/tag/javascript
            ↓
        System reads: routes.json
            ↓
        Finds: "tag/javascript" → "blog/tag.html"
            ↓
        Serves: blog/tag.html
            ↓
        blog/tag.html extracts slug "javascript" from URL
            ↓
        Reads: blog.json (TempDB)
            ↓
        Filters blogs by tag slug
            ↓
        Displays: filtered blog list

    ---

  debugging_tips:

    if_blog_json_not_found:
      javascript: |
        // ✅ This is NORMAL for first time
        console.log('blog.json not found - will create on first save')

    if_route_doesn_t_work:
      javascript: |
        // Check if route exists
        const routes = await appifio_client.appifio_getRoutes()
        console.log('All routes:', routes.data.routes)
        
        // Check specific route
        if (!routes.data.routes['my-slug']) {
            console.error('Route missing! Add it:')
            await appifio_client.appifio_addRoute('my-slug', 'blog/detail.html', 'blog')
        }

    if_nested_route_doesn_t_work:
      javascript: |
        // Check nested route
        const routes = await appifio_client.appifio_getRoutes()
        if (!routes.data.routes['tag/javascript']) {
            console.error('Nested route missing! Add it:')
            await appifio_client.appifio_addRoute('tag/javascript', 'blog/tag.html', 'tag')
        }

    if_handler_file_doesn_t_exist:
      javascript: |
        // Check if file exists
        const result = await appifio_client.appifio_readFsFile('blog/detail.html')
        if (!result.success) {
            console.error('Handler file missing! Create it:')
            await appifio_client.appifio_writeFsFile('blog/detail.html', htmlContent)
        }

    if_navigation_links_broken:
      javascript: |
        // Always use absolute URLs
        const urlName = getUrlName();
        let siteUrl = window.location.origin;
        if (window.appifio && window.appifio.global && window.appifio.global.apps && window.appifio.global.apps.site_url) {
            siteUrl = window.appifio.global.apps.site_url.replace(/\/$/, '');
        }
        const absoluteUrl = siteUrl + '/' + urlName + '/path';

    ---

  final_reminder:

    info: "You are building an APPLICATION that USES a pre-built API."

    info: "The API library is already written and injected."
    your_job: "Write business logic using the API."
    focus: "User interface, data flow, user experience."
    don_t_waste_time: "Reimplementing what already exists."

    key_points:

      - "File not found is NORMAL for first-time usage"
      - "Routes are MANDATORY for all non-index URLs"
      - "Nested routes work: `tag/slug`, `category/slug`"
      - "Handler HTML files MUST exist in filesystem"
      - "Always initialize with default structure"
      - "Always add routes when creating content"
      - "Always remove routes when deleting content"
      - "Always use absolute URLs for navigation"
      - "Always check `appifio_client && appifio_client.methodName` before calling"

      info: "Build great applications with the tools provided!"

