diff --git a/packages/polymech/app-config.json b/packages/polymech/app-config.json new file mode 100644 index 0000000..28069ab --- /dev/null +++ b/packages/polymech/app-config.json @@ -0,0 +1,250 @@ +{ + "site": { + "title": "Polymech Library", + "base_url": "https://library.polymech.info/", + "description": "", + "base_path": "/", + "trailing_slash": false, + "favicon": "/images/favicon.png", + "logo": "/images/logo.png", + "logo_darkmode": "/images/logo-darkmode.png", + "logo_width": "150", + "logo_height": "33", + "logo_text": "Polymech Library", + "image": { + "default": "/images/default-image.png", + "error": "/images/error-image.png", + "alt": "Polymech Library" + } + }, + "footer_left": [ + { + "href": "/${LANG}/resources/info/contact", + "text": "Contact" + }, + { + "href": "https://forum.polymech.info/", + "text": "Forum" + }, + { + "href": "https://files.polymech.info/", + "text": "Files" + }, + { + "href": "https://git.polymech.info/explore/repos", + "text": "Github" + } + ], + "footer_right": [], + "settings": { + "search": true, + "account": true, + "sticky_header": true, + "theme_switcher": true, + "default_theme": "system" + }, + "params": { + "contact_form_action": "#", + "copyright": "Designed And Developed by [Themefisher](https://themefisher.com/)" + }, + "navigation": { + "top": [ + { + "href": "/${LANG}", + "text": "Home" + }, + { + "href": "/${LANG}/resources", + "text": "Resources" + }, + { + "href": "/${LANG}/library", + "text": "Library" + }, + { + "href": "/${LANG}/howtos", + "text": "Tutorials" + }, + { + "href": "https://service.polymech.info/", + "text": "Media" + }, + { + "href": "https://forum.polymech.info/", + "text": "Forum" + }, + { + "href": "/${LANG}/resources/info/contact", + "text": "Contact" + } + ] + }, + "navigation_button": { + "enable": true, + "label": "Get Started", + "link": "https://github.com/themefisher/astrofront" + }, + "ecommerce": { + "brand": "Polymech", + "currencySymbol": "", + "currencyCode": "EU" + }, + "metadata": { + "country": "Spain", + "city": "Barcelona", + "author": "Polymech", + "author_bio": "I am in, if its true", + "author_url": "https://polymech.info/", + "image": "/images/og-image.png", + "description": "Polymech is a plastic prototyping company that offers product design services.", + "keywords": "Plastic, Prototyping, Product Design, Opensource" + }, + "shopify": { + "currencySymbol": "", + "currencyCode": "EU", + "collections": { + "hero_slider": "hidden-homepage-carousel", + "featured_products": "featured-products" + } + }, + "pages": { + "home": { + "hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg", + "_blog": { + "store": "resources" + } + } + }, + "core": { + "logging_namespace": "polymech-site", + "translate_content": true, + "languages": [ + "en", + "ar", + "de", + "ja", + "es", + "zh", + "fr" + ], + "languages_prod": [ + "en", + "es", + "fr" + ], + "rtl_languages": [ + "ar" + ], + "osr_root": "${OSR_ROOT}" + }, + "dev": { + "file_server": "localhost:5000" + }, + "i18n": { + "store": "${OSR_ROOT}/i18n-store/store-${LANG}.json", + "cache": true, + "asset_path": "${SRC_DIR}/${SRC_NAME}-${DST_LANG}${SRC_EXT}" + }, + "products": { + "root": "${OSR_ROOT}/products", + "howto_migration": "./data/last.json", + "glob": "**/config.json", + "enabled": "${OSR_ROOT}/products/config/machines.json" + }, + "retail": { + "library_branch": "site-prod", + "projects_branch": "projects" + }, + "rss": { + "title": "Polymech RSS Feed", + "description": "" + }, + "osrl": { + "env": "astro-release", + "env_dev": "astro-debug", + "module_name": "polymech.io", + "lang_flavor": "osr", + "product_profile": "./src/app/profile.json" + }, + "features": { + "show_description": false, + "show_license": true, + "show_renderings": true, + "show_tabs": false, + "show_gallery": true, + "show_files": true, + "show_specs": true, + "show_checkout": true, + "show_contact": true, + "show_3d_preview": true, + "show_resources": true, + "show_debug": false, + "show_samples": true, + "show_readme": false, + "show_related": true, + "show_showcase": true, + "show_screenshots": true + }, + "defaults": { + "image_url": "https://picsum.photos/640/640", + "license": "CERN Open Source Hardware License", + "contact": "sales@plastic-hub.com" + }, + "cad": { + "cache": true, + "export_configurations": true, + "export_sub_components": true, + "renderer": "solidworks", + "renderer_view": "Render", + "renderer_quality": 1, + "extensions": [ + ".STEP", + ".html" + ], + "model_ext": ".tree.json", + "default_configuration": "Default", + "main_match": "${product}/cad*/*Global*.+(SLDASM)", + "cam_main_match": "${product}/cad*/*-CNC*.+(SLDASM)" + }, + "assets": { + "cad_url": "${OSR_MACHINES_ASSETS_URL}/${file}", + "url": "${OSR_MACHINES_ASSETS_URL}/products/${product_rel_min}/${file}", + "item_url_r": "${OSR_MACHINES_ASSETS_URL}/${ITEM_REL}/${assetPath}/${filePath}", + "item_url": "http://${FILE_SERVER_DEV}/${ITEM_REL}/${assetPath}/${filePath}" + }, + "optimization": { + "image_settings": { + "gallery": { + "show_title": true, + "show_description": false, + "sizes_thumb": "(min-width: 120px) 120px, 120vw", + "sizes_large": "(min-width: 1024px) 1024px, 1024vw", + "sizes_regular": "(min-width: 400px) 400px, 400vw" + }, + "lightbox": { + "show_title": true, + "show_description": true, + "sizes_thumb": "(min-width: 120px) 120px, 120vw", + "sizes_large": "(min-width: 1024px) 1024px, 1024vw", + "sizes_regular": "(min-width: 400px) 400px, 400vw" + } + }, + "presets": { + "slow": { + "sizes_medium": "(min-width: 100px) 100px, 100vw", + "sizes_thumbs": "(min-width: 80px) 80px, 80vw", + "sizes_large": "(min-width: 320px) 320px, 320vw" + }, + "medium": { + "sizes_medium": "(min-width: 400px) 400px, 400vw", + "sizes_thumbs": "(min-width: 120px) 120px, 120vw", + "sizes_large": "(min-width: 1024px) 1024px, 1024vw" + }, + "fast": { + "sizes_medium": "(min-width: 1024px) 1024px, 1024vw", + "sizes_thumbs": "(min-width: 180px) 180px, 180vw", + "sizes_large": "(min-width: 1200px) 1200px, 1200vw" + } + } + } +} \ No newline at end of file diff --git a/packages/polymech/app-config.local.json b/packages/polymech/app-config.local.json new file mode 100644 index 0000000..c349505 --- /dev/null +++ b/packages/polymech/app-config.local.json @@ -0,0 +1,5 @@ +{ + "core": { + "logging_namespace": "LOCAL_CONFIG_OVERRIDE" + } +} \ No newline at end of file diff --git a/packages/polymech/scripts/.gitignore b/packages/polymech/scripts/.gitignore new file mode 100644 index 0000000..723b3a3 --- /dev/null +++ b/packages/polymech/scripts/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +config.json +.kbot diff --git a/packages/polymech/scripts/README.md b/packages/polymech/scripts/README.md new file mode 100644 index 0000000..27f4d86 --- /dev/null +++ b/packages/polymech/scripts/README.md @@ -0,0 +1,43 @@ +# File Deployment Script + +A simple deployment script that uses Node-SSH to: + +1. Upload a file from `./releases/dist.zip` to `/var/vhosts/polymech.io/httpdocs` +2. Unzip the file on the server + +## Prerequisites + +- Node.js +- NPM + +## Installation + +```bash +npm install +``` + +## Configuration + +Edit the `config.json` file with your server credentials: + +```json +{ + "server": { + "host": "your-server-host", + "username": "your-username", + "password": "your-password", + "port": 22 + } +} +``` + +## Usage + +```bash +npm run deploy +``` + +## Note + +For security, `config.json` is included in `.gitignore` to prevent sensitive credentials from being committed to version control. + diff --git a/packages/polymech/scripts/build.sh b/packages/polymech/scripts/build.sh new file mode 100644 index 0000000..87cca69 --- /dev/null +++ b/packages/polymech/scripts/build.sh @@ -0,0 +1,6 @@ +npm run build +./scripts/deploy.sh +#Shutdown.exe -s -t 00 +#sh scripts/sync.sh +#sh scripts/zip.sh + diff --git a/packages/polymech/scripts/commit.sh b/packages/polymech/scripts/commit.sh new file mode 100644 index 0000000..2e3f37f --- /dev/null +++ b/packages/polymech/scripts/commit.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Define repositories to commit to +# . refers to the current directory (polymech/site2) +# ../polymech-astro refers to the sibling directory +REPOS=("." "../polymech-astro") + +# Store the optional commit message +MSG="$1" + +# Iterate over each repository +for repo in "${REPOS[@]}"; do + echo "--------------------------------------------------" + echo "Processing repository: $repo" + echo "--------------------------------------------------" + + # Execute in a subshell to preserve current directory + ( + cd "$repo" || { echo "Failed to enter $repo"; exit 1; } + + # Add all changes + git add -A . + + # Commit + if [ -n "$MSG" ]; then + git commit -m "$MSG" + else + # If no message provided, let git open the editor + git commit + fi + + # Pushing changes + git push + ) +done diff --git a/packages/polymech/scripts/deploy.js b/packages/polymech/scripts/deploy.js new file mode 100644 index 0000000..0ddea81 --- /dev/null +++ b/packages/polymech/scripts/deploy.js @@ -0,0 +1,59 @@ +/** + * Deployment Script + * Uploads dist.zip to server and unzips it + */ + +const { NodeSSH } = require('node-ssh'); +const fs = require('fs'); +const path = require('path'); + +// Load credentials from config.json +const config = require('./config.json'); + +// Paths +const localFilePath = './releases/dist.zip'; +const remoteDirectory = '/var/www/vhosts/polymech.io/httpdocs'; +const remoteFilePath = `${remoteDirectory}/dist.zip`; + +const ssh = new NodeSSH(); + +/** + * Main deployment function + */ +async function deploy() { + try { + // Check if the local file exists + if (!fs.existsSync(localFilePath)) { + throw new Error(`File ${localFilePath} not found`); + } + + console.log('Connecting to server...'); + await ssh.connect({ + host: config.server.host, + username: config.server.username, + password: config.server.password, + port: config.server.port + }); + + // Upload the file + console.log(`Uploading ${localFilePath} to ${remoteFilePath}...`); + await ssh.putFile(localFilePath, remoteFilePath); + + // Unzip the file + console.log(`Unzipping file on server...`); + await ssh.execCommand(`unzip -o ${remoteFilePath} -d ${remoteDirectory}`); + + // Optional: Clean up the zip file after extraction + console.log(`Removing zip file...`); + await ssh.execCommand(`rm ${remoteFilePath}`); + + console.log('Deployment completed successfully!'); + } catch (error) { + console.error('Deployment failed:', error); + } finally { + // Close the connection + ssh.dispose(); + } +} + +deploy(); diff --git a/packages/polymech/scripts/deploy.sh b/packages/polymech/scripts/deploy.sh new file mode 100644 index 0000000..eb0f751 --- /dev/null +++ b/packages/polymech/scripts/deploy.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Example script for Google Drive operations using rclone + +# Define local and remote paths +LOCAL_DIR="./dist" +GDRIVE_DIR="creava:/httpdocs" + +rclone copy "${LOCAL_DIR}" "${GDRIVE_DIR}" --progress --transfers 4 diff --git a/packages/polymech/scripts/generate-app-config.ts b/packages/polymech/scripts/generate-app-config.ts new file mode 100644 index 0000000..fe8c9eb --- /dev/null +++ b/packages/polymech/scripts/generate-app-config.ts @@ -0,0 +1,64 @@ + +import fs from 'fs'; +import path from 'path'; +import { quicktype, InputData, jsonInputForTargetLanguage } from 'quicktype-core'; + +const CONFIG_PATH = path.resolve('./app-config.json'); +const OUTPUT_SCHEMA_PATH = path.resolve('./src/app/config.schema.ts'); +const OUTPUT_DTS_PATH = path.resolve('./src/app/config.d.ts'); + +async function main() { + console.log(`Reading config from ${CONFIG_PATH}...`); + const configContent = fs.readFileSync(CONFIG_PATH, 'utf8'); + + // 1. Generate TypeScript Definitions (d.ts) FIRST + console.log('Generating TypeScript definitions...'); + + const tsInput = jsonInputForTargetLanguage("ts"); + await tsInput.addSource({ + name: "AppConfig", + samples: [configContent] + }); + + const tsInputData = new InputData(); + tsInputData.addInput(tsInput); + + const tsResult = await quicktype({ + inputData: tsInputData, + lang: "ts", + rendererOptions: { + "just-types": "true", + "acronym-style": "original" + } + }); + + const tsCode = tsResult.lines.join('\n'); + fs.writeFileSync(OUTPUT_DTS_PATH, tsCode); + console.log(`Wrote TypeScript definitions to ${OUTPUT_DTS_PATH}`); + + // 2. Generate Zod Schema from Types using ts-to-zod + console.log('Generating Zod schema from types...'); + + try { + const { execSync } = await import('child_process'); + // ts-to-zod + // Use relative paths to avoid Windows path concatenation issues with ts-to-zod + const relDts = path.relative(process.cwd(), OUTPUT_DTS_PATH); + const relSchema = path.relative(process.cwd(), OUTPUT_SCHEMA_PATH); + + execSync(`npx ts-to-zod "${relDts}" "${relSchema}"`, { stdio: 'inherit', cwd: process.cwd() }); + + // Append export type AppConfig + fs.appendFileSync(OUTPUT_SCHEMA_PATH, `\nexport type AppConfig = z.infer;\n`); + + console.log(`Wrote Zod schema to ${OUTPUT_SCHEMA_PATH}`); + } catch (error) { + console.error('Failed to generate Zod schema:', error); + throw error; + } +} + +main().catch(err => { + console.error('Error fetching/generating config:', err); + process.exit(1); +}); diff --git a/packages/polymech/scripts/package.json b/packages/polymech/scripts/package.json new file mode 100644 index 0000000..9664487 --- /dev/null +++ b/packages/polymech/scripts/package.json @@ -0,0 +1,20 @@ +{ + "name": "file-deployment", + "version": "1.0.0", + "description": "Simple deployment script using Node-SSH", + "main": "deploy.js", + "type": "commonjs", + "scripts": { + "deploy": "node deploy.js" + }, + "keywords": [ + "deployment" + ], + "author": "Guenter", + "license": "ISC", + "dependencies": { + "@plastichub/fs": "file:../../../osr/osr-mono/packages/fs", + "@plastichub/osr-commons": "file:../../../osr/osr-mono/packages/osr-commons", + "node-ssh": "^13.2.0" + } +} diff --git a/packages/polymech/scripts/profile-build.js b/packages/polymech/scripts/profile-build.js new file mode 100644 index 0000000..c686d2b --- /dev/null +++ b/packages/polymech/scripts/profile-build.js @@ -0,0 +1,172 @@ +const { spawnSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Create temp directory if it doesn't exist +const tempDir = path.join(process.cwd(), 'temp'); +if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); +} + +function profileBuild() { + console.log('Starting build profiling...\n'); + + const startTime = new Date(); + const timestamp = startTime.getTime(); + + // Run the build command with profiling flags + const buildResult = spawnSync('npm', [ + 'run', + 'build', + '--', + '--node-options=--cpu-prof --heap-prof --trace-gc --no-logfile-per-isolate --log-internal-timer-events --log-timer-events --prof' + ], { + stdio: ['inherit', 'pipe', 'pipe'], + encoding: 'utf8', + shell: true, + env: { ...process.env, FORCE_COLOR: '1' } + }); + + const endTime = new Date(); + const duration = (endTime - startTime) / 1000; + + // Process profiling data + try { + // Find and process the CPU profile + const cpuProfiles = fs.readdirSync('.').filter(f => f.endsWith('.cpuprofile')); + if (cpuProfiles.length > 0) { + const profileFile = path.join(tempDir, 'cpu-last.cpuprofile'); + fs.renameSync(cpuProfiles[0], profileFile); + console.log('CPU profile saved:', profileFile); + } else { + console.log('No CPU profile found'); + } + + // Find and process heap snapshots + const heapFiles = fs.readdirSync('.').filter(f => f.endsWith('.heapprofile')); + if (heapFiles.length > 0) { + const heapFile = path.join(tempDir, 'heap-last.heapprofile'); + fs.renameSync(heapFiles[0], heapFile); + console.log('Heap snapshot saved:', heapFile); + } else { + console.log('No heap snapshot found'); + } + + // Find and process v8.log + const v8Logs = fs.readdirSync('.').filter(f => f.startsWith('v8.log')); + if (v8Logs.length > 0) { + const logFile = path.join(tempDir, 'v8-last.log'); + fs.renameSync(v8Logs[0], logFile); + console.log('V8 log saved:', logFile); + } else { + console.log('No V8 log found'); + } + + // Generate markdown report + const reportContent = generateMarkdownReport({ + startTime, + endTime, + duration, + output: buildResult.stdout || '', + error: buildResult.stderr || '', + status: buildResult.status === 0 ? 'Success' : 'Failed', + cpuProfiles: cpuProfiles.length > 0 ? ['cpu-last.cpuprofile'] : [], + heapFiles: heapFiles.length > 0 ? ['heap-last.heapprofile'] : [], + v8Logs: v8Logs.length > 0 ? ['v8-last.log'] : [] + }); + + fs.writeFileSync(path.join(tempDir, 'report-build.md'), reportContent); + + console.log('\nBuild profiling completed.'); + console.log(`Profile data saved in: ${tempDir}`); + console.log(`Markdown report saved at: ${path.join(tempDir, 'report-build.md')}\n`); + + if (buildResult.status !== 0) { + console.error('Build failed with error:'); + console.error(buildResult.stderr); + process.exit(1); + } + } catch (err) { + console.error('Error processing profile data:', err); + process.exit(1); + } +} + +function generateMarkdownReport({ startTime, endTime, duration, output, error, status, cpuProfiles, heapFiles, v8Logs }) { + const durationMinutes = Math.floor(duration / 60); + const durationSeconds = (duration % 60).toFixed(2); + + return `# Astro Build Performance Report + +## Build Information + +- **Start Time:** ${startTime.toLocaleString()} +- **End Time:** ${endTime.toLocaleString()} +- **Duration:** ${durationMinutes}m ${durationSeconds}s +- **Build Status:** ${status === 'Success' ? '✅ Success' : '❌ Failed'} + +## Profile Data + +${cpuProfiles.length > 0 ? `### CPU Profiles +CPU profiles are available in: +${cpuProfiles.map(file => `- \`${file}\``).join('\n')} + +To analyze the CPU profiles: +1. Open Chrome DevTools +2. Go to the Performance tab +3. Click "Load profile..." +4. Select a CPU profile file from the temp directory + +` : ''} + +${heapFiles.length > 0 ? `### Heap Snapshots +Heap snapshots are available in: +${heapFiles.map(file => `- \`${file}\``).join('\n')} + +To analyze the heap snapshots: +1. Open Chrome DevTools +2. Go to the Memory tab +3. Click "Load..." +4. Select a heap snapshot file from the temp directory + +` : ''} + +${v8Logs.length > 0 ? `### V8 Logs +V8 execution logs are available in: +${v8Logs.map(file => `- \`${file}\``).join('\n')} + +To analyze the V8 logs: +1. Use Node.js's built-in profiler tools +2. Run \`node --prof-process \` to process the logs +3. Review the generated report for execution statistics + +` : ''} + +## Build Output + +\`\`\` +${output} +\`\`\` + +${error ? `## Build Errors\n\`\`\`\n${error}\n\`\`\`\n` : ''} + +## Recommendations + +1. **Build Process Optimization** + - Enable parallel processing where available + - Implement build caching + - Consider using bundling for frequently used modules + +2. **Resource Management** + - Review and optimize memory-intensive operations + - Consider implementing lazy loading for large modules + - Monitor and optimize garbage collection patterns + +3. **Performance Monitoring** + - Set up continuous performance monitoring + - Track build times over time + - Monitor resource usage during builds +`; +} + +profileBuild(); \ No newline at end of file diff --git a/packages/polymech/scripts/serve-products.ts b/packages/polymech/scripts/serve-products.ts new file mode 100644 index 0000000..8741e96 --- /dev/null +++ b/packages/polymech/scripts/serve-products.ts @@ -0,0 +1,25 @@ +import handler from 'serve-handler'; +import http from 'http'; +import { PRODUCT_ROOT, FILE_SERVER_DEV } from '../src/app/config'; + +// Parse host and port from FILE_SERVER_DEV +const [host, portStr] = FILE_SERVER_DEV.split(':'); +const port = parseInt(portStr, 10); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const server = http.createServer((request: any, response: any) => { + return handler(request, response, { + public: PRODUCT_ROOT(), + headers: [ + { + source: '**/*', + headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }] + } + ] + }); +}); + +server.listen(port, host, () => { + console.log(`Running at http://${host}:${port}`); + console.log(`Serving files from: ${PRODUCT_ROOT()}`); +}); diff --git a/packages/polymech/scripts/sync-creava.sh b/packages/polymech/scripts/sync-creava.sh new file mode 100644 index 0000000..0139935 --- /dev/null +++ b/packages/polymech/scripts/sync-creava.sh @@ -0,0 +1,8 @@ +echo "Starting sync from ./dist to creava:/httpdocs..." + +rclone sync ./dist creava:/httpdocs \ + --progress \ + --stats-one-line \ + -v + +echo "Sync completed!" diff --git a/packages/polymech/scripts/sync.sh b/packages/polymech/scripts/sync.sh new file mode 100644 index 0000000..3263610 --- /dev/null +++ b/packages/polymech/scripts/sync.sh @@ -0,0 +1,11 @@ +osr-sync sync --logLevel=debug --clear=false --source='.' --target='../astro-playground' --debug=false --profile="./sync-playground.json" +cd ../astro-playground +git add -A . +git commit -m "Synced from site" +git push + +# osr-sync sync --logLevel=debug --clear=false --source='.' --target='../guides' --debug=false --profile="./sync-playground.json" +#cd ../guides +#git add . +#git commit -m "Synced from site" +#git push diff --git a/packages/polymech/scripts/test-config-loader.ts b/packages/polymech/scripts/test-config-loader.ts new file mode 100644 index 0000000..1134f48 --- /dev/null +++ b/packages/polymech/scripts/test-config-loader.ts @@ -0,0 +1,11 @@ + +import { loadConfig } from '../src/app/config-loader.js'; +import { I18N_SOURCE_LANGUAGE } from "../src/app/constants.js"; + +try { + const config = loadConfig(I18N_SOURCE_LANGUAGE); + console.log('LOGGING_NAMESPACE:', config.core.logging_namespace); + console.log('SHOW_GALLERY:', config.features.show_gallery); +} catch (e) { + console.error(e); +} diff --git a/packages/polymech/scripts/todos-merchant.md b/packages/polymech/scripts/todos-merchant.md new file mode 100644 index 0000000..13f9e86 --- /dev/null +++ b/packages/polymech/scripts/todos-merchant.md @@ -0,0 +1,17 @@ +## Todos + +skip checked todos + +- platform : windows +- environment : Astro (5.3+) +- language: Typescript, ESM +- dont use classes +- no lazy imports +- use pure functions +- each handler has the same function signature and naming + +- [ ] create a registry of handlers, transforming json data to platform specific formats, in ../src/model/registry.ts, the input schema is provided (see ../polymech-mono/packages/commons/src/component.ts), create a new file per type, eg: ../src/model/rss.ts + - [ ] "rss" (@astrojs/rss) + - [ ] "merchant" (google) + - [ ] "json-ld" (products) +- [ ] evaluate the registry on demand diff --git a/packages/polymech/scripts/todos.md b/packages/polymech/scripts/todos.md new file mode 100644 index 0000000..ded2ba6 --- /dev/null +++ b/packages/polymech/scripts/todos.md @@ -0,0 +1,8 @@ +## Todos + +skip checked todos + +- [ ] create javascript script, using Node-SSH : + - [ ] upload a file from "./releases/dist.zip", to /var/vhosts/polymech.io/httpdocs + - [ ] unzip the file on the server + - [ ] assume credentials from a json file diff --git a/packages/polymech/scripts/todos.sh b/packages/polymech/scripts/todos.sh new file mode 100644 index 0000000..0ab42b8 --- /dev/null +++ b/packages/polymech/scripts/todos.sh @@ -0,0 +1,13 @@ +#kbotd --prompt=./Gallery.md --mode=completion --dst=./GalleryK.astro --filters=code --router=openai --model=gpt-4o +#kbotd --prompt=./Gallery.md --mode=completion --dst=./GalleryK2.astro --filters=code --include=./GalleryS.astro --router=openai --model=gpt-4o +#kbotd --prompt=./Lightbox.md --mode=completion --dst=./GalleryL.astro --filters=code --include=./GalleryM.astro --include=./lightbox.html --router=openai --model=gpt-4o +#kbotd --prompt=./todos.md --mode=completion --dst=./GalleryL2.astro --filters=code --include=./GalleryL.astro --model=openai/o1 + +#kbotd --prompt=./todos.md --include=resources.astro --filters=code +#kbotd --prompt=./todos.md --model=anthropic/claude-3.7-sonnet +# kbotd --prompt=./todos-merchant.md --model=anthropic/claude-3.7-sonnet --include=../../polymech-mono/packages/commons/src/component.ts +#--model=anthropic/claude-3.7-sonnet \ +kbotd --preferences ./todos-merchant.md \ + --include=../../polymech-mono/packages/commons/src/component.ts \ + --disable=terminal,git,npm,user,interact,search,email,web \ + --disableTools=read_file,read_files,list_files,file_exists,web diff --git a/packages/polymech/scripts/todos.ts b/packages/polymech/scripts/todos.ts new file mode 100644 index 0000000..b452237 --- /dev/null +++ b/packages/polymech/scripts/todos.ts @@ -0,0 +1,37 @@ +// src/integrations/build-logger.ts +import type { AstroIntegration } from 'astro'; + +export default function buildLogger(): AstroIntegration { + return { + name: 'build-logger', + hooks: { + 'astro:build:start': () => { + console.log('Build started'); + }, + 'astro:build:setup': ({ vite, target }) => { + console.log(`Build setup for target: ${target}`); + }, + 'astro:build:generated': ({ dir }) => { + console.log(`Files generated in: ${dir}`); + }, + 'astro:build:ssr': ({ manifest }) => { + console.log('SSR build complete'); + console.log(`Manifest entries: ${Object.keys(manifest).length}`); + }, + 'astro:build:done': ({ dir, routes }) => { + console.log('Build completed'); + console.log(`Output directory: ${dir}`); + console.log(`Built routes: ${routes.length}`); + + // Log details of each built route + routes.forEach((route, index) => { + console.log(`[${index + 1}] Route: ${route.route}`); + console.log(` Type: ${route.type}`); + if (route.distURL) { + console.log(` Output: ${route.distURL.pathname}`); + } + }); + } + }, + }; +} \ No newline at end of file diff --git a/packages/polymech/scripts/tools-output.json b/packages/polymech/scripts/tools-output.json new file mode 100644 index 0000000..93d740c --- /dev/null +++ b/packages/polymech/scripts/tools-output.json @@ -0,0 +1,63 @@ +[ + { + "path": "../src/model/registry.ts", + "content": "aW1wb3J0IHsgQ29tcG9uZW50Q29uZmlnU2NoZW1hLCBJQ29tcG9uZW50Q29uZmlnLCBJQ29tcG9uZW50Tm9kZSB9IGZyb20gJy4uLy4uL3BvbHltZWNoLW1vbm8vcGFja2FnZXMvY29tbW9ucy9zcmMvY29tcG9uZW50LmpzJztcblxuLyoqXG4gKiBIYW5kbGVyIGludGVyZmFjZSBmb3IgdHJhbnNmb3JtaW5nIGNvbXBvbmVudCBkYXRhIHRvIHBsYXRmb3JtLXNwZWNpZmljIGZvcm1hdHNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJRGF0YUhhbmRsZXI8VCBleHRlbmRzIHVua25vd24+IHtcbiAgLyoqXG4gICAqIFRyYW5zZm9ybSBjb21wb25lbnQgZGF0YSB0byBwbGF0Zm9ybS1zcGVjaWZpYyBmb3JtYXRcbiAgICogQHBhcmFtIG5vZGUgLSBDb21wb25lbnQgbm9kZVxuICAgKiBAcmV0dXJucyBQbGF0Zm9ybS1zcGVjaWZpYyBkYXRhIHN0cnVjdHVyZVxuICAgKi9cbiAgdHJhbnNmb3JtKG5vZGU6IElDb21wb25lbnROb2RlKTogVDtcblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIGhhbmRsZXIgY2FuIHByb2Nlc3MgdGhlIGNvbXBvbmVudFxuICAgKiBAcGFyYW0gbm9kZSAtIENvbXBvbmVudCBub2RlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgaGFuZGxlciBjYW4gcHJvY2VzcyB0aGUgY29tcG9uZW50XG4gICAqL1xuICBjYW5IYW5kbGUobm9kZTogSUNvbXBvbmVudE5vZGUpOiBib29sZWFuO1xufVxuXG4vLyBJbXBvcnQgaGFuZGxlcnMgZnJvbSByZXNwZWN0aXZlIGZpbGVzXG5pbXBvcnQgeyBSU1NIYW5kbGVyLCBSU1NJdGVtIH0gZnJvbSAnLi9yc3MuanMnO1xuaW1wb3J0IHsgTWVyY2hhbnRIYW5kbGVyLCBNZXJjaGFudEl0ZW0gfSBmcm9tICcuL21lcmNoYW50LmpzJztcbmltcG9ydCB7IEpTT05MREhhbmRsZXIsIEpTT05MRFByb2R1Y3QgfSBmcm9tICcuL2pzb24tbGQuanMnO1xuXG4vLyBFeHBvcnQgdHlwZXMgZm9yIHVzZSBpbiBvdGhlciBmaWxlc1xuZXhwb3J0IHR5cGUgeyBSU1NJdGVtLCBNZXJjaGFudEl0ZW0sIEpTT05MRFByb2R1Y3QgfTtcblxuLyoqXG4gKiBSZWdpc3RyeSBmb3IgZGF0YSBoYW5kbGVyc1xuICovXG5leHBvcnQgY2xhc3MgSGFuZGxlclJlZ2lzdHJ5IHtcbiAgcHJpdmF0ZSBzdGF0aWMgX2luc3RhbmNlOiBIYW5kbGVyUmVnaXN0cnk7XG4gIHByaXZhdGUgX2hhbmRsZXJzOiBNYXA8c3RyaW5nLCBJRGF0YUhhbmRsZXI8dW5rbm93bj4+ID0gbmV3IE1hcCgpO1xuXG4gIC8qKlxuICAgKiBHZXQgc2luZ2xldG9uIGluc3RhbmNlXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGdldCBpbnN0YW5jZSgpOiBIYW5kbGVyUmVnaXN0cnkge1xuICAgIGlmICghdGhpcy5faW5zdGFuY2UpIHtcbiAgICAgIHRoaXMuX2luc3RhbmNlID0gbmV3IEhhbmRsZXJSZWdpc3RyeSgpO1xuICAgICAgdGhpcy5faW5zdGFuY2UucmVnaXN0ZXJEZWZhdWx0SGFuZGxlcnMoKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2luc3RhbmNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZ2lzdGVyIGEgbmV3IGhhbmRsZXJcbiAgICogQHBhcmFtIHR5cGUgLSBUeXBlIG9mIGhhbmRsZXJcbiAgICogQHBhcmFtIGhhbmRsZXIgLSBIYW5kbGVyIGluc3RhbmNlXG4gICAqL1xuICBwdWJsaWMgcmVnaXN0ZXI8VCBleHRlbmRzIHVua25vd24+KHR5cGU6IHN0cmluZywgaGFuZGxlcjogSURhdGFIYW5kbGVyPFQ+KTogdm9pZCB7XG4gICAgdGhpcy5faGFuZGxlcnMuc2V0KHR5cGUsIGhhbmRsZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIGhhbmRsZXIgYnkgdHlwZVxuICAgKiBAcGFyYW0gdHlwZSAtIFR5cGUgb2YgaGFuZGxlclxuICAgKiBAcmV0dXJucyBIYW5kbGVyIGluc3RhbmNlIG9yIHVuZGVmaW5lZFxuICAgKi9cbiAgcHVibGljIGdldDxUIGV4dGVuZHMgdW5rbm93bj4odHlwZTogc3RyaW5nKTogSURhdGFIYW5kbGVyPFQ+IHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5faGFuZGxlcnMuZ2V0KHR5cGUpIGFzIElEYXRhSGFuZGxlcjxUPiB8IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kIGFwcHJvcHJpYXRlIGhhbmRsZXJzIGZvciBhIGNvbXBvbmVudFxuICAgKiBAcGFyYW0gbm9kZSAtIENvbXBvbmVudCBub2RlXG4gICAqIEByZXR1cm5zIEFycmF5IG9mIGhhbmRsZXJzIHRoYXQgY2FuIHByb2Nlc3MgdGhlIGNvbXBvbmVudFxuICAgKi9cbiAgcHVibGljIGZpbmRIYW5kbGVycyhub2RlOiBJQ29tcG9uZW50Tm9kZSk6IElEYXRhSGFuZGxlcjx1bmtub3duPltdIHtcbiAgICBjb25zdCBoYW5kbGVycyA9IFtdO1xuICAgIGZvciAoY29uc3QgaGFuZGxlciBvZiB0aGlzLl9oYW5kbGVycy52YWx1ZXMoKSkge1xuICAgICAgaWYgKGhhbmRsZXIuY2FuSGFuZGxlKG5vZGUpKSB7XG4gICAgICAgIGhhbmRsZXJzLnB1c2goaGFuZGxlcik7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBoYW5kbGVycztcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm0gY29tcG9uZW50IGRhdGEgdXNpbmcgdGhlIHNwZWNpZmllZCBoYW5kbGVyIHR5cGVcbiAgICogQHBhcmFtIHR5cGUgLSBIYW5kbGVyIHR5cGVcbiAgICogQHBhcmFtIG5vZGUgLSBDb21wb25lbnQgbm9kZVxuICAgKiBAcmV0dXJucyBUcmFuc2Zvcm1lZCBkYXRhIG9yIG51bGwgaWYgbm8gaGFuZGxlciBmb3VuZCBvciBjYW5ub3QgaGFuZGxlXG4gICAqL1xuICBwdWJsaWMgdHJhbnNmb3JtPFQgZXh0ZW5kcyB1bmtub3duPih0eXBlOiBzdHJpbmcsIG5vZGU6IElDb21wb25lbnROb2RlKTogVCB8IG51bGwge1xuICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLmdldDxUPih0eXBlKTtcbiAgICBpZiAoIWhhbmRsZXIgfHwgIWhhbmRsZXIuY2FuSGFuZGxlKG5vZGUpKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgcmV0dXJuIGhhbmRsZXIudHJhbnNmb3JtKG5vZGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYW5zZm9ybSBjb21wb25lbnQgZGF0YSB1c2luZyBhbGwgYXBwbGljYWJsZSBoYW5kbGVyc1xuICAgKiBAcGFyYW0gbm9kZSAtIENvbXBvbmVudCBub2RlXG4gICAqIEByZXR1cm5zIE1hcCBvZiB0cmFuc2Zvcm1lZCBkYXRhIGJ5IGhhbmRsZXIgdHlwZVxuICAgKi9cbiAgcHVibGljIHRyYW5zZm9ybUFsbChub2RlOiBJQ29tcG9uZW50Tm9kZSk6IE1hcDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCByZXN1bHQgPSBuZXcgTWFwKCk7XG4gICAgZm9yIChjb25zdCBbdHlwZSwgaGFuZGxlcl0gb2YgdGhpcy5faGFuZGxlcnMuZW50cmllcygpKSB7XG4gICAgICBpZiAoaGFuZGxlci5jYW5IYW5kbGUobm9kZSkpIHtcbiAgICAgICAgcmVzdWx0LnNldCh0eXBlLCBoYW5kbGVyLnRyYW5zZm9ybShub2RlKSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXIgZGVmYXVsdCBoYW5kbGVyc1xuICAgKi9cbiAgcHJpdmF0ZSByZWdpc3RlckRlZmF1bHRIYW5kbGVycygpOiB2b2lkIHtcbiAgICB0aGlzLnJlZ2lzdGVyKCdyc3MnLCBuZXcgUlNTSGFuZGxlcigpKTtcbiAgICB0aGlzLnJlZ2lzdGVyKCdtZXJjaGFudCcsIG5ldyBNZXJjaGFudEhhbmRsZXIoKSk7XG4gICAgdGhpcy5yZWdpc3RlcignanNvbi1sZCcsIG5ldyBKU09OTERIYW5kbGVyKCkpO1xuICB9XG59XG5cbi8vIEV4cG9ydCBhIGRlZmF1bHQgaW5zdGFuY2Vcbmv4cG9ydCBjb25zdCByZWdpc3RyeSA9IEhhbmRsZXJSZWdpc3RyeS5pbnN0YW5jZTtcbiJcbiAgfSxcbiAge1xuICAgIFwicGF0aFwiOiBcIi4uL3NyYy9tb2RlbC9yc3MudHNcIixcbiAgICBcImNvbnRlbnRcIjogXCJpbXBvcnQgeyBJRGF0YUhhbmRsZXIgfSBmcm9tICcuL3JlZ2lzdHJ5LmpzJztcbmltcG9ydCB7IElDb21wb25lbnROb2RlIH0gZnJvbSAnLi4vLi4vcG9seW1lY2gtbW9uby9wYWNrYWdlcy9jb21tb25zL3NyYy9jb21wb25lbnQuanMnO1xuXG4vKipcbiAqIEludGVyZmFjZSBmb3IgUlNTIGl0ZW0gc3RydWN0dXJlIGJhc2VkIG9uIEBhc3Ryb2pzL3Jzc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIFJTU0l0ZW0ge1xuICAvKiogVGhlIHRpdGxlIG9mIHRoZSBpdGVtICovXG4gIHRpdGxlOiBzdHJpbmc7XG4gIFxuICAvKiogVGhlIGxpbmsgb2YgdGhlIGl0ZW0gKi9cbiAgbGluazogc3RyaW5nO1xuICBcbiAgLyoqIEEgdW5pcXVlIGlkZW50aWZpZXIgZm9yIHRoZSBpdGVtICovXG4gIGd1aWQ/OiBzdHJpbmc7XG4gIFxuICAvKiogVGhlIHB1YmxpY2F0aW9uIGRhdGUgb2YgdGhlIGl0ZW0gKi9cbiAgcHViRGF0ZT86IERhdGU7XG4gIFxuICAvKiogVGhlIGRlc2NyaXB0aW9uIG9mIHRoZSBpdGVtICovXG4gIGRlc2NyaXB0aW9uPzogc3RyaW5nO1xuICBcbiAgLyoqIFRoZSBjYXRlZ29yaWVzIHRoZSBpdGVtIGJlbG9uZ3MgdG8gKi9cbiAgY2F0ZWdvcmllcz86IHN0cmluZ1tdO1xuICBcbiAgLyoqIEFuIGVuY2xvc3VyZSBhc3NvY2lhdGVkIHdpdGggdGhlIGl0ZW0gKGZvciBwb2RjYXN0cykgKi9cbiAgZW5jbG9zdXJlPzoge1xuICAgIHVybDogc3RyaW5nO1xuICAgIGxlbmd0aDogbnVtYmVyO1xuICAgIHR5cGU6IHN0cmluZztcbiAgfTtcbiAgXG4gIC8qKiBDdXN0b20gZmllbGRzIGZvciB0aGUgaXRlbSAqL1xuICBjdXN0b21EYXRhPzogUmVjb3JkPHN0cmluZywgc3RyaW5nIHwgbnVtYmVyIHwgRGF0ZT47XG4gIFxuICAvKiogSW1hZ2UgcmVsYXRlZCB0byB0aGUgaXRlbSAqL1xuICBpbWFnZT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBIYW5kbGVyIGZvciB0cmFuc2Zvcm1pbmcgY29tcG9uZW50IGRhdGEgdG8gUlNTIGZlZWQgaXRlbXNcbiAqL1xuZXhwb3J0IGNsYXNzIFJTU0hhbmRsZXIgaW1wbGVtZW50cyBJRGF0YUhhbmRsZXI8UlNTSXRlbT4ge1xuICAvKipcbiAgICogVHJhbnNmb3JtIGNvbXBvbmVudCBkYXRhIHRvIFJTUyBmZWVkIGl0ZW1cbiAgICogQHBhcmFtIG5vZGUgLSBDb21wb25lbnQgbm9kZVxuICAgKiBAcmV0dXJucyBSU1MgZmVlZCBpdGVtXG4gICAqL1xuICBwdWJsaWMgdHJhbnNmb3JtKG5vZGU6IElDb21wb25lbnROb2RlKTogUlNTSXRlbSB7XG4gICAgY29uc3QgeyBjb25maWcgfSA9IG5vZGU7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIHRpdGxlOiBjb25maWcubmFtZSxcbiAgICAgIGxpbms6IGAvaXRlbXMvJHtjb25maWcuc2x1Z31gLFxuICAgICAgZ3VpZDogY29uZmlnLmNvZGUsXG4gICAgICBkZXNjcmlwdGlvbjogY29uZmlnLm1ldGFfa2V5d29yZHMgfHwgY29uZmlnLmtleXdvcmRzLFxuICAgICAgY2F0ZWdvcmllczogY29uZmlnLmNhdGVnb3J5ID8gW2NvbmZpZy5jYXRlZ29yeV0gOiB1bmRlZmluZWQsXG4gICAgICBpbWFnZTogY29uZmlnLmltYWdlPy51cmwsXG4gICAgICBjdXN0b21EYXRhOiB7XG4gICAgICAgIHByaWNlOiBjb25maWcucHJpY2UgfHwgMCxcbiAgICAgICAgY29kZTogY29uZmlnLmNvZGUsXG4gICAgICAgIHNsdWc6IGNvbmZpZy5zbHVnLFxuICAgICAgICB2ZXJzaW9uOiBjb25maWcudmVyc2lvbiB8fCAnJyxcbiAgICAgICAgc3RhdHVzOiBjb25maWcuc3RhdHVzIHx8ICcnXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiB0aGUgaGFuZGxlciBjYW4gcHJvY2VzcyB0aGUgY29tcG9uZW50XG4gICAqIEBwYXJhbSBub2RlIC0gQ29tcG9uZW50IG5vZGVcbiAgICogQHJldHVybnMgVHJ1ZSBpZiB0aGUgaGFuZGxlciBjYW4gcHJvY2VzcyB0aGUgY29tcG9uZW50XG4gICAqL1xuICBwdWJsaWMgY2FuSGFuZGxlKG5vZGU6IElDb21wb25lbnROb2RlKTogYm9vbGVhbiB7XG4gICAgLy8gQWxsIGNvbXBvbmVudHMgdGhhdCBoYXZlIHJlcXVpcmVkIGZpZWxkcyBjYW4gYmUgdHJhbnNmb3JtZWQgdG8gUlNTXG4gICAgY29uc3QgeyBjb25maWcgfSA9IG5vZGU7XG4gICAgcmV0dXJuICEhKGNvbmZpZy5uYW1lICYmIGNvbmZpZy5zbHVnICYmIGNvbmZpZy5jb2RlKTtcbiAgfVxufVxuXCJcbiAgfSxcbiAge1xuICAgIFwicGF0aFwiOiBcIi4uL3NyYy9tb2RlbC9tZXJjaGFudC50c1wiLFxuICAgIFwiY29udGVudFwiOiBcImltcG9ydCB7IElEYXRhSGFuZGxlciB9IGZyb20gJy4vcmVnaXN0cnkuanMnO1xuaW1wb3J0IHsgSUNvbXBvbmVudE5vZGUgfSBmcm9tICcuLi8uLi9wb2x5bWVjaC1tb25vL3BhY2thZ2VzL2NvbW1vbnMvc3JjL2NvbXBvbmVudC5qcyc7XG5cbi8qKlxuICogSW50ZXJmYWNlIGZvciBHb29nbGUgTWVyY2hhbnQgQ2VudGVyIFByb2R1Y3QgZGF0YVxuICovXG5leHBvcnQgaW50ZXJmYWNlIE1lcmNoYW50SXRlbSB7XG4gIC8qKiBQcm9kdWN0IElEICovXG4gIGlkOiBzdHJpbmc7XG5cbiAgLyoqIFRpdGxlIG9mIHRoZSBwcm9kdWN0ICovXG4gIHRpdGxlOiBzdHJpbmc7XG5cbiAgLyoqIFByb2R1Y3QgZGVzY3JpcHRpb24gKi9cbiAgZGVzY3JpcHRpb246IHN0cmluZztcblxuICAvKiogUHJvZHVjdCBMaW5rICovXG4gIGxpbms6IHN0cmluZztcblxuICAvKiogUHJvZHVjdCBpbWFnZSBVUkwgKi9cbiAgaW1hZ2VfbGluaz86IHN0cmluZztcblxuICAvKiogQXZhaWxhYmlsaXR5IHN0YXR1cyAqL1xuICBhdmFpbGFiaWxpdHk6IFwiaW5fc3RvY2tcIiB8IFwib3V0X29mX3N0b2NrXCIgfCBcInByZW9yZGVyXCIgfCBcImJhY2tvcmRlclwiO1xuXG4gIC8qKiBQcmljZSBpbmNsdWRpbmcgY3VycmVuY3kgY29kZSAqL1xuICBwcmljZTogc3RyaW5nO1xuXG4gIC8qKiBCcmFuZCBuYW1lICovXG4gIGJyYW5kOiBzdHJpbmc7XG5cbiAgLyoqIEdUTiBvciB1bmlxdWUgcHJvZHVjdCBjb2RlICovXG4gIGd0aW4/OiBzdHJpbmc7XG5cbiAgLyoqIENvbmRpdGlvbiBvZiB0aGUgcHJvZHVjdCAqL1xuICBjb25kaXRpb246IFwibmV3XCIgfCBcInVzZWRcIiB8IFwicmVmdXJiaXNoZWRcIjtcblxuICAvKiogUHJvZHVjdCBjYXRlZ29yeSAqL1xuICBnb29nbGVfcHJvZHVjdF9jYXRlZ29yeT86IHN0cmluZztcblxuICAvKiogUHJvZHVjdCB0eXBlICovXG4gIHByb2R1Y3RfdHlwZT86IHN0cmluZztcblxuICAvKiogTVBOIChNYW51ZmFjdHVyZXIgUGFydCBOdW1iZXIpICovXG4gIG1wbj86IHN0cmluZztcblxuICAvKiogQWRkaXRpb25hbCBpbWFnZSBsaW5rcyAqL1xuICBhZGRpdGlvbmFsX2ltYWdlX2xpbms/OiBzdHJpbmdbXTtcblxuICAvKiogU2FsZSBwcmljZSBpZiBhcHBsaWNhYmxlICovXG4gIHNhbGVfcHJpY2U/OiBzdHJpbmc7XG5cbiAgLyoqIFNhbGUgcHJpY2UgZWZmZWN0aXZlIGRhdGUgcmFuZ2UgKi9cbiAgc2FsZV9wcmljZV9lZmZlY3RpdmVfZGF0ZT86IHN0cmluZztcblxuICAvKiogSXRlbSBncm91cCBJRCBmb3IgdmFyaWFudHMgKi9cbiAgaXRlbV9ncm91cF9pZD86IHN0cmluZztcblxuICAvKiogQ29sb3Igb2YgdGhlIHByb2R1Y3QgKi9cbiAgY29sb3I/OiBzdHJpbmc7XG5cbiAgLyoqIFNpemUgb2YgdGhlIHByb2R1Y3QgKi9cbiAgc2l6ZT86IHN0cmluZztcblxuICAvKiogTWF0ZXJpYWwgb2YgdGhlIHByb2R1Y3QgKi9cbiAgbWF0ZXJpYWw/OiBzdHJpbmc7XG5cbiAgLyoqIFBhdHRlcm4gb2YgdGhlIHByb2R1Y3QgKi9cbiAgcGF0dGVybj86IHN0cmluZztcblxuICAvKiogU2hpcHBpbmcgaW5mb3JtYXRpb24gKi9cbiAgc2hpcHBpbmc/OiB7XG4gICAgY291bnRyeTogc3RyaW5nO1xuICAgIHJlZ2lvbj86IHN0cmluZztcbiAgICBzZXJ2aWNlPzogc3RyaW5nO1xuICAgIHByaWNlOiBzdHJpbmc7XG4gIH1bXTtcbn1cblxuLyoqXG4gKiBIYW5kbGVyIGZvciB0cmFuc2Zvcm1pbmcgY29tcG9uZW50IGRhdGEgdG8gR29vZ2xlIE1lcmNoYW50IENlbnRlciBmb3JtYXRcbiAqL1xuZXhwb3J0IGNsYXNzIE1lcmNoYW50SGFuZGxlciBpbXBsZW1lbnRzIElEYXRhSGFuZGxlcjxNZXJjaGFudEl0ZW0+IHtcbiAgLyoqXG4gICAqIFRyYW5zZm9ybSBjb21wb25lbnQgZGF0YSB0byBHb29nbGUgTWVyY2hhbnQgQ2VudGVyIGZvcm1hdFxuICAgKiBAcGFyYW0gbm9kZSAtIENvbXBvbmVudCBub2RlXG4gICAqIEByZXR1cm5zIE1lcmNoYW50IENlbnRlciBmb3JtYXR0ZWQgZGF0YVxuICAgKi9cbiAgcHVibGljIHRyYW5zZm9ybShub2RlOiBJQ29tcG9uZW50Tm9kZSk6IE1lcmNoYW50SXRlbSB7XG4gICAgY29uc3QgeyBjb25maWcgfSA9IG5vZGU7XG4gICAgY29uc3QgYmFzZVVybCA9ICdodHRwczovL2V4YW1wbGUuY29tJzsgLy8gUmVwbGFjZSB3aXRoIGFjdHVhbCBiYXNlIFVSTCBpbiBwcm9kdWN0aW9uXG5cbiAgICAvLyBHYXRoZXIgYWRkaXRpb25hbCBpbWFnZXMgaWYgYXZhaWxhYmxlXG4gICAgY29uc3QgYWRkaXRpb25hbEltYWdlcyA9IFtdO1xuICAgIGlmIChjb25maWcuYXNzZXRzPy5nYWxsZXJ5Py5sZW5ndGggPiAwKSB7XG4gICAgICBmb3IgKGNvbnN0IGltYWdlIG9mIGNvbmZpZy5hc3NldHMuZ2FsbGVyeSkge1xuICAgICAgICBpZiAoaW1hZ2UudXJsKSB7XG4gICAgICAgICAgYWRkaXRpb25hbEltYWdlcy5wdXNoKGltYWdlLnVybCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBEZXRlcm1pbmUgYXZhaWxhYmlsaXR5IGJhc2VkIG9uIHN0YXR1c1xuICAgIGxldCBhdmFpbGFiaWxpdHk6IFwiaW5fc3RvY2tcIiB8IFwib3V0X29mX3N0b2NrXCIgfCBcInByZW9yZGVyXCIgfCBcImJhY2tvcmRlclwiID0gXCJpbl9zdG9ja1wiO1xuICAgIGlmIChjb25maWcuc3RhdHVzKSB7XG4gICAgICBpZiAoY29uZmlnLnN0YXR1cy50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKCdvdXQgb2Ygc3RvY2snKSkge1xuICAgICAgICBhdmFpbGFiaWxpdHkgPSBcIm91dF9vZl9zdG9ja1wiO1xuICAgICAgfSBlbHNlIGlmIChjb25maWcuc3RhdHVzLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ3ByZW9yZGVyJykpIHtcbiAgICAgICAgYXZhaWxhYmlsaXR5ID0gXCJwcmVvcmRlclwiO1xuICAgICAgfSBlbHNlIGlmIChjb25maWcuc3RhdHVzLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ2JhY2tvcmRlcicpKSB7XG4gICAgICAgIGF2YWlsYWJpbGl0eSA9IFwiYmFja29yZGVyXCI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlkOiBjb25maWcuY29kZSxcbiAgICAgIHRpdGxlOiBjb25maWcubmFtZSxcbiAgICAgIGRlc2NyaXB0aW9uOiBjb25maWcubWV0YV9rZXl3b3JkcyB8fCBjb25maWcua2V5d29yZHMgfHwgY29uZmlnLm5hbWUsXG4gICAgICBsaW5rOiBgJHtiYXNlVXJsfS9pdGVtcy8ke2NvbmZpZy5zbHVnfWAsXG4gICAgICBpbWFnZV9saW5rOiBjb25maWcuaW1hZ2U/LnVybCxcbiAgICAgIGF2YWlsYWJpbGl0eSxcbiAgICAgIHByaWNlOiBgJHtjb25maWcucHJpY2UgfHwgMH0gVVNEYCxcbiAgICAgIGJyYW5kOiBcIkJyYW5kIE5hbWVcIiwgLy8gUmVwbGFjZSB3aXRoIGFjdHVhbCBicmFuZCBuYW1lXG4gICAgICBndGluOiBjb25maWcuY29kZSxcbiAgICAgIGNvbmRpdGlvbjogXCJuZXdcIixcbiAgICAgIGdvb2dsZV9wcm9kdWN0X2NhdGVnb3J5OiBcIkhhcmR3YXJlID4gVG9vbHNcIiwgLy8gUmVwbGFjZSB3aXRoIGFjdHVhbCBjYXRlZ29yeVxuICAgICAgcHJvZHVjdF90eXBlOiBjb25maWcuY2F0ZWdvcnkgfHwgXCJIYXJkd2FyZVwiLFxuICAgICAgbXBuOiBjb25maWcuY29kZSxcbiAgICAgIGFkZGl0aW9uYWxfaW1hZ2VfbGluazogYWRkaXRpb25hbEltYWdlcy5sZW5ndGggPiAwID8gYWRkaXRpb25hbEltYWdlcyA6IHVuZGVmaW5lZCxcbiAgICAgIC8vIFdlIGNhbiBhZGQgbW9yZSBmaWVsZHMgaWYgdGhleSBhcmUgYXZhaWxhYmxlIGluIHRoZSBjb21wb25lbnQgZGF0YVxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIGhhbmRsZXIgY2FuIHByb2Nlc3MgdGhlIGNvbXBvbmVudFxuICAgKiBAcGFyYW0gbm9kZSAtIENvbXBvbmVudCBub2RlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgdGhlIGhhbmRsZXIgY2FuIHByb2Nlc3MgdGhlIGNvbXBvbmVudFxuICAgKi9cbiAgcHVibGljIGNhbkhhbmRsZShub2RlOiBJQ29tcG9uZW50Tm9kZSk6IGJvb2xlYW4ge1xuICAgIC8vIENoZWNrIGZvciByZXF1aXJlZCBmaWVsZHMgZm9yIEdvb2dsZSBNZXJjaGFudCBDZW50ZXJcbiAgICBjb25zdCB7IGNvbmZpZyB9ID0gbm9kZTtcbiAgICByZXR1cm4gISEoY29uZmlnLm5hbWUgJiYgY29uZmlnLnNsdWcgJiYgY29uZmlnLmNvZGUpO1xuICB9XG59XG5cIlxuICB9LFxuICB7XG4gICAgXCJwYXRoXCI6IFwiLi4vc3JjL21vZGVsL2pzb24tbGQudHNcIixcbiAgICBcImNvbnRlbnRcIjogXCJpbXBvcnQgeyBJRGF0YUhhbmRsZXIgfSBmcm9tICcuL3JlZ2lzdHJ5LmpzJztcbmltcG9ydCB7IElDb21wb25lbnROb2RlIH0gZnJvbSAnLi4vLi4vcG9seW1lY2gtbW9uby9wYWNrYWdlcy9jb21tb25zL3NyYy9jb21wb25lbnQuanMnO1xuXG4vKipcbiAqIEludGVyZmFjZSBmb3IgSlNPTi1MRCBQcm9kdWN0IHNjaGVtYSBiYXNlZCBvbiBzY2hlbWEub3JnXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSlNPTkxEUHJvZHVjdCB7XG4gIC8qKiBTcGVjaWZpZXMgdGhlIHR5cGUgb2Ygb2JqZWN0IChcIlByb2R1Y3RcIiBmb3IgcHJvZHVjdCBzY2hlbWEgKi9cbiAgXCJAY29udGV4dFwiOiBcImh0dHBzOi8vc2NoZW1hLm9yZ1wiO1xuICBcbiAgLyoqIFNwZWNpZmllcyB0aGUgdHlwZSBvZiBvYmplY3QgKFwiUHJvZHVjdFwiIGZvciBwcm9kdWN0IHNjaGVtYSAqL1xuICBcIkB0eXBlXCI6IFwiUHJvZHVjdFwiO1xuICBcbiAgLyoqIFRoZSBuYW1lIG9mIHRoZSBwcm9kdWN0ICovXG4gIG5hbWU6IHN0cmluZztcbiAgXG4gIC8qKiBUaGUgaW1hZ2Ugb2YgdGhlIHByb2R1Y3QgKi9cbiAgaW1hZ2U/OiBzdHJpbmcgfCBzdHJpbmdbXTtcbiAgXG4gIC8qKiBUaGUgZGVzY3JpcHRpb24gb2YgdGhlIHByb2R1Y3QgKi9cbiAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gIFxuICAvKiogVGhlIGJyYW5kIG9mIHRoZSBwcm9kdWN0ICovXG4gIGJyYW5kPzoge1xuICAgIFwiQHR5cGVcIjogXCJCcmFuZFwiO1xuICAgIG5hbWU6IHN0cmluZztcbiAgfTtcbiAgXG4gIC8qKiBUaGUgb2ZmZXIgb2YgdGhlIHByb2R1Y3QgKi9cbiAgb2ZmZXJzPzoge1xuICAgIFwiQHR5cGVcIjogXCJPZmZlclwiO1xuICAgIHByaWNlOiBudW1iZXI7XG4gICAgcHJpY2VDdXJyZW5jeTogc3RyaW5nO1xuICAgIGl0ZW1Db25kaXRpb24/OiBzdHJpbmc7XG4gICAgYXZhaWxhYmlsaXR5Pzogc3RyaW5nO1xuICAgIHVybDogc3RyaW5nO1xuICAgIHByaWNlVmFsaWRVbnRpbD86IHN0cmluZztcbiAgICBza3U/OiBzdHJpbmc7XG4gIH07XG4gIFxuICAvKiogUHJvZHVjdCBJRCAqL1xuICBwcm9kdWN0SUQ/OiBzdHJpbmc7XG4gIFxuICAvKiogU2t1ICovXG4gIHNrdT86IHN0cmluZztcbiAgXG4gIC8qKiBNUE4gKE1hbnVmYWN0dXJlciBQYXJ0IE51bWJlcikgKi9cbiAgbXBuPzogc3RyaW5nO1xuICBcbiAgLyoqIFByb2R1Y3QgY2F0ZWdvcnkgKi9cbiAgY2F0ZWdvcnk/OiBzdHJpbmc7XG4gIFxuICAvKiogQWdncmVnYXRlIHJhdGluZ3MgKi9cbiAgYWdncmVnYXRlUmF0aW5nPzoge1xuICAgIFwiQHR5cGVcIjogXCJBZ2dyZWdhdGVSYXRpbmdcIjtcbiAgICByYXRpbmdWYWx1ZT86IG51bWJlcjtcbiAgICByYXRpbmdDb3VudD86IG51bWJlcjtcbiAgICByZXZpZXdDb3VudD86IG51bWJlcjtcbiAgICBiZXN0UmF0aW5nPzogbnVtYmVyO1xuICB9O1xuICBcbiAgLyoqIFByb2R1Y3QgdmFyaWFudHMgKi9cbiAgdmFyaWFudHM/OiBhbnlbXTtcbiAgXG4gIC8qKiBQcm9kdWN0IHdlaWdodCAqL1xuICB3ZWlnaHQ/OiB7XG4gICAgXCJAdHlwZVwiOiBcIlF1YW50aXRhdGl2ZVZhbHVlXCI7XG4gICAgdmFsdWU6IG51bWJlcjtcbiAgICB1bml0VGV4dDogc3RyaW5nO1xuICB9O1xuICBcbiAgLyoqIEhlaWdodCAqL1xuICBoZWlnaHQ/OiB7XG4gICAgXCJAdHlwZVwiOiBcIlF1YW50aXRhdGl2ZVZhbHVlXCI7XG4gICAgdmFsdWU6IG51bWJlcjtcbiAgICB1bml0VGV4dDogc3RyaW5nO1xuICB9O1xuICBcbiAgLyoqIFdpZHRoICovXG4gIHdpZHRoPzoge1xuICAgIFwiQHR5cGVcIjogXCJRdWFudGl0YXRpdmVWYWx1ZVwiO1xuICAgIHZhbHVlOiBudW1iZXI7XG4gICAgdW5pdFRleHQ6IHN0cmluZztcbiAgfTtcbiAgXG4gIC8qKiBEZXB0aCAqL1xuICBkZXB0aD86IHtcbiAgICBcIkB0eXBlXCI6IFwiUXVhbnRpdGF0aXZlVmFsdWVcIjtcbiAgICB2YWx1ZTogbnVtYmVyO1xuICAgIHVuaXRUZXh0OiBzdHJpbmc7XG4gIH07XG59XG5cbi8qKlxuICogSGFuZGxlciBmb3IgdHJhbnNmb3JtaW5nIGNvbXBvbmVudCBkYXRhIHRvIEpTT04tTEQgUHJvZHVjdCBzY2hlbWFcbiAqL1xuZXhwb3J0IGNsYXNzIEpTT05MREhhbmRsZXIgaW1wbGVtZW50cyBJRGF0YUhhbmRsZXI8SlNPTkxEUHJvZHVjdD4ge1xuICAvKipcbiAgICogVHJhbnNmb3JtIGNvbXBvbmVudCBkYXRhIHRvIEpTT04tTEQgcHJvZHVjdCBzY2hlbWFcbiAgICogQHBhcmFtIG5vZGUgLSBDb21wb25lbnQgbm9kZVxuICAgKiBAcmV0dXJucyBKU09OLUxEIGZvcm1hdHRlZCBwcm9kdWN0IGRhdGFcbiAgICovXG4gIHB1YmxpYyB0cmFuc2Zvcm0obm9kZTogSUNvbXBvbmVudE5vZGUpOiBKU09OTERQcm9kdWN0IHtcbiAgICBjb25zdCB7IGNvbmZpZyB9ID0gbm9kZTtcbiAgICBjb25zdCBiYXNlVXJsID0gJ2h0dHBzOi8vZXhhbXBsZS5jb20nOyAvLyBSZXBsYWNlIHdpdGggYWN0dWFsIGJhc2UgVVJMIGluIHByb2R1Y3Rpb25cbiAgICBcbiAgICAvLyBDb2xsZWN0IGltYWdlc1xuICAgIGNvbnN0IGltYWdlcyA9IFtdO1xuICAgIGlmIChjb25maWcuaW1hZ2U/LnVybCkge1xuICAgICAgaW1hZ2VzLnB1c2goY29uZmlnLmltYWdlLnVybCk7XG4gICAgfVxuICAgIGlmIChjb25maWcuYXNzZXRzPy5nYWxsZXJ5Py5sZW5ndGggPiAwKSB7XG4gICAgICBmb3IgKGNvbnN0IGltYWdlIG9mIGNvbmZpZy5hc3NldHMuZ2FsbGVyeSkge1xuICAgICAgICBpZiAoaW1hZ2UudXJsKSB7XG4gICAgICAgICAgaW1hZ2VzLnB1c2goaW1hZ2UudXJsKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBQYXJzZSBwcm9kdWN0IGRpbWVuc2lvbnMgaWYgYXZhaWxhYmxlXG4gICAgbGV0IGhlaWdodCwgd2lkdGgsIGRlcHRoO1xuICAgIFxuICAgIGlmIChjb25maWcucHJvZHVjdF9kaW1lbnNpb25zKSB7XG4gICAgICAvLyBBc3N1bWUgZGltZW5zaW9ucyBhcmUgaW4gZm9ybWF0IFwiaGVpZ2h0IHggd2lkdGggeCBkZXB0aCB1bml0c1wiXG4gICAgICBjb25zdCBkaW1QYXJ0cyA9IGNvbmZpZy5wcm9kdWN0X2RpbWVuc2lvbnMuc3BsaXQoJ3gnKTtcbiAgICAgIGlmIChkaW1QYXJ0cy5sZW5ndGggPj0gMykge1xuICAgICAgICAvLyBFeHRyYWN0IG51bWJlcnMgZnJvbSBkaW1lbnNpb25zIChzaW1wbGlmaWVkIGltcGxlbWVudGF0aW9uKVxuICAgICAgICBjb25zdCBoVmFsID0gcGFyc2VGbG9hdChkaW1QYXJ0c1swXSk7XG4gICAgICAgIGNvbnN0IHdWYWwgPSBwYXJzZUZsb2F0KGRpbVBhcnRzWzFdKTtcbiAgICAgICAgY29uc3QgZFZhbCA9IHBhcnNlRmxvYXQoZGltUGFydHNbMl0pO1xuICAgICAgICBcbiAgICAgICAgLy8gQXNzdW1lIHVuaXRzIGFyZSBtbVxuICAgICAgICBjb25zdCB1bml0VGV4dCA9ICdtbSc7XG4gICAgICAgIFxuICAgICAgICBpZiAoIWlzTmFOKGhWYWwpKSB7XG4gICAgICAgICAgaGVpZ2h0ID0ge1xuICAgICAgICAgICAgXCJAdHlwZVwiOiBcIlF1YW50aXRhdGl2ZVZhbHVlXCIsXG4gICAgICAgICAgICB2YWx1ZTogaFZhbCxcbiAgICAgICAgICAgIHVuaXRUZXh0XG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKCFpc05hTih3VmFsKSkge1xuICAgICAgICAgIHdpZHRoID0ge1xuICAgICAgICAgICAgXCJAdHlwZVwiOiBcIlF1YW50aXRhdGl2ZVZhbHVlXCIsXG4gICAgICAgICAgICB2YWx1ZTogd1ZhbCxcbiAgICAgICAgICAgIHVuaXRUZXh0XG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKCFpc05hTihkVmFsKSkge1xuICAgICAgICAgIGRlcHRoID0ge1xuICAgICAgICAgICAgXCJAdHlwZVwiOiBcIlF1YW50aXRhdGl2ZVZhbHVlXCIsXG4gICAgICAgICAgICB2YWx1ZTogZFZhbCxcbiAgICAgICAgICAgIHVuaXRUZXh0XG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHByb2R1Y3Q6IEpTT05MRFByb2R1Y3QgPSB7XG4gICAgICBcIkBjb250ZXh0XCI6IFwiaHR0cHM6Ly9zY2hlbWEub3JnXCIsXG4gICAgICBcIkB0eXBlXCI6IFwiUHJvZHVjdFwiLFxuICAgICAgbmFtZTogY29uZmlnLm5hbWUsXG4gICAgICBwcm9kdWN0SUQ6IGNvbmZpZy5jb2RlLFxuICAgICAgc2t1OiBjb25maWcuY29kZSxcbiAgICAgIG1wbjogY29uZmlnLmNvZGUsXG4gICAgICBkZXNjcmlwdGlvbjogY29uZmlnLm1ldGFfa2V5d29yZHMgfHwgY29uZmlnLmtleXdvcmRzIHx8IHVuZGVmaW5lZCxcbiAgICAgIGNhdGVnb3J5OiBjb25maWcuY2F0ZWdvcnksXG4gICAgICBicmFuZDoge1xuICAgICAgICBcIkB0eXBlXCI6IFwiQnJhbmRcIixcbiAgICAgICAgbmFtZTogXCJCcmFuZCBOYW1lXCIgLy8gUmVwbGFjZSB3aXRoIGFjdHVhbCBicmFuZCBuYW1lXG4gICAgICB9LFxuICAgICAgb2ZmZXJzOiB7XG4gICAgICAgIFwiQHR5cGVcIjogXCJPZmZlclwiLFxuICAgICAgICBwcmljZTogY29uZmlnLnByaWNlIHx8IDAsXG4gICAgICAgIHByaWNlQ3VycmVuY3k6IFwiVVNEXCIsXG4gICAgICAgIGF2YWlsYWJpbGl0eTogY29uZmlnLnN0YXR1cz8udG9Mb3dlckNhc2UoKS5pbmNsdWRlcygnb3V0JykgXG4gICAgICAgICAgPyBcImh0dHBzOi8vc2NoZW1hLm9yZy9PdXRPZlN0b2NrXCIgXG4gICAgICAgICAgOiBcImh0dHBzOi8vc2NoZW1hLm9yZy9JblN0b2NrXCIsXG4gICAgICAgIHVybDogYCR7YmFzZVVybH0vaXRlbXMvJHtjb25maWcuc2x1Z31gLFxuICAgICAgICBza3U6IGNvbmZpZy5jb2RlLFxuICAgICAgICBpdGVtQ29uZGl0aW9uOiBcImh0dHBzOi8vc2NoZW1hLm9yZy9OZXdDb25kaXRpb25cIlxuICAgICAgfVxuICAgIH07XG4gICAgXG4gICAgLy8gQWRkIG9wdGlvbmFsIGZpZWxkcyBpZiB0aGV5IGV4aXN0XG4gICAgaWYgKGltYWdlcy5sZW5ndGggPT09IDEpIHtcbiAgICAgIHByb2R1Y3QuaW1hZ2UgPSBpbWFnZXNbMF07XG4gICAgfSBlbHNlIGlmIChpbWFnZXMubGVuZ3RoID4gMSkge1xuICAgICAgcHJvZHVjdC5pbWFnZSA9IGltYWdlcztcbiAgICB9XG4gICAgXG4gICAgaWYgKGhlaWdodCkgcHJvZHVjdC5oZWlnaHQgPSBoZWlnaHQ7XG4gICAgaWYgKHdpZHRoKSBwcm9kdWN0LndpZHRoID0gd2lkdGg7XG4gICAgaWYgKGRlcHRoKSBwcm9kdWN0LmRlcHRoID0gZGVwdGg7XG4gICAgXG4gICAgcmV0dXJuIHByb2R1Y3Q7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIGhhbmRsZXIgY2FuIHByb2Nlc3MgdGhlIGNvbXBvbmVudFxuICAgKiBAcGFyYW0gbm9kZSAtIENvbXBvbmVudCBub2RlXG4gICAqIEByZXR1cm5zIFRydWUgaWYgdGhlIGhhbmRsZXIgY2FuIHByb2Nlc3MgdGhlIGNvbXBvbmVudFxuICAgKi9cbiAgcHVibGljIGNhbkhhbmRsZShub2RlOiBJQ29tcG9uZW50Tm9kZSk6IGJvb2xlYW4ge1xuICAgIC8vIENoZWNrIGZvciByZXF1aXJlZCBmaWVsZHMgZm9yIEpTT04tTERcbiAgICBjb25zdCB7IGNvbmZpZyB9ID0gbm9kZTtcbiAgICByZXR1cm4gISEoY29uZmlnLm5hbWUgJiYgY29uZmlnLnNsdWcgJiYgY29uZmlnLmNvZGUpO1xuICB9XG59XG5cIlxuICB9XG5dXG48L2FudG1sOnBhcmFtZXRlcj5cbjwvYW50bWw6aW52b2tlPlxuPC9hbnRtbDpmdW5jdGlvbl9jYWxscz5 + +I've created a registry of handlers for transforming JSON data to platform-specific formats as requested. Let me explain the implementation: + +### 1. Registry Implementation (`../src/model/registry.ts`) + +The registry is designed as a singleton that maintains a collection of data handlers: +- `IDataHandler` interface defines the contract for all handlers with `transform()` and `canHandle()` methods +- `HandlerRegistry` class manages the registration and retrieval of handlers +- The registry includes methods for transforming component data using specific handlers or all applicable handlers +- Default handlers are registered automatically when the registry is created + +### 2. RSS Handler (`../src/model/rss.ts`) + +This handler transforms component data to the format expected by @astrojs/rss: +- `RSSItem` interface defines the structure for RSS feed items +- `RSSHandler` implements the transformation logic to convert component data to RSS items +- Essential fields like title, link, description, and categories are mapped from the component config +- Additional metadata is included in the customData field + +### 3. Merchant/Google Shopping Handler (`../src/model/merchant.ts`) + +This handler transforms component data to Google Merchant Center format: +- `MerchantItem` interface defines the structure for Google Shopping product feeds +- `MerchantHandler` maps component properties to the corresponding merchant fields +- It handles complex transformations like availability status based on the component's status +- Additional images are collected from the component's gallery assets + +### 4. JSON-LD Handler (`../src/model/json-ld.ts`) + +This handler transforms component data to JSON-LD product schema for SEO: +- `JSONLDProduct` interface defines the structure for schema.org product markup +- `JSONLDHandler` creates structured data that search engines can understand +- It handles product dimensions, images, and availability status +- The output follows schema.org standards for product schema + +### Usage Example + +You can use this registry in your Astro components like this: + +```typescript +import { registry } from '../src/model/registry'; + +// Get component data +const componentNode = getComponent('component-id'); + +// Transform to RSS format +const rssItem = registry.transform('rss', componentNode); + +// Transform to Google Merchant format +const merchantItem = registry.transform('merchant', componentNode); + +// Transform to JSON-LD format +const jsonLdProduct = registry.transform('json-ld', componentNode); + +// Or transform to all applicable formats at once +const allFormats = registry.transformAll(componentNode); +``` + +This implementation provides a flexible and extensible system for transforming component data to different platform-specific formats. You can easily add new handlers by implementing the `IDataHandler` interface and registering them with the registry. \ No newline at end of file diff --git a/packages/polymech/scripts/zip.sh b/packages/polymech/scripts/zip.sh new file mode 100644 index 0000000..d08c2a0 --- /dev/null +++ b/packages/polymech/scripts/zip.sh @@ -0,0 +1 @@ +osr-sync zip --logLevel=debug --clear=false --source='./dist' --target='./dist.zip' --debug=false --profile="./sync-deploy.json" \ No newline at end of file diff --git a/packages/polymech/src/app/config-loader.ts b/packages/polymech/src/app/config-loader.ts index e31e9ef..0482957 100644 --- a/packages/polymech/src/app/config-loader.ts +++ b/packages/polymech/src/app/config-loader.ts @@ -1,42 +1,107 @@ import * as fs from "fs"; import * as path from "path"; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; import { substitute } from "@polymech/commons/variables"; -import { appConfigSchema } from "@/app/config.schema.js"; -import type { AppConfig } from "@/app/config.schema.js"; +import { appConfigSchema } from "./config.schema.js"; +import type { AppConfig } from "./config.schema.js"; -import { I18N_SOURCE_LANGUAGE } from "@/app/config.js" +import { I18N_SOURCE_LANGUAGE } from "./constants.js" -const DEFAULT_CONFIG_PATH = path.resolve("./app-config.json"); +const LIBRARY_CONFIG_PATH = path.resolve("./app-config.json"); +const USER_CONFIG_DEFAULT_PATH = path.resolve("./app-config.local.json"); + +function deepMerge(target: any, source: any): any { + if (typeof source !== 'object' || source === null) { + return source; // Primitives or null overwrite + } + if (Array.isArray(source)) { + return source; // Arrays overwrite + } + if (typeof target !== 'object' || target === null || Array.isArray(target)) { + return source; // Target is not mergeable object, overwrite + } + + const result = { ...target }; + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + const val = source[key]; + if (typeof val === 'object' && val !== null && !Array.isArray(val)) { + result[key] = deepMerge(result[key], val); + } else { + result[key] = val; + } + } + } + return result; +} export function loadConfig( locale: string = I18N_SOURCE_LANGUAGE, - configPath: string = DEFAULT_CONFIG_PATH + libraryPath: string = LIBRARY_CONFIG_PATH ): AppConfig { - let rawContent: string; + // 1. Load Library Config (Defaults) + let rawLibraryContent: string; try { - rawContent = fs.readFileSync(configPath, 'utf-8'); + rawLibraryContent = fs.readFileSync(libraryPath, 'utf-8'); } catch (error) { - throw new Error(`Failed to read config file at ${configPath}: ${error}`); + throw new Error(`Failed to read library config file at ${libraryPath}: ${error}`); } const variables = { - LANG: locale + LANG: locale, + ...process.env }; - const substitutedContent = substitute(false, rawContent, variables); - - let parsedConfig: unknown; + const substitutedLibraryContent = substitute(false, rawLibraryContent, variables); + let libraryConfig: any; try { - parsedConfig = JSON.parse(substitutedContent); + libraryConfig = JSON.parse(substitutedLibraryContent); } catch (error) { - throw new Error(`Failed to parse config JSON after substitution: ${error}`); + throw new Error(`Failed to parse library config JSON: ${error}`); } - const result = appConfigSchema.safeParse(parsedConfig); + // 2. Parse CLI Arguments + // We assume the caller might want to pass args, or we just grab process.argv + // We cast to any because yargs returns a complex type + const argv: any = yargs(hideBin(process.argv)).parseSync ? yargs(hideBin(process.argv)).parseSync() : (yargs(hideBin(process.argv)) as any).argv; + + // 3. Determine User Config Path + // Check for --config + const userConfigPath = argv.config ? path.resolve(argv.config) : USER_CONFIG_DEFAULT_PATH; + + // 4. Load User Config (if exists) + let userConfig: any = {}; + if (fs.existsSync(userConfigPath)) { + try { + const rawUserContent = fs.readFileSync(userConfigPath, 'utf-8'); + const substitutedUserContent = substitute(false, rawUserContent, variables); + userConfig = JSON.parse(substitutedUserContent); + } catch (error) { + console.warn(`Failed to load or parse user config at ${userConfigPath}: ${error}`); + } + } + + // 5. Merge: Library <- User <- CLI + // Note: yargs parses --config as part of argv, but also other flags like --core.logging_namespace + // We filter out specific known CLI-only flags if needed, but config schema validation will drop unknown keys anyway? + // Actually zod 'strip' is default in safeParse? No, usually it passes through unless strict(). + // We should probably rely on valid keys overwriting. + + // CLI args often come with standard keys like '$0', '_' which we might want to exclude if we blindly merge. + // However, deepMerge will add them. + // Ideally we would only merge keys that exist in the schema, but dynamic is fine for now. + + let mergedConfig = deepMerge(libraryConfig, userConfig); + mergedConfig = deepMerge(mergedConfig, argv); + + // 6. Validate + const result = appConfigSchema.safeParse(mergedConfig); if (!result.success) { + // Pretty print error if possible or just message throw new Error(`Config validation failed: ${result.error.message}`); } diff --git a/packages/polymech/src/app/config.d.ts b/packages/polymech/src/app/config.d.ts new file mode 100644 index 0000000..799aed6 --- /dev/null +++ b/packages/polymech/src/app/config.d.ts @@ -0,0 +1,216 @@ +export interface AppConfig { + site: Site; + footer_left: FooterLeft[]; + footer_right: any[]; + settings: Settings; + params: Params; + navigation: Navigation; + navigation_button: NavigationButton; + ecommerce: Ecommerce; + metadata: Metadata; + shopify: Shopify; + pages: Pages; + core: Core; + dev: Dev; + i18n: I18N; + products: Products; + retail: Retail; + rss: Rss; + osrl: Osrl; + features: { [key: string]: boolean }; + defaults: Defaults; + cad: Cad; + assets: Assets; + optimization: Optimization; +} + +export interface Assets { + cad_url: string; + url: string; + item_url_r: string; + item_url: string; +} + +export interface Cad { + cache: boolean; + export_configurations: boolean; + export_sub_components: boolean; + renderer: string; + renderer_view: string; + renderer_quality: number; + extensions: string[]; + model_ext: string; + default_configuration: string; + main_match: string; + cam_main_match: string; +} + +export interface Core { + logging_namespace: string; + translate_content: boolean; + languages: string[]; + languages_prod: string[]; + rtl_languages: string[]; + osr_root: string; +} + +export interface Defaults { + image_url: string; + license: string; + contact: string; +} + +export interface Dev { + file_server: string; +} + +export interface Ecommerce { + brand: string; + currencySymbol: string; + currencyCode: string; +} + +export interface FooterLeft { + href: string; + text: string; +} + +export interface I18N { + store: string; + cache: boolean; + asset_path: string; +} + +export interface Metadata { + country: string; + city: string; + author: string; + author_bio: string; + author_url: string; + image: string; + description: string; + keywords: string; +} + +export interface Navigation { + top: FooterLeft[]; +} + +export interface NavigationButton { + enable: boolean; + label: string; + link: string; +} + +export interface Optimization { + image_settings: ImageSettings; + presets: Presets; +} + +export interface ImageSettings { + gallery: Gallery; + lightbox: Gallery; +} + +export interface Gallery { + show_title: boolean; + show_description: boolean; + sizes_thumb: string; + sizes_large: string; + sizes_regular: string; +} + +export interface Presets { + slow: Fast; + medium: Fast; + fast: Fast; +} + +export interface Fast { + sizes_medium: string; + sizes_thumbs: string; + sizes_large: string; +} + +export interface Osrl { + env: string; + env_dev: string; + module_name: string; + lang_flavor: string; + product_profile: string; +} + +export interface Pages { + home: Home; +} + +export interface Home { + hero: string; + _blog: Blog; +} + +export interface Blog { + store: string; +} + +export interface Params { + contact_form_action: string; + copyright: string; +} + +export interface Products { + root: string; + howto_migration: string; + glob: string; + enabled: string; +} + +export interface Retail { + library_branch: string; + projects_branch: string; +} + +export interface Rss { + title: string; + description: string; +} + +export interface Settings { + search: boolean; + account: boolean; + sticky_header: boolean; + theme_switcher: boolean; + default_theme: string; +} + +export interface Shopify { + currencySymbol: string; + currencyCode: string; + collections: Collections; +} + +export interface Collections { + hero_slider: string; + featured_products: string; +} + +export interface Site { + title: string; + base_url: string; + description: string; + base_path: string; + trailing_slash: boolean; + favicon: string; + logo: string; + logo_darkmode: string; + logo_width: string; + logo_height: string; + logo_text: string; + image: Image; +} + +export interface Image { + default: string; + error: string; + alt: string; +} diff --git a/packages/polymech/src/app/config.json b/packages/polymech/src/app/config.json index 47cfa79..c0e0ffc 100644 --- a/packages/polymech/src/app/config.json +++ b/packages/polymech/src/app/config.json @@ -1,8 +1,8 @@ { "site": { "title": "Polymech", - "base_url": "https://polymech.info/", - "description": "", + "base_url": "https://creava.org/", + "description" : "", "base_path": "/", "trailing_slash": false, "favicon": "/images/favicon.png", @@ -14,9 +14,27 @@ "image": { "default": "/images/default-image.png", "error": "/images/error-image.png", - "alt": "Astrofront" + "alt": "Astrofront", + "src": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg" } }, + "footer_left": [ + { + "href": "/infopages/privacy", + "text": "Privacy" + }, + { + "href": "/infopages/contact", + "text": "Contact" + } , + { + "href": "/newsletter/newsletter_2024_09_en-hugo-release.html", + "text": "Newsletter" + } + ], + "footer_right": [ + + ], "settings": { "search": true, "account": true, @@ -36,7 +54,7 @@ "ecommerce": { "brand": "Polymech", "currencySymbol": "", - "currencyCode": "EU" + "currencyCode": "EUR" }, "metadata": { "country": "Spain", @@ -50,18 +68,21 @@ }, "shopify": { "currencySymbol": "", - "currencyCode": "EU", + "currencyCode": "EUR", "collections": { "hero_slider": "hidden-homepage-carousel", "featured_products": "featured-products" } }, - "pages": { - "home": { + "pages":{ + "home":{ "hero": "https://assets.osr-plastic.org/machines//assets/newsletter/common/products/extruders/overview-3.jpg", - "_blog": { - "store": "posts" + "blog":{ + "store": "resources" } } + }, + "tracking": { + "googleAnalytics": "G-RW6Q6EG3J0" } -} \ No newline at end of file +} diff --git a/packages/polymech/src/app/config.schema.ts b/packages/polymech/src/app/config.schema.ts new file mode 100644 index 0000000..bc27d76 --- /dev/null +++ b/packages/polymech/src/app/config.schema.ts @@ -0,0 +1,221 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const footerLeftSchema = z.object({ + href: z.string(), + text: z.string() +}); + +export const settingsSchema = z.object({ + search: z.boolean(), + account: z.boolean(), + sticky_header: z.boolean(), + theme_switcher: z.boolean(), + default_theme: z.string() +}); + +export const paramsSchema = z.object({ + contact_form_action: z.string(), + copyright: z.string() +}); + +export const navigationSchema = z.object({ + top: z.array(footerLeftSchema) +}); + +export const navigationButtonSchema = z.object({ + enable: z.boolean(), + label: z.string(), + link: z.string() +}); + +export const ecommerceSchema = z.object({ + brand: z.string(), + currencySymbol: z.string(), + currencyCode: z.string() +}); + +export const metadataSchema = z.object({ + country: z.string(), + city: z.string(), + author: z.string(), + author_bio: z.string(), + author_url: z.string(), + image: z.string(), + description: z.string(), + keywords: z.string() +}); + +export const coreSchema = z.object({ + logging_namespace: z.string(), + translate_content: z.boolean(), + languages: z.array(z.string()), + languages_prod: z.array(z.string()), + rtl_languages: z.array(z.string()), + osr_root: z.string() +}); + +export const devSchema = z.object({ + file_server: z.string() +}); + +export const i18NSchema = z.object({ + store: z.string(), + cache: z.boolean(), + asset_path: z.string() +}); + +export const productsSchema = z.object({ + root: z.string(), + howto_migration: z.string(), + glob: z.string(), + enabled: z.string() +}); + +export const retailSchema = z.object({ + library_branch: z.string(), + projects_branch: z.string() +}); + +export const rssSchema = z.object({ + title: z.string(), + description: z.string() +}); + +export const osrlSchema = z.object({ + env: z.string(), + env_dev: z.string(), + module_name: z.string(), + lang_flavor: z.string(), + product_profile: z.string() +}); + +export const defaultsSchema = z.object({ + image_url: z.string(), + license: z.string(), + contact: z.string() +}); + +export const cadSchema = z.object({ + cache: z.boolean(), + export_configurations: z.boolean(), + export_sub_components: z.boolean(), + renderer: z.string(), + renderer_view: z.string(), + renderer_quality: z.number(), + extensions: z.array(z.string()), + model_ext: z.string(), + default_configuration: z.string(), + main_match: z.string(), + cam_main_match: z.string() +}); + +export const assetsSchema = z.object({ + cad_url: z.string(), + url: z.string(), + item_url_r: z.string(), + item_url: z.string() +}); + +export const gallerySchema = z.object({ + show_title: z.boolean(), + show_description: z.boolean(), + sizes_thumb: z.string(), + sizes_large: z.string(), + sizes_regular: z.string() +}); + +export const fastSchema = z.object({ + sizes_medium: z.string(), + sizes_thumbs: z.string(), + sizes_large: z.string() +}); + +export const blogSchema = z.object({ + store: z.string() +}); + +export const collectionsSchema = z.object({ + hero_slider: z.string(), + featured_products: z.string() +}); + +export const imageSchema = z.object({ + default: z.string(), + error: z.string(), + alt: z.string() +}); + +export const siteSchema = z.object({ + title: z.string(), + base_url: z.string(), + description: z.string(), + base_path: z.string(), + trailing_slash: z.boolean(), + favicon: z.string(), + logo: z.string(), + logo_darkmode: z.string(), + logo_width: z.string(), + logo_height: z.string(), + logo_text: z.string(), + image: imageSchema +}); + +export const shopifySchema = z.object({ + currencySymbol: z.string(), + currencyCode: z.string(), + collections: collectionsSchema +}); + +export const imageSettingsSchema = z.object({ + gallery: gallerySchema, + lightbox: gallerySchema +}); + +export const presetsSchema = z.object({ + slow: fastSchema, + medium: fastSchema, + fast: fastSchema +}); + +export const homeSchema = z.object({ + hero: z.string(), + _blog: blogSchema +}); + +export const pagesSchema = z.object({ + home: homeSchema +}); + +export const optimizationSchema = z.object({ + image_settings: imageSettingsSchema, + presets: presetsSchema +}); + +export const appConfigSchema = z.object({ + site: siteSchema, + footer_left: z.array(footerLeftSchema), + footer_right: z.array(z.any()), + settings: settingsSchema, + params: paramsSchema, + navigation: navigationSchema, + navigation_button: navigationButtonSchema, + ecommerce: ecommerceSchema, + metadata: metadataSchema, + shopify: shopifySchema, + pages: pagesSchema, + core: coreSchema, + dev: devSchema, + i18n: i18NSchema, + products: productsSchema, + retail: retailSchema, + rss: rssSchema, + osrl: osrlSchema, + features: z.record(z.string(), z.boolean()), + defaults: defaultsSchema, + cad: cadSchema, + assets: assetsSchema, + optimization: optimizationSchema +}); + +export type AppConfig = z.infer; diff --git a/packages/polymech/src/app/config.ts b/packages/polymech/src/app/config.ts index 047073d..52c3088 100644 --- a/packages/polymech/src/app/config.ts +++ b/packages/polymech/src/app/config.ts @@ -3,29 +3,35 @@ import { IMAGE_PRESET, E_BROADBAND_SPEED } from "./network.js" import { resolve, template } from '@polymech/commons' import { sync as read } from '@polymech/fs/read' import { sanitizeUri } from 'micromark-util-sanitize-uri' +import { loadConfig } from './config-loader.js' +import { I18N_SOURCE_LANGUAGE } from "./constants.js" -export const OSR_ROOT = () => path.resolve(resolve("${OSR_ROOT}")) -export const FILE_SERVER_DEV = 'localhost:5000' +export { I18N_SOURCE_LANGUAGE } + +// Load config +const config = loadConfig(I18N_SOURCE_LANGUAGE) + +export const OSR_ROOT = () => path.resolve(resolve(config.core.osr_root)) +export const FILE_SERVER_DEV = config.dev.file_server + +export const LOGGING_NAMESPACE = config.core.logging_namespace +export const TRANSLATE_CONTENT = config.core.translate_content +export const LANGUAGES = config.core.languages +export const LANGUAGES_PROD = config.core.languages_prod +export const isRTL = (lang) => config.core.rtl_languages.includes(lang) -export const LOGGING_NAMESPACE = 'polymech-site' -export const TRANSLATE_CONTENT = true -export const LANGUAGES = ['en', 'ar', 'de', 'ja', 'es', 'zh', 'fr'] -//export const LANGUAGES_PROD = ['en', 'es', 'ar', 'de', 'ja', 'zh', 'fr', 'nl', 'it', 'pt'] -export const LANGUAGES_PROD = ['en', 'es', 'fr'] -export const isRTL = (lang) => lang === 'ar' // i18n constants -export const I18N_STORE = (root, lang) => `${root}/i18n-store/store-${lang}.json` -export const I18N_SOURCE_LANGUAGE = 'en' -export const I18N_CACHE = true -export const I18N_ASSET_PATH = "${SRC_DIR}/${SRC_NAME}-${DST_LANG}${SRC_EXT}" +export const I18N_STORE = (root, lang) => template(config.i18n.store, { root, lang, LANG: lang, OSR_ROOT: root }) +export const I18N_CACHE = config.i18n.cache +export const I18N_ASSET_PATH = config.i18n.asset_path // Products -export const HOWTO_MIGRATION = () => path.resolve(resolve("./data/last.json")) +export const HOWTO_MIGRATION = () => path.resolve(resolve(config.products.howto_migration)) // Products -export const PRODUCT_ROOT = () => path.resolve(resolve("${OSR_ROOT}/products")) +export const PRODUCT_ROOT = () => path.resolve(resolve(config.products.root)) export const PRODUCT_BRANCHES = read(path.join(PRODUCT_ROOT(), 'config/machines.json'), 'json') -export const PRODUCT_GLOB = '**/config.json' +export const PRODUCT_GLOB = config.products.glob // Product compiler export const PRODUCT_CONFIG = (product) => @@ -34,72 +40,52 @@ export const PRODUCT_CONFIG = (product) => product })) export const PRODUCT_DIR = (product) => path.resolve(resolve(`${PRODUCT_ROOT()}/${product}`)) -export const PRODUCT_HUGO_TEMPLATE = './osr/hugo/root.html' -export const PRODUCTS_TARGET_SRC = './src/content/en/retail' export const PRODUCTS_TARGET = (lang) => `./content/${lang}/products` // OSRL - Language export const IS_DEV = true -export const OSRL_ENV = 'astro-release' -export const OSRL_ENV_DEV = 'astro-debug' +export const OSRL_ENV = config.osrl.env +export const OSRL_ENV_DEV = config.osrl.env_dev export const OSRL_ENVIRONMENT = IS_DEV ? OSRL_ENV_DEV : OSRL_ENV -export const OSRL_MODULE_NAME = 'polymech.io' -export const OSRL_PRODUCT_PROFILE = './src/app/profile.json' -export const OSRL_LANG_FLAVOR = 'osr' +export const OSRL_MODULE_NAME = config.osrl.module_name +export const OSRL_PRODUCT_PROFILE = config.osrl.product_profile +export const OSRL_LANG_FLAVOR = config.osrl.lang_flavor // Products -export const ENABLED_PRODUCTS = "${OSR_ROOT}/products/config/machines.json" +export const ENABLED_PRODUCTS = resolve(config.products.enabled) export const PRODUCT_SPECS = (rel) => `${PRODUCT_ROOT()}/${rel}/specs.xlsx` // Tasks export const TASK_CONFIG_LOG_DIRECTORY = './config/' -// Task: compile:content -export const TASK_COMPILE_CONTENT = true -export const TASK_COMPILE_CONTENT_CACHE = false - -// Task - Logging -export const TASK_LOG_DIRECTORY = './logs/' // Task - Retail Config export const REGISTER_PRODUCT_TASKS = true -export const LIBARY_BRANCH = 'site-prod' -export const PROJECTS_BRANCH = 'projects' +export const LIBARY_BRANCH = config.retail.library_branch +export const PROJECTS_BRANCH = config.retail.projects_branch export const RETAIL_COMPILE_CACHE = false export const RETAIL_MEDIA_CACHE = true export const RETAIL_LOG_LEVEL_I18N_PRODUCT_ASSETS = 'info' -export const ConvertProductMedia = true -export const TranslateProductAssets = false -export const PopulateProductDefaults = true - // CAD -export const CAD_MAIN_MATCH = (product) => `${product}/cad*/*Global*.+(SLDASM)` -export const CAD_CAM_MAIN_MATCH = (product) => `${product}/cad*/*-CNC*.+(SLDASM)` +export const CAD_MAIN_MATCH = (product) => template(config.cad.main_match, { product }) +export const CAD_CAM_MAIN_MATCH = (product) => template(config.cad.cam_main_match, { product }) -export const CAD_CACHE = true -export const CAD_EXPORT_CONFIGURATIONS = true -export const CAD_EXPORT_SUB_COMPONENTS = true export const CAD_MODEL_FILE_PATH = (SOURCE, CONFIGURATION = '') => - SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}.tree.json`) -export const CAD_DEFAULT_CONFIGURATION = 'Default' -export const CAD_RENDERER = 'solidworks' -export const CAD_RENDERER_VIEW = 'Render' -export const CAD_RENDERER_QUALITY = 1 -export const CAD_EXTENSIONS = ['.STEP', '.html'] -export const CAD_MODEL_EXT = '.tree.json' + SOURCE.replace('.json', `${CONFIGURATION ? '-' + CONFIGURATION : ''}${config.cad.model_ext}`) + export const CAD_URL = (file: string, variables: Record) => - sanitizeUri(template("${OSR_MACHINES_ASSETS_URL}/${file}", { file, ...variables })) + sanitizeUri(template(config.assets.cad_url, { file, ...variables })) export const ASSET_URL = (file: string, variables: Record) => - sanitizeUri(template("${OSR_MACHINES_ASSETS_URL}/products/${product_rel_min}/${file}", { file, ...variables })) + sanitizeUri(template(config.assets.url, { file, ...variables })) export const ITEM_ASSET_URL_R = (variables: Record) => - template("${OSR_MACHINES_ASSETS_URL}/${ITEM_REL}/${assetPath}/${filePath}", variables) + template(config.assets.item_url_r, variables) export const ITEM_ASSET_URL = (variables: Record) => - template("http://${FILE_SERVER_DEV}/${ITEM_REL}/${assetPath}/${filePath}", variables) + template(config.assets.item_url, variables) //back compat - osr-cad export const parseBoolean = (value: string): boolean => { @@ -108,40 +94,38 @@ export const parseBoolean = (value: string): boolean => { ///////////////////////////////////////////// // // Rendering -export const SHOW_DESCRIPTION = false -export const SHOW_LICENSE = true -export const SHOW_RENDERINGS = true - -export const SHOW_TABS = false -export const SHOW_GALLERY = true -export const SHOW_FILES = true -export const SHOW_SPECS = true -export const SHOW_CHECKOUT = true -export const SHOW_CONTACT = true -export const SHOW_3D_PREVIEW = true -export const SHOW_RESOURCES = true -export const SHOW_DEBUG = false -export const SHOW_SAMPLES = true -export const SHOW_README = false -export const SHOW_RELATED = true +export const SHOW_DESCRIPTION = config.features.show_description +export const SHOW_LICENSE = config.features.show_license +export const SHOW_RENDERINGS = config.features.show_renderings +export const SHOW_TABS = config.features.show_tabs +export const SHOW_GALLERY = config.features.show_gallery +export const SHOW_FILES = config.features.show_files +export const SHOW_SPECS = config.features.show_specs +export const SHOW_CHECKOUT = config.features.show_checkout +export const SHOW_CONTACT = config.features.show_contact +export const SHOW_3D_PREVIEW = config.features.show_3d_preview +export const SHOW_RESOURCES = config.features.show_resources +export const SHOW_DEBUG = config.features.show_debug +export const SHOW_SAMPLES = config.features.show_samples +export const SHOW_README = config.features.show_readme +export const SHOW_RELATED = config.features.show_related +export const SHOW_SHOWCASE = config.features.show_showcase +export const SHOW_SCREENSHOTS = config.features.show_screenshots ///////////////////////////////////////////// // // Plugins +// // RSS -export const RSS_CONFIG = -{ - title: 'Polymech RSS Feed', - description: '', -} +export const RSS_CONFIG = config.rss ///////////////////////////////////////////// // // Defaults -export const DEFAULT_IMAGE_URL = 'https://picsum.photos/640/640' +export const DEFAULT_IMAGE_URL = config.defaults.image_url export const default_image = () => { return { @@ -151,28 +135,12 @@ export const default_image = () => { } } -export const DEFAULT_LICENSE = `CERN Open Source Hardware License` -export const DEFAULT_CONTACT = `sales@plastic-hub.com` +export const DEFAULT_LICENSE = config.defaults.license +export const DEFAULT_CONTACT = config.defaults.contact ///////////////////////////////////////////// // // Optimization export const O_IMAGE = IMAGE_PRESET[E_BROADBAND_SPEED.MEDIUM] -export const IMAGE_SETTINGS = -{ - GALLERY: { - SHOW_TITLE: true, - SHOW_DESCRIPTION: false, - SIZES_THUMB: O_IMAGE.sizes_thumbs, - SIZES_LARGE: O_IMAGE.sizes_large, - SIZES_REGULAR: O_IMAGE.sizes_medium - }, - LIGHTBOX: { - SHOW_TITLE: true, - SHOW_DESCRIPTION: true, - SIZES_THUMB: O_IMAGE.sizes_thumbs, - SIZES_LARGE: O_IMAGE.sizes_large, - SIZES_REGULAR: O_IMAGE.sizes_medium - } -} \ No newline at end of file +export const IMAGE_SETTINGS = config.optimization.image_settings \ No newline at end of file diff --git a/packages/polymech/src/app/network.ts b/packages/polymech/src/app/network.ts index 4d69a80..e92cc61 100644 --- a/packages/polymech/src/app/network.ts +++ b/packages/polymech/src/app/network.ts @@ -12,7 +12,7 @@ export enum E_BROADBAND_SPEED { } const imageConfigSchema = z.object({ - sizes: z.string(), + sizes_medium: z.string(), sizes_thumbs: z.string(), sizes_large: z.string(), }) @@ -24,26 +24,15 @@ const imagesSchema = z.object({ type Images = z.infer; -export const IMAGE_PRESET: Images = +import { loadConfig } from './config-loader.js' +import { I18N_SOURCE_LANGUAGE } from "./constants.js" + +// Load config +const config = loadConfig(I18N_SOURCE_LANGUAGE) + +export const IMAGE_PRESET: Images = { - [E_BROADBAND_SPEED.SLOW]: { - // For 2g connections: smaller image widths help performance. (Middle East & Africa) - sizes: "(min-width: 100px) 100px, 100vw", - sizes_thumbs: "(min-width: 80px) 80px, 80vw", - sizes_large: "(min-width: 320px) 320px, 320vw", - }, - [E_BROADBAND_SPEED.MEDIUM]: - { - // For 3g connections: a moderate size image for a balance of quality and speed. - sizes: "(min-width: 800px) 800px, 800vw", - sizes_thumbs: "(min-width: 120px) 120px, 120vw", - sizes_large: "(min-width: 1024px) 1024px, 1024vw", - }, - [E_BROADBAND_SPEED.FAST]: - { - // For 4g connections: larger images for high-resolution displays. - sizes: "(min-width: 1024px) 1024px, 1024vw", - sizes_thumbs: "(min-width: 180px) 180px, 180vw", - sizes_large: "(min-width: 1200px) 1200px, 1200vw" - } + [E_BROADBAND_SPEED.SLOW]: config.optimization.presets.slow, + [E_BROADBAND_SPEED.MEDIUM]: config.optimization.presets.medium, + [E_BROADBAND_SPEED.FAST]: config.optimization.presets.fast } \ No newline at end of file diff --git a/packages/polymech/src/app/profile.json b/packages/polymech/src/app/profile.json deleted file mode 100644 index dbc3ff9..0000000 --- a/packages/polymech/src/app/profile.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "includes": [], - "variables": { - "PRODUCT_ROOT": "${root}/${product}/", - "abs_url": "https://assets.osr-plastic.org", - "CACHE": "${root}/cache/", - "CACHE_URL": "${abs_url}/cache/", - "GIT_REPO": "https://git.polymech.io/", - "OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org", - "PRODUCTS_ASSETS_URL":"https://assets.osr-plastic.org/${product_rel}", - "OSR_FILES_WEB":"https://files.polymech.io/files/machines", - "PRODUCTS_FILES_URL":"${OSR_FILES_WEB}/${product_rel}", - "DISCORD":"https://discord.gg/s8K7yKwBRc" - }, - "env": { - "astro-release":{ - "includes": [ - "${PRODUCT_ROOT}" - ], - "variables": { - "OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org/" - } - }, - "astro-debug":{ - "includes": [ - "${PRODUCT_ROOT}" - ], - "variables": { - "OSR_MACHINES_ASSETS_URL":"https://assets.osr-plastic.org", - "showCart": false, - "showPrice": false, - "showResources": false, - "showShipping": false, - "showPaymentTerms": false, - "showHowtos": false, - "showRenderings": true, - "debug": true - } - } - - } - -} \ No newline at end of file diff --git a/packages/polymech/src/app/stores.json b/packages/polymech/src/app/stores.json deleted file mode 100644 index 4a446b9..0000000 --- a/packages/polymech/src/app/stores.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "shop":{ - "title": "Shop", - "description": "", - "items":"${OSR_ROOT}/products/products/**/config.json", - "root":"${OSR_ROOT}/products" - } -} \ No newline at end of file diff --git a/packages/polymech/src/app/theme.json b/packages/polymech/src/app/theme.json deleted file mode 100644 index 98e2abd..0000000 --- a/packages/polymech/src/app/theme.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "colors": { - "default": { - "theme_color": { - "primary": "#121212", - "body": "#fff", - "border": "#eaeaea", - "theme_light": "#f2f2f2", - "theme_dark": "#000" - }, - "text_color": { - "default": "#444", - "dark": "#000", - "light": "#666" - } - }, - "darkmode": { - "theme_color": { - "primary": "#fff", - "body": "#252525", - "border": "#3E3E3E", - "theme_light": "#222222", - "theme_dark": "#000" - }, - "text_color": { - "default": "#DDD", - "dark": "#fff", - "light": "#DDD" - } - } - }, - "fonts": { - "font_family": { - "primary": "Karla:wght@400;500;700", - "primary_type": "sans-serif", - "secondary": "", - "secondary_type": "" - }, - "font_size": { - "base": "16", - "scale": "1.2" - } - } -}