]> Slayer Assistant Repositories - slayer.unlishema.org/.git/commitdiff
Testing out CORS live
authorunlishema <unlishema@jtryba.com>
Thu, 18 Sep 2025 16:20:01 +0000 (12:20 -0400)
committerunlishema <unlishema@jtryba.com>
Thu, 18 Sep 2025 16:20:01 +0000 (12:20 -0400)
dist/.htaccess
dist/dev.html [new file with mode: 0644]
dist/pages/data/version.json
src/.htaccess
src/dev.html [new file with mode: 0644]
src/pages/data/version.json
webpack.config.js

index 85193d9ba48e5c64cc55b74c734ae0d0c2033fc5..bb510dd47bc991ebdcef127f85acfa708681ee13 100644 (file)
@@ -17,34 +17,44 @@ ErrorDocument 502 /pages/error.html?code=502
 ErrorDocument 503 /pages/error.html?code=503
 ErrorDocument 504 /pages/error.html?code=504
 
-# Security Headers for slayer.unlishema.org
+# Prevent Directory Listing
+Options -Indexes
+
+# Deny access to .htaccess and sensitive files
+<FilesMatch "^\.|\.bak|\.config|\.sql|\.fla|\.psd|\.ini|\.log|\.sh|\.inc|\.swp|\.dist|\.old|\.orig$">
+    Require all denied
+</FilesMatch>
+
+# Security and Performance Headers
 <IfModule mod_headers.c>
+    # Unset conflicting headers first
+    Header unset ETag
+    Header unset Cache-Control
+
+    # Security Headers for slayer.unlishema.org
     Header set X-Content-Type-Options "nosniff"
     Header set X-XSS-Protection "1; mode=block"
     Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
     Header set Referrer-Policy "strict-origin-when-cross-origin"
-    Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors http://unlishema.local https://unlishema.org https://*.unlishema.org"
+    Header set Content-Security-Policy "default-src 'self' https://oldschool.runescape.wiki; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://oldschool.runescape.wiki; font-src 'self'; frame-ancestors http://unlishema.local https://unlishema.org https://*.unlishema.org"
     Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
     
     # Set the Host header to ensure requests target slayer.unlishema.org
     Header set Host "slayer.unlishema.org"
-</IfModule>
-
-# Prevent Directory Listing
-Options -Indexes
 
-# Deny access to .htaccess and sensitive files
-<FilesMatch "^\.|\.bak|\.config|\.sql|\.fla|\.psd|\.ini|\.log|\.sh|\.inc|\.swp|\.dist|\.old|\.orig$">
-    Require all denied
-</FilesMatch>
+    # Custom Cache-Control for specific files
+    <Files "images/icon.png">
+        Header set Cache-Control "no-cache, no-store, must-revalidate"
+    </Files>
 
-# Disable ETag for performance
-<IfModule mod_headers.c>
-    Header unset ETag
+    # Leverage Browser Caching for static resources (overridden by the above rule for icon.png)
+    Header set Cache-Control "public, max-age=31536000"
 </IfModule>
+
+# Disable ETag for performance (FileETag None is outside the mod_headers block)
 FileETag None
 
-# Leverage Browser Caching for static resources
+# Leverage Browser Caching for static resources (using mod_expires)
 <IfModule mod_expires.c>
     ExpiresActive On
     ExpiresByType image/jpg "access plus 3 months"
@@ -58,14 +68,4 @@ FileETag None
     ExpiresByType application/javascript "access plus 6 hours"
     ExpiresByType image/x-icon "access plus 1 year"
     ExpiresDefault "access plus 1 day"
-</IfModule>
-
-# Custom Cache-Control for favicon (icon file located at images/icon.png)
-<Files "images/icon.png">
-    Header set Cache-Control "no-cache, no-store, must-revalidate"
-</Files>
-
-# Custom Cache-Control for static files
-<IfModule mod_headers.c>
-    Header set Cache-Control "public, max-age=31536000"
-</IfModule>
+</IfModule>
\ No newline at end of file
diff --git a/dist/dev.html b/dist/dev.html
new file mode 100644 (file)
index 0000000..821f53e
--- /dev/null
@@ -0,0 +1,274 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>OSRS Wiki CORS Test</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 20px;
+            background-color: #f4f4f4;
+            color: #333;
+        }
+
+        h1,
+        h2,
+        h3 {
+            color: #2c3e50;
+        }
+
+        .container {
+            max-width: 800px;
+            margin: auto;
+        }
+
+        .input-section {
+            padding: 20px;
+            border: 1px solid #ccc;
+            border-radius: 8px;
+            margin-bottom: 20px;
+            background-color: #fff;
+        }
+
+        .data-section {
+            padding: 20px;
+            border: 1px solid #ccc;
+            border-radius: 8px;
+            margin-bottom: 20px;
+            background-color: #fff;
+        }
+
+        .raw-data-section {
+            padding: 20px;
+            border: 1px solid #ccc;
+            border-radius: 8px;
+            background-color: #fff;
+        }
+
+        #querySearch {
+            width: 70%;
+            padding: 8px;
+            margin-right: 10px;
+        }
+
+        #querySelect {
+            width: 33%;
+            padding: 8px;
+            margin-top: 10px;
+        }
+
+        #itemSelect {
+            width: 70%;
+            padding: 8px;
+            margin-top: 10px;
+        }
+
+        img {
+            max-width: 100px;
+            height: auto;
+            display: block;
+            margin-top: 10px;
+        }
+
+        pre {
+            white-space: pre-wrap;
+            word-wrap: break-word;
+            background-color: #f8f8f8;
+            padding: 10px;
+            border: 1px solid #ddd;
+            border-radius: 4px;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="container">
+        <h1>OSRS Wiki CORS Test</h1>
+        <p>This page tests CORS with the OSRS wiki and demonstrates fetching data.</p>
+
+        <div class="input-section">
+            <h3>Search a Bucket</h3>
+
+            <label for="querySearch">Enter a bucket API query:</label><br>
+            <input type="text" id="querySearch"
+                value="bucket('infobox_item').select('item_name','item_id','image','examine').run()">
+            <button onclick="fetchAndDisplayData()">Search</button><br>
+            <label for="querySelect">Choose an default bucket:</label><br>
+            <select id="querySelect"></select><br>
+
+            <h3>Select an Item</h3>
+            <label for="itemSelect">Choose an item from query:</label><br>
+            <select id="itemSelect"></select>
+        </div>
+
+        <div class="data-section">
+            <h3>Extracted Item Data</h3>
+            <div id="extractedData">
+                <p>Select an item to display its data here.</p>
+            </div>
+        </div>
+
+        <div class="raw-data-section">
+            <h3>Raw Data</h3>
+            <div id="rawData">
+                <p>Raw JSON data will appear here.</p>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        document.addEventListener("DOMContentLoaded", async () => {
+            // Populate the combobox with some default items
+            const defaultItems = [
+                { name: "Items", query: "bucket('infobox_item').select('item_name','item_id','image','examine').run()" },
+                { name: "Monsters", query: "bucket('infobox_monster').select('name', 'examine').run()" },
+                { name: "Construction", query: "bucket('infobox_construction').select('page_name', 'image').run()" },
+                { name: "Locations", query: "bucket('infobox_location').select('page_name', 'is_members_only').run()" },
+            ];
+
+            const queryBox = document.getElementById("querySelect");
+            const selectBox = document.getElementById("itemSelect");
+
+            // Add default items to the queryBox
+            defaultItems.forEach(item => {
+                const option = document.createElement("option");
+                option.value = item.query;
+                option.textContent = item.name;
+                queryBox.appendChild(option);
+            });
+
+            // Add event listener to the queryBox using an arrow function
+            queryBox.addEventListener("change", (event) => {
+                handleQueryChange(event, true);
+            });
+
+            // Add event listener to the selectBox using an arrow function
+            selectBox.addEventListener("change", (event) => {
+                handleQueryChange(event, false);
+            });
+
+            fetchAndDisplayData(); // Initial fetch with default value
+        });
+
+        // Function to handle changes in the search input
+        function handleQueryChange(event, updateItemList) {
+            const newValue = event.target.value;
+            document.getElementById("querySearch").value = newValue;
+            fetchAndDisplayData(newValue, updateItemList);
+        }
+
+        // Function to extract the bucket name from the query
+        function extractBucketName(query) {
+            // Regex to find content inside bucket(...)
+            const regex = /bucket\('(.+?)'\)/;
+            const match = query.match(regex);
+
+            if (match && match[1]) {
+                return match[1]; // match[1] contains the captured group
+            }
+
+            return ''; // Return an empty string if no match is found
+        }
+
+        // Function to extract keys from the select(...) part of the query
+        function extractSelectKeys(query) {
+            // Regex to find content inside select(...)
+            const regex = /\.select\((.*?)\)/;
+            const match = query.match(regex);
+
+            if (match && match[1]) {
+                // Split the captured string by commas, trim whitespace, and remove quotes
+                return match[1].split(',')
+                    .map(key => key.trim().replace(/^'|'$/g, ''));
+            }
+
+            return []; // Return an empty array if no match is found
+        }
+
+        // Main function to fetch and display data
+        async function fetchAndDisplayData(query, updateItemList = true) {
+            if (!query)
+                query = document.getElementById("querySearch").value;
+            const bucketName = extractBucketName(query);
+            const keys = extractSelectKeys(query);
+
+            const extractedDataElement = document.getElementById("extractedData");
+            const rawDataElement = document.getElementById("rawData");
+
+            extractedDataElement.innerHTML = "<p>Loading data...</p>";
+            rawDataElement.innerHTML = "<p>Loading raw data...</p>";
+
+            const itemData = await requestBucket(query);
+
+            if (itemData) {
+                // Clear previous content
+                extractedDataElement.innerHTML = "";
+
+                for (let i = 0; i < itemData.bucket.length; i++) {
+                    const item = itemData.bucket[i];
+
+                    if (updateItemList && itemData.bucket.length > 1) {
+                        if (i == 0) document.getElementById("itemSelect").innerHTML = "";
+
+                        // Add each item to the combobox
+                        const option = document.createElement("option");
+
+                        // Build the select query dynamically based on keys
+                        option.value = `bucket('${bucketName}').select(`;
+                        for (const key of keys)
+                            option.value += `'${key}',`;
+                        option.value = option.value.slice(0, -1); // Remove trailing comma
+                        option.value += `).where('${keys[0]}', '${item[keys[0]]}').run()`;
+
+                        option.textContent = keys ? item[keys[0]] : "No KEYS";
+                        document.getElementById("itemSelect").appendChild(option);
+                    }
+                    if (i > 0) continue; // Only display the first item by default
+
+                    for (const key of keys)
+                        extractedDataElement.innerHTML += `<p><strong>${key}:</strong> ${item[key] || 'N/A'}</p>`;
+                }
+
+                rawDataElement.innerHTML = `<pre>${JSON.stringify(itemData, null, 2)}</pre>`;
+            } else {
+                extractedDataElement.textContent = "Failed to fetch data from the API.";
+                rawDataElement.textContent = "No raw data available.";
+            }
+        }
+
+        // Function to make a request to the runescape wiki api
+        async function requestBucket(query) {
+            const url = new URL('https://oldschool.runescape.wiki/api.php');
+            const params = {
+                action: 'bucket',
+                format: 'json',
+                query: query,
+                origin: '*' // This parameter is key for CORS
+            };
+            url.search = new URLSearchParams(params).toString();
+
+            try {
+                const response = await fetch(url);
+
+                if (!response.ok) {
+                    throw new Error(`HTTP error! Status: ${response.status}`);
+                }
+
+                const data = await response.json();
+
+                // The Bucket API sometimes returns an error object on failure
+                if (data.error) {
+                    alert(`Query Error: ${query}`)
+                    throw new Error(`API Error: ${data.error.info}`);
+                }
+
+                return data;
+            } catch (error) {
+                console.error('Error fetching data:', error);
+                return null;
+            }
+        }
+    </script>
+</body>
+
+</html>
\ No newline at end of file
index 8aa0c33b236e2ec61ccf3d008d95fc87902b1d64..ef08071e86aee2f86d51b7fe281fb567b1b4425f 100644 (file)
@@ -1,3 +1,3 @@
 {
-  "version": "0.0.31"
+  "version": "0.0.32"
 }
\ No newline at end of file
index 85193d9ba48e5c64cc55b74c734ae0d0c2033fc5..bb510dd47bc991ebdcef127f85acfa708681ee13 100644 (file)
@@ -17,34 +17,44 @@ ErrorDocument 502 /pages/error.html?code=502
 ErrorDocument 503 /pages/error.html?code=503
 ErrorDocument 504 /pages/error.html?code=504
 
-# Security Headers for slayer.unlishema.org
+# Prevent Directory Listing
+Options -Indexes
+
+# Deny access to .htaccess and sensitive files
+<FilesMatch "^\.|\.bak|\.config|\.sql|\.fla|\.psd|\.ini|\.log|\.sh|\.inc|\.swp|\.dist|\.old|\.orig$">
+    Require all denied
+</FilesMatch>
+
+# Security and Performance Headers
 <IfModule mod_headers.c>
+    # Unset conflicting headers first
+    Header unset ETag
+    Header unset Cache-Control
+
+    # Security Headers for slayer.unlishema.org
     Header set X-Content-Type-Options "nosniff"
     Header set X-XSS-Protection "1; mode=block"
     Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
     Header set Referrer-Policy "strict-origin-when-cross-origin"
-    Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors http://unlishema.local https://unlishema.org https://*.unlishema.org"
+    Header set Content-Security-Policy "default-src 'self' https://oldschool.runescape.wiki; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://oldschool.runescape.wiki; font-src 'self'; frame-ancestors http://unlishema.local https://unlishema.org https://*.unlishema.org"
     Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
     
     # Set the Host header to ensure requests target slayer.unlishema.org
     Header set Host "slayer.unlishema.org"
-</IfModule>
-
-# Prevent Directory Listing
-Options -Indexes
 
-# Deny access to .htaccess and sensitive files
-<FilesMatch "^\.|\.bak|\.config|\.sql|\.fla|\.psd|\.ini|\.log|\.sh|\.inc|\.swp|\.dist|\.old|\.orig$">
-    Require all denied
-</FilesMatch>
+    # Custom Cache-Control for specific files
+    <Files "images/icon.png">
+        Header set Cache-Control "no-cache, no-store, must-revalidate"
+    </Files>
 
-# Disable ETag for performance
-<IfModule mod_headers.c>
-    Header unset ETag
+    # Leverage Browser Caching for static resources (overridden by the above rule for icon.png)
+    Header set Cache-Control "public, max-age=31536000"
 </IfModule>
+
+# Disable ETag for performance (FileETag None is outside the mod_headers block)
 FileETag None
 
-# Leverage Browser Caching for static resources
+# Leverage Browser Caching for static resources (using mod_expires)
 <IfModule mod_expires.c>
     ExpiresActive On
     ExpiresByType image/jpg "access plus 3 months"
@@ -58,14 +68,4 @@ FileETag None
     ExpiresByType application/javascript "access plus 6 hours"
     ExpiresByType image/x-icon "access plus 1 year"
     ExpiresDefault "access plus 1 day"
-</IfModule>
-
-# Custom Cache-Control for favicon (icon file located at images/icon.png)
-<Files "images/icon.png">
-    Header set Cache-Control "no-cache, no-store, must-revalidate"
-</Files>
-
-# Custom Cache-Control for static files
-<IfModule mod_headers.c>
-    Header set Cache-Control "public, max-age=31536000"
-</IfModule>
+</IfModule>
\ No newline at end of file
diff --git a/src/dev.html b/src/dev.html
new file mode 100644 (file)
index 0000000..821f53e
--- /dev/null
@@ -0,0 +1,274 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>OSRS Wiki CORS Test</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 20px;
+            background-color: #f4f4f4;
+            color: #333;
+        }
+
+        h1,
+        h2,
+        h3 {
+            color: #2c3e50;
+        }
+
+        .container {
+            max-width: 800px;
+            margin: auto;
+        }
+
+        .input-section {
+            padding: 20px;
+            border: 1px solid #ccc;
+            border-radius: 8px;
+            margin-bottom: 20px;
+            background-color: #fff;
+        }
+
+        .data-section {
+            padding: 20px;
+            border: 1px solid #ccc;
+            border-radius: 8px;
+            margin-bottom: 20px;
+            background-color: #fff;
+        }
+
+        .raw-data-section {
+            padding: 20px;
+            border: 1px solid #ccc;
+            border-radius: 8px;
+            background-color: #fff;
+        }
+
+        #querySearch {
+            width: 70%;
+            padding: 8px;
+            margin-right: 10px;
+        }
+
+        #querySelect {
+            width: 33%;
+            padding: 8px;
+            margin-top: 10px;
+        }
+
+        #itemSelect {
+            width: 70%;
+            padding: 8px;
+            margin-top: 10px;
+        }
+
+        img {
+            max-width: 100px;
+            height: auto;
+            display: block;
+            margin-top: 10px;
+        }
+
+        pre {
+            white-space: pre-wrap;
+            word-wrap: break-word;
+            background-color: #f8f8f8;
+            padding: 10px;
+            border: 1px solid #ddd;
+            border-radius: 4px;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="container">
+        <h1>OSRS Wiki CORS Test</h1>
+        <p>This page tests CORS with the OSRS wiki and demonstrates fetching data.</p>
+
+        <div class="input-section">
+            <h3>Search a Bucket</h3>
+
+            <label for="querySearch">Enter a bucket API query:</label><br>
+            <input type="text" id="querySearch"
+                value="bucket('infobox_item').select('item_name','item_id','image','examine').run()">
+            <button onclick="fetchAndDisplayData()">Search</button><br>
+            <label for="querySelect">Choose an default bucket:</label><br>
+            <select id="querySelect"></select><br>
+
+            <h3>Select an Item</h3>
+            <label for="itemSelect">Choose an item from query:</label><br>
+            <select id="itemSelect"></select>
+        </div>
+
+        <div class="data-section">
+            <h3>Extracted Item Data</h3>
+            <div id="extractedData">
+                <p>Select an item to display its data here.</p>
+            </div>
+        </div>
+
+        <div class="raw-data-section">
+            <h3>Raw Data</h3>
+            <div id="rawData">
+                <p>Raw JSON data will appear here.</p>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        document.addEventListener("DOMContentLoaded", async () => {
+            // Populate the combobox with some default items
+            const defaultItems = [
+                { name: "Items", query: "bucket('infobox_item').select('item_name','item_id','image','examine').run()" },
+                { name: "Monsters", query: "bucket('infobox_monster').select('name', 'examine').run()" },
+                { name: "Construction", query: "bucket('infobox_construction').select('page_name', 'image').run()" },
+                { name: "Locations", query: "bucket('infobox_location').select('page_name', 'is_members_only').run()" },
+            ];
+
+            const queryBox = document.getElementById("querySelect");
+            const selectBox = document.getElementById("itemSelect");
+
+            // Add default items to the queryBox
+            defaultItems.forEach(item => {
+                const option = document.createElement("option");
+                option.value = item.query;
+                option.textContent = item.name;
+                queryBox.appendChild(option);
+            });
+
+            // Add event listener to the queryBox using an arrow function
+            queryBox.addEventListener("change", (event) => {
+                handleQueryChange(event, true);
+            });
+
+            // Add event listener to the selectBox using an arrow function
+            selectBox.addEventListener("change", (event) => {
+                handleQueryChange(event, false);
+            });
+
+            fetchAndDisplayData(); // Initial fetch with default value
+        });
+
+        // Function to handle changes in the search input
+        function handleQueryChange(event, updateItemList) {
+            const newValue = event.target.value;
+            document.getElementById("querySearch").value = newValue;
+            fetchAndDisplayData(newValue, updateItemList);
+        }
+
+        // Function to extract the bucket name from the query
+        function extractBucketName(query) {
+            // Regex to find content inside bucket(...)
+            const regex = /bucket\('(.+?)'\)/;
+            const match = query.match(regex);
+
+            if (match && match[1]) {
+                return match[1]; // match[1] contains the captured group
+            }
+
+            return ''; // Return an empty string if no match is found
+        }
+
+        // Function to extract keys from the select(...) part of the query
+        function extractSelectKeys(query) {
+            // Regex to find content inside select(...)
+            const regex = /\.select\((.*?)\)/;
+            const match = query.match(regex);
+
+            if (match && match[1]) {
+                // Split the captured string by commas, trim whitespace, and remove quotes
+                return match[1].split(',')
+                    .map(key => key.trim().replace(/^'|'$/g, ''));
+            }
+
+            return []; // Return an empty array if no match is found
+        }
+
+        // Main function to fetch and display data
+        async function fetchAndDisplayData(query, updateItemList = true) {
+            if (!query)
+                query = document.getElementById("querySearch").value;
+            const bucketName = extractBucketName(query);
+            const keys = extractSelectKeys(query);
+
+            const extractedDataElement = document.getElementById("extractedData");
+            const rawDataElement = document.getElementById("rawData");
+
+            extractedDataElement.innerHTML = "<p>Loading data...</p>";
+            rawDataElement.innerHTML = "<p>Loading raw data...</p>";
+
+            const itemData = await requestBucket(query);
+
+            if (itemData) {
+                // Clear previous content
+                extractedDataElement.innerHTML = "";
+
+                for (let i = 0; i < itemData.bucket.length; i++) {
+                    const item = itemData.bucket[i];
+
+                    if (updateItemList && itemData.bucket.length > 1) {
+                        if (i == 0) document.getElementById("itemSelect").innerHTML = "";
+
+                        // Add each item to the combobox
+                        const option = document.createElement("option");
+
+                        // Build the select query dynamically based on keys
+                        option.value = `bucket('${bucketName}').select(`;
+                        for (const key of keys)
+                            option.value += `'${key}',`;
+                        option.value = option.value.slice(0, -1); // Remove trailing comma
+                        option.value += `).where('${keys[0]}', '${item[keys[0]]}').run()`;
+
+                        option.textContent = keys ? item[keys[0]] : "No KEYS";
+                        document.getElementById("itemSelect").appendChild(option);
+                    }
+                    if (i > 0) continue; // Only display the first item by default
+
+                    for (const key of keys)
+                        extractedDataElement.innerHTML += `<p><strong>${key}:</strong> ${item[key] || 'N/A'}</p>`;
+                }
+
+                rawDataElement.innerHTML = `<pre>${JSON.stringify(itemData, null, 2)}</pre>`;
+            } else {
+                extractedDataElement.textContent = "Failed to fetch data from the API.";
+                rawDataElement.textContent = "No raw data available.";
+            }
+        }
+
+        // Function to make a request to the runescape wiki api
+        async function requestBucket(query) {
+            const url = new URL('https://oldschool.runescape.wiki/api.php');
+            const params = {
+                action: 'bucket',
+                format: 'json',
+                query: query,
+                origin: '*' // This parameter is key for CORS
+            };
+            url.search = new URLSearchParams(params).toString();
+
+            try {
+                const response = await fetch(url);
+
+                if (!response.ok) {
+                    throw new Error(`HTTP error! Status: ${response.status}`);
+                }
+
+                const data = await response.json();
+
+                // The Bucket API sometimes returns an error object on failure
+                if (data.error) {
+                    alert(`Query Error: ${query}`)
+                    throw new Error(`API Error: ${data.error.info}`);
+                }
+
+                return data;
+            } catch (error) {
+                console.error('Error fetching data:', error);
+                return null;
+            }
+        }
+    </script>
+</body>
+
+</html>
\ No newline at end of file
index 8aa0c33b236e2ec61ccf3d008d95fc87902b1d64..ef08071e86aee2f86d51b7fe281fb567b1b4425f 100644 (file)
@@ -1,3 +1,3 @@
 {
-  "version": "0.0.31"
+  "version": "0.0.32"
 }
\ No newline at end of file
index 5b9442a40dfbff96b21132a3982631a4733c877b..fa6b9ec5680e7d6ccc6391fe17444fae34f15ab4 100644 (file)
@@ -49,6 +49,7 @@ module.exports = {
                 { from: path.resolve(__dirname, 'src/.htaccess'), to: path.resolve(__dirname, 'dist/.htaccess'), toType: 'file' },
                 // Main app files
                 { from: 'index.html', to: 'index.html' },
+                { from: 'dev.html', to: 'dev.html' },
                 { from: 'dev-appconfig.json', to: 'appconfig.json' },
                 // Folders we need
                 { from: 'images', to: 'images', globOptions: { ignore: ['**/data/**'] } },