diff --git a/README.md b/README.md deleted file mode 120000 index c65a710c..00000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -./packages/launchpad/README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..861a1b89 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# 🚀 Launchpad + +Launchpad is a suite of tools for managing interactive installations. It provides content management, process monitoring, and system configuration capabilities. + +## Documentation + +For complete documentation, configuration options, and guides, visit: +[Launchpad Documentation](https://bluecadet.github.io/launchpad/) + +## Core Packages + +- [@bluecadet/launchpad-cli](./packages/cli): Command line interface and configuration management +- [@bluecadet/launchpad-content](./packages/content): Content pipeline and transformation tools +- [@bluecadet/launchpad-monitor](./packages/monitor): Process monitoring and management +- [@bluecadet/launchpad-scaffold](./packages/scaffold): System configuration +- [@bluecadet/launchpad-utils](./packages/utils): Shared utilities +- [@bluecadet/launchpad](./packages/launchpad): Meta-package that installs all core packages + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for development guidelines. + +## License + +MIT © Bluecadet diff --git a/biome.json b/biome.json index 2371b981..4cfbad56 100644 --- a/biome.json +++ b/biome.json @@ -8,7 +8,7 @@ "files": { "ignoreUnknown": false, "ignore": [], - "include": ["**/src/**/*.ts"] + "include": ["**/src/**/*.ts", "docs/**/*.ts", "docs/**/*.mts"] }, "formatter": { "enabled": true, diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 7e469d42..83434eaa 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,70 +1,135 @@ -import { defineConfig } from 'vitepress' -import pkg from '../../packages/launchpad/package.json' +import { defineConfig } from "vitepress"; +import pkg from "../../packages/launchpad/package.json"; // https://vitepress.dev/reference/site-config export default defineConfig({ - lang: 'en-US', - title: "Launchpad", - description: "A suite of tools to manage media installations", - lastUpdated: true, - ignoreDeadLinks: true, // TODO: disable after writing all docs - base: '/launchpad/', - themeConfig: { - search: { - provider: 'local' - }, + lang: "en-US", + title: "Launchpad", + description: "A suite of tools to manage media installations", + lastUpdated: true, + base: "/launchpad/", + srcDir: "src", - nav: [ - { - text: pkg.version, - items: [ - { - text: 'Changelog', - link: 'https://github.com/bluecadet/launchpad/releases' - }, - { - text: 'Contributing', - link: 'https://github.com/bluecadet/launchpad/blob/develop/CONTRIBUTING.md' - } - ] - } - ], + themeConfig: { + siteTitle: "🚀 Launchpad", + search: { + provider: "local", + }, + outline: { + level: [2, 3], + }, + nav: [ + { + text: pkg.version, + items: [ + { + text: "Changelog", + link: "https://github.com/bluecadet/launchpad/releases", + }, + { + text: "Contributing", + link: "https://github.com/bluecadet/launchpad/blob/develop/CONTRIBUTING.md", + }, + ], + }, + ], - sidebar: [ - { - text: 'Guides', - items: [ - { text: 'Introduction', link: '/' }, - { text: 'Getting Started', link: '/guides/getting-started' }, - ] - }, - { - text: 'Content', - items: [ - { text: 'Overview', link: '/content/overview' }, - { text: 'Caching & Updates', link: '/content/caching' } - ] - }, - { - text: 'CLI', - items: [ - { text: 'Commands', link: '/cli/commands' } - ] - } - ], + sidebar: [ + { + text: "Guides", + items: [ + { text: "Introduction", link: "/" }, + { text: "Getting Started", link: "/guides/getting-started" }, + { text: "Fetching Content", link: "/guides/fetching-content" }, + { text: "Downloading Media", link: "/guides/downloading-media" }, + { text: "Running Applications", link: "/guides/running-applications" }, + ], + }, + { + text: "Recipes", + items: [ + { text: "Custom Content Source", link: "/recipes/custom-content-source" }, + { text: "Custom Content Plugin", link: "/recipes/custom-content-plugin" }, + { text: "Custom Monitor Plugin", link: "/recipes/custom-monitor-plugin" }, + ], + }, + { + text: "Reference", + items: [ + { + text: "CLI", + link: "/reference/cli", + items: [ + { text: "Env", link: "/reference/cli/env" }, + { text: "Config Loading", link: "/reference/cli/config-loading" }, + { text: "Commands", link: "/reference/cli/commands" }, + ], + }, + { + text: "Content", + link: "/reference/content", + items: [ + { text: "Content Config", link: "/reference/content/content-config" }, + { + text: "Sources", + link: "/reference/content/sources", + items: [ + { text: "airtableSource", link: "/reference/content/sources/airtable-source" }, + { + text: "contentfulSource", + link: "/reference/content/sources/contentful-source", + }, + { text: "jsonSource", link: "/reference/content/sources/json-source" }, + { text: "sanitySource", link: "/reference/content/sources/sanity-source" }, + { text: "strapiSource", link: "/reference/content/sources/strapi-source" }, + ], + }, + { + text: "Plugins", + link: "/reference/content/plugins", + items: [ + { text: "mdToHtml", link: "/reference/content/plugins/md-to-html" }, + { text: "sanityToHtml", link: "/reference/content/plugins/sanity-to-html" }, + { text: "sanityToPlain", link: "/reference/content/plugins/sanity-to-plain" }, + { text: "sanityToMd", link: "/reference/content/plugins/sanity-to-md" }, + { text: "mediaDownloader", link: "/reference/content/plugins/media-downloader" }, + { text: "sharp", link: "/reference/content/plugins/sharp" }, + { + text: "sanityImageUrlTransform", + link: "/reference/content/plugins/sanity-image-url-transform", + }, + ], + }, + { text: "DataStore", link: "/reference/content/data-store" }, + ], + }, + { + text: "Monitor", + link: "/reference/monitor", + items: [ + { text: "Monitor Config", link: "/reference/monitor/monitor-config" }, + { text: "Plugins", link: "/reference/monitor/plugins" }, + ], + }, + { + text: "Scaffold", + link: "/reference/scaffold", + items: [{ text: "Scaffold Config", link: "/reference/scaffold/scaffold-config" }], + }, + ], + }, + ], - socialLinks: [ - { icon: 'github', link: 'https://github.com/bluecadet/launchpad' } - ], + socialLinks: [{ icon: "github", link: "https://github.com/bluecadet/launchpad" }], - editLink: { - pattern: 'https://github.com/bluecadet/launchpad/edit/develop/docs/:path', - text: 'Edit this page on GitHub' - }, + editLink: { + pattern: "https://github.com/bluecadet/launchpad/edit/develop/docs/:path", + text: "Edit this page on GitHub", + }, - footer: { - message: 'Released under the MIT License.', - copyright: 'Copyright © 2024 Bluecadet' - } - } -}) + footer: { + message: "Released under the MIT License.", + copyright: "Copyright © 2024 Bluecadet", + }, + }, +}); diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index def4cfc8..e92c1030 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,17 +1,17 @@ +import type { Theme } from "vitepress"; +import DefaultTheme from "vitepress/theme"; // https://vitepress.dev/guide/custom-theme -import { h } from 'vue' -import type { Theme } from 'vitepress' -import DefaultTheme from 'vitepress/theme' -import './style.css' +import { h } from "vue"; +import "./style.css"; export default { - extends: DefaultTheme, - Layout: () => { - return h(DefaultTheme.Layout, null, { - // https://vitepress.dev/guide/extending-default-theme#layout-slots - }) - }, - enhanceApp({ app, router, siteData }) { - // ... - } -} satisfies Theme + extends: DefaultTheme, + Layout: () => { + return h(DefaultTheme.Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + }); + }, + enhanceApp({ app, router, siteData }) { + // ... + }, +} satisfies Theme; diff --git a/docs/cli/commands.md b/docs/cli/commands.md deleted file mode 100644 index d9927dc5..00000000 --- a/docs/cli/commands.md +++ /dev/null @@ -1,61 +0,0 @@ -# CLI Commands - -Launchpad provides several CLI commands to manage your exhibit: - -## General Options - -- `-c, --config ` - Path to your JS config file -- `-e, --env ` - Path(s) to your .env file(s) -- `-E, --env-cascade ` - Cascade env variables from `.env`, `.env.local`, `.env.`, `.env..local` - -## Core Commands - -### `launchpad start` - -Starts Launchpad by updating content and starting apps. - -Requires installing `@bluecadet/launchpad-content` and `@bluecadet/launchpad-monitor` or `@bluecadet/launchpad` as a dependency. - -```bash -launchpad start [options] -``` - -### `launchpad stop` - -Stops Launchpad by stopping apps and killing any existing PM2 instance. - -Requires installing `@bluecadet/launchpad-monitor` or `@bluecadet/launchpad` as a dependency. - -```bash -launchpad stop -``` - -### `launchpad content` - -Only starts apps without downloading content. - -Requires installing `@bluecadet/launchpad-content` or `@bluecadet/launchpad` as a dependency. - -```bash -launchpad content -``` - -### `launchpad monitor` - -Only starts apps without downloading content. - -Requires installing `@bluecadet/launchpad-monitor` or `@bluecadet/launchpad` as a dependency. - -```bash -launchpad monitor -``` - -### `launchpad scaffold` - -Configures the current PC for exhibit environments (requires admin privileges). - -Requires installing `@bluecadet/launchpad-scaffold` or `@bluecadet/launchpad` as a dependency. - -```bash -launchpad scaffold -``` \ No newline at end of file diff --git a/docs/content/caching.md b/docs/content/caching.md deleted file mode 100644 index c362964f..00000000 --- a/docs/content/caching.md +++ /dev/null @@ -1,62 +0,0 @@ -# Caching & Updates - -Launchpad implements a smart caching system to minimize unnecessary downloads and ensure reliable content updates. - -## How Caching Works - -The caching system operates on multiple levels: - -1. **Content Caching**: Checks if remote content has changed -2. **Media Caching**: Tracks downloaded media files -3. **Transform Caching**: Caches processed images and content - -## Configuration - -```js -import { defineConfig } from '@bluecadet/launchpad'; - -export default defineConfig({ - content: { - downloadPath: '.downloads/', - tempPath: '.tmp/', - backupPath: '.backups/', - backupAndRestore: true - } -}); -``` - -## Cache Directories - -| Directory | Purpose | Default Path | -| -------------- | ------------------------ | ------------- | -| `downloadPath` | Final content location | `.downloads/` | -| `tempPath` | Temporary processing | `.tmp/` | -| `backupPath` | Backup of previous state | `.backups/` | - -## Backup & Restore - -Launchpad maintains backups of your content: - -```js -{ - content: { - // Enable automatic backup/restore - backupAndRestore: true, - - // Backup location with timestamp - backupPath: '.backups/%TIMESTAMP%/', - - // Keep specific files during updates - keep: ['*.git*', 'custom/**/*'] - } -} -``` - -## Error Recovery - -The caching system provides automatic error recovery: - -1. Content is downloaded to temp directory -2. Current content is backed up -3. New content replaces old content -4. On error, backup is restored diff --git a/docs/content/overview.md b/docs/content/overview.md deleted file mode 100644 index 54756c5d..00000000 --- a/docs/content/overview.md +++ /dev/null @@ -1,102 +0,0 @@ -# Content Management - -The Launchpad Content system downloads and processes content from various sources. It uses a plugin system to transform content and download media files. - -## Basic Usage - -```js -import { defineConfig } from '@bluecadet/launchpad-cli'; -import { jsonSource, mdToHtml, mediaDownloader } from '@bluecadet/launchpad-content'; - -export default defineConfig({ - content: { - // Define content sources - sources: [ - jsonSource({ - id: 'blog', - files: { - 'posts.json': 'https://api.example.com/posts' - } - }) - ], - // Configure content processing - plugins: [ - // Convert markdown to HTML - mdToHtml({ - path: '$.content' - }), - // Download referenced media - mediaDownloader({ - mediaPattern: /\.(jpg|png|mp4)$/i - }) - ] - } -}); -``` - -## Content Sources - -Sources are functions that create content fetchers. Each source: -- Has a unique ID -- Downloads content from a specific API -- Returns content in a standardized format -- Uses `neverthrow` for error handling - -Available sources: -- [JSON Source](./sources/json) - Any HTTP JSON API -- [Airtable Source](./sources/airtable) - Airtable bases -- [Contentful Source](./sources/contentful) - Contentful CMS -- [Sanity Source](./sources/sanity) - Sanity.io -- [Strapi Source](./sources/strapi) - Strapi CMS - -## Content Plugins - -Plugins process content after it's downloaded. Common use cases: -- Converting markdown to HTML -- Downloading media files -- Custom transformations - -Built-in plugins: -- [Markdown to HTML](./plugins/md-to-html) -- [Sanity Transforms](./plugins/sanity-transforms) -- [Media Downloader](./plugins/media-downloader) - -## Content Flow - -1. **Source Setup** - ```js - jsonSource({ - id: 'blog', - files: { /* ... */ } - }); - ``` - -3. **Plugin Processing** - ```js - // Plugins transform content in the DataStore - mdToHtml({ - path: '$.content' - }); - ``` - -4. **File Output** - ``` - .downloads/ - └── blog/ # Source ID - ├── posts.json # Content files - └── media/ # Downloaded media - ``` - -## Error Handling - -The system uses `neverthrow` for robust error handling. If an error occurs, or a plugin throws an error, the error is logged and the backup is restored. - -## Caching & Updates - -Content is cached intelligently: -- HTTP caching headers respected -- Media files cached locally -- Transform results cached -- Automatic backup and restore - -[Learn more about caching](./caching) \ No newline at end of file diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 1028d496..00000000 --- a/docs/index.md +++ /dev/null @@ -1,25 +0,0 @@ -# Launchpad - -Launchpad is a highly configurable suite of tools to manage media installations. It features: - -### 🚀 **Process Management** - - Launch and monitor multiple applications - - Automatic restarts on crashes - - Graceful shutdowns - -### 📥 **Content Management** - - Download from multiple CMS platforms - - Transform content (markdown to HTML, etc) - - Process images (resize, crop, etc) - - Local caching with smart updates - -### 🔧 **System Configuration** - - Windows kiosk mode setup - - Power settings optimization - - Common exhibit configurations - -### 📊 **Monitoring & Logging** - - Centralized logging - - Process statistics - - Health monitoring - diff --git a/docs/src/guides/downloading-media.md b/docs/src/guides/downloading-media.md new file mode 100644 index 00000000..d4d39789 --- /dev/null +++ b/docs/src/guides/downloading-media.md @@ -0,0 +1,121 @@ +# Downloading Media + +Media downloading is a crucial part of the Launchpad content pipeline. This guide will walk you through downloading and transforming media files from your content sources. + +## Overview + +When you fetch content that includes media (images, videos, etc.), Launchpad can automatically: + +1. Detect media URLs in your content +2. Download the files locally +3. Update content references to point to local files +4. Transform media files (resize images, convert formats, etc.) + +## Basic Setup + +First, add the `mediaDownloader` plugin to your configuration: + +```ts +import { defineConfig } from '@bluecadet/launchpad-cli'; +import { mediaDownloader } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + mediaDownloader({ + maxConcurrent: 4 // number of simultaneous downloads + }) + ] + } +}); +``` + +The media downloader will automatically: + +- Scan your content for media URLs +- Download files to your content directory +- Update URLs in your content to point to local files + +## Image Transformations + +After downloading media, you can transform images using the `sharp` plugin. This is useful for: + +- Resizing images +- Converting formats +- Optimizing quality +- Applying effects + +Add the sharp plugin *after* the media downloader: + +```ts{8-13} +import { defineConfig } from '@bluecadet/launchpad-cli'; +import { mediaDownloader, sharp } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + mediaDownloader(), + sharp({ + buildTransform: (transform) => transform + .resize(800, 600) + .jpeg({ quality: 80 }), + updateURLs: true + }) + ] + } +}); +``` + +>[!TIP] +>The `sharp` plugin uses the powerful [sharp](https://sharp.pixelplumbing.com/) image processing library under the hood. Check their documentation for all available transformations. + +## Common Transformations + +Here are some useful image transformation examples: + +```ts +// Resize to specific dimensions +sharp({ + buildTransform: (t) => t.resize(800, 600) +}) + +// Convert to specific format +sharp({ + buildTransform: (t) => t.webp({ quality: 80 }) +}) + +// Multiple operations +sharp({ + buildTransform: (t) => t + .resize(1200, 800) + .rotate(90) + .grayscale() +}) +``` + +## Best Practices + +- **Enable Caching**: Launchpad automatically caches downloaded and transformed files +- **Order Plugins Correctly**: Always put `mediaDownloader` before `sharp` plugins + +## Troubleshooting + +If media isn't downloading: + +1. Check your network connection +2. Verify media URLs are accessible +3. Ensure proper permissions in download directory +4. Look for error messages in the console output + +If transformations aren't working: + +1. Confirm media was downloaded successfully +2. Check sharp plugin configuration +3. Verify input image format is supported +4. Look for transform-specific error messages + +## Next Steps + +- Learn more about [content plugins](../reference/content/plugins/index.md) +- Explore [sharp plugin options](../reference/content/plugins/sharp.md) +- See [media downloader configuration](../reference/content/plugins/media-downloader.md) diff --git a/docs/src/guides/fetching-content.md b/docs/src/guides/fetching-content.md new file mode 100644 index 00000000..6ad72115 --- /dev/null +++ b/docs/src/guides/fetching-content.md @@ -0,0 +1,82 @@ +# Fetching Content + +Launchpad's content fetching process is designed to be flexible and robust, allowing you to fetch, transform, and manage content from various sources. This guide provides an overview of how content flows through Launchpad, from configuration to storage. + +## Basic Configuration + +Content fetching requires a `ContentConfig` object in your Launchpad configuration: + +```js +// launchpad.config.js +import { defineConfig } from '@bluecadet/launchpad-cli'; +import { jsonSource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + jsonSource({ + id: "example-source", + files: { + "example.json": "https://example.com/api/data", + }, + }), + ], + plugins: [], // Add plugins here + downloadPath: './content', + backupAndRestore: true, + }, +}); +``` + +See the [Content Configuration Reference](/reference/content/content-config.md) for all options. + +## Running Content Updates + +You can update content in two ways: + +```bash +# As part of the full Launchpad startup +npx launchpad start + +# Content updates only +npx launchpad content +``` + +See the [CLI Commands Reference](../reference/cli/commands.md) for more details. + +## Content Sources + +Sources define where your content comes from. Launchpad includes several built-in sources: + +- JSON/REST APIs +- CMS platforms (Contentful, Sanity, etc.) + +Check the [Content Sources Reference](../reference/content/sources/index.md) to learn more about available sources and how to create custom ones. + +## Transform with Plugins + +Plugins process your content after it's downloaded. Common use cases include: + +- Converting Markdown to HTML +- Resizing images +- Validating data +- Custom transformations + +Learn more in the [Plugins Reference](../reference/content/plugins/index.md). + +## Best Practices + +- **Use Backup and Restore**: Enable `backupAndRestore` to automatically recover from failures +- **Implement Error Handling**: Use the [error handling system](../reference/content/index.md#error-handling) to gracefully handle failures +- **Monitor Progress**: Use the [logging system](../reference/content/index.md#logging) to track content updates +- **Organize Sources**: Group related content into separate sources for better management +- **Cache Effectively**: Configure appropriate cache settings for your content type + +## Next Steps + +- Read about [Content Configuration](../reference/content/content-config.md) +- Learn about [Content Sources](../reference/content/sources/index.md) +- Explore [Plugin Development](../reference/content/plugins/index.md) +- Understanding [Error Handling](../reference/content/index.md#error-handling) + +For complete API documentation, visit the [Content Reference Documentation](../reference/content/index.md). diff --git a/docs/guides/getting-started.md b/docs/src/guides/getting-started.md similarity index 90% rename from docs/guides/getting-started.md rename to docs/src/guides/getting-started.md index c989f0ae..945c4d3c 100644 --- a/docs/guides/getting-started.md +++ b/docs/src/guides/getting-started.md @@ -78,9 +78,3 @@ npx launchpad monitor # Stop any running launchpad processes npx launchpad stop ``` - -## Next Steps - -- Learn about [Content Sources](../content/sources/json) -- Configure [Process Management](../monitor/configuration) -- Set up [Health Monitoring](../monitor/health) \ No newline at end of file diff --git a/docs/src/guides/running-applications.md b/docs/src/guides/running-applications.md new file mode 100644 index 00000000..348f1d5f --- /dev/null +++ b/docs/src/guides/running-applications.md @@ -0,0 +1,93 @@ +# Running Applications + +Launchpad's monitoring system helps you manage and maintain long-running applications reliably. This guide covers how to configure, launch, and monitor your applications using Launchpad. + +## Overview + +The monitoring system is built on top of [PM2](https://pm2.keymetrics.io/), a robust process manager, providing: + +- Process management and auto-restart +- Log collection and rotation +- Application status monitoring +- Graceful shutdown handling + +## Basic Setup + +1. Install the required packages: + +```bash +npm install @bluecadet/launchpad-cli @bluecadet/launchpad-monitor +``` + +2. Configure your applications in `launchpad.config.js`: + +```js +import { defineConfig } from '@bluecadet/launchpad-cli'; + +export default defineConfig({ + monitor: { + apps: [ + { + pm2: { + name: "my-app", + script: "./app.exe", + cwd: "./builds/", + // Optional: environment variables + env: { + PORT: "3000" + } + } + } + ] + } +}); +``` + +>[!WARNING] +>Always test your configuration in a development environment first + +3. Start your application + +```bash +npx launchpad monitor start +``` + +## Configuration Options + +### Basic Settings + +- `name`: Unique identifier for your application +- `script`: Path to your executable or script +- `cwd`: Working directory for your application + +### Advanced Settings + +```js +{ + pm2: { + // Process settings + autorestart: true, + + // Resource limits + max_memory_restart: '1G', + + // Environment + env: { + NODE_ENV: 'production' + } + } +} +``` + +## Best Practices + +1. **Unique Names**: Give each application a unique, descriptive name +2. **Error Handling**: Configure proper restart policies +3. **Resource Limits**: Set memory limits to prevent system overload +4. **Logging**: Use appropriate log levels for debugging + +## Next Steps + +- Learn about [content management](./fetching-content.md) +- Explore [system configuration](./configuring-windows.md) +- Read the [monitor reference](../reference/monitor/index.md) for detailed API documentation diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 00000000..abd2449b --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,66 @@ +# 🚀 Launchpad + +**A toolkit for building and managing interactive media installations** + +Launchpad provides a collection of tools designed to streamline the development, deployment, and maintenance of media installations. It handles content management, process monitoring, system configuration, and more. + +## Key Features + +- 📂 **Content Management**: Fetch and transform content from any source +- 🔍 **Process Monitoring**: Keep your applications running reliably +- 🛠️ **System Configuration**: Automate Windows kiosk setup +- 💻 **Command Line Interface**: Easy-to-use commands for common operations +- 🔌 **Plugin Architecture**: Extend functionality with custom plugins + +## Why Launchpad? + +- **Reliable**: Built for 24/7 operation in museum environments +- **Flexible**: Modular design lets you use only what you need +- **Extensible**: Plugin system for custom functionality +- **Type-Safe**: Written in TypeScript with full type coverage + +## Core Packages + +- [@bluecadet/launchpad-cli](./reference/cli/index.md): Command-line interface and configuration management +- [@bluecadet/launchpad-content](./reference/content/index.md): Content pipeline and transformation tools +- [@bluecadet/launchpad-monitor](./reference/monitor/index.md): Process monitoring and management +- [@bluecadet/launchpad-scaffold](./reference/scaffold/index.md): Windows system configuration + +## Quick Start + +>[!NOTE] Tip +> See the [Getting Started](/guides/getting-started) guide for detailed setup instructions. + + + +1. Install the packages you need: + +```bash +npm install @bluecadet/launchpad-cli @bluecadet/launchpad-content @bluecadet/launchpad-monitor +``` + +2. Create a configuration file: + +```js +// launchpad.config.js +import { defineConfig } from '@bluecadet/launchpad-cli'; + +export default defineConfig({ + content: { + sources: [ + // Content source configurations + ] + }, + monitor: { + apps: [ + // Application configurations + ] + } +}); +``` + +3. Run launchpad: + +```bash +npx launchpad start +``` \ No newline at end of file diff --git a/docs/src/recipes/custom-content-plugin.md b/docs/src/recipes/custom-content-plugin.md new file mode 100644 index 00000000..3e82a419 --- /dev/null +++ b/docs/src/recipes/custom-content-plugin.md @@ -0,0 +1,145 @@ +# Creating a Custom Content Plugin + +This recipe walks through creating a custom content plugin for Launchpad's content pipeline. Content plugins let you transform, analyze, or enhance content after it's downloaded. + +## Overview + +Content plugins in Launchpad: + +- Run after content is downloaded from sources +- Can transform or process content +- Have access to a shared data store +- Can hook into different stages of the content pipeline +- Run in the order they are defined in your config + +## Basic Plugin Structure + +Here's a minimal plugin example: + +```typescript +const myPlugin = { + name: 'my-custom-plugin', + hooks: { + onContentFetchDone: async (ctx) => { + // Your transformation logic here + } + } +} +``` + +## Step-by-Step Example + +Let's create a plugin that adds a timestamp to every content item: + +```typescript +const timestampPlugin = { + name: 'timestamp-plugin', + hooks: { + // Runs after content is fetched + onContentFetchDone: async ({ data, logger }) => { + // Iterate through all documents + for (const doc of data.documents()) { + // Add timestamp to document + doc.data.lastUpdated = new Date().toISOString(); + // Save changes back to data store + await data.set(doc.id, doc.data); + } + + logger.info('Added timestamps to all documents'); + } + } +} +``` + +Add it to your Launchpad config: + +```typescript +import { defineConfig } from '@bluecadet/launchpad-cli'; + +export default defineConfig({ + content: { + sources: [ /* your sources */ ], + plugins: [ + timestampPlugin + ] + } +}); +``` + +## Available Hooks + +Plugins can use these hooks: + +- `onContentFetchSetup`: Before content download begins +- `onContentFetchDone`: After content is downloaded +- `onSetupError`: When setup fails +- `onContentFetchError`: When content fetch fails + +>[!TIP] +>Choose hooks based on when you need to process content. Most transformations should use `onContentFetchDone`. + +## Working with the Context + +Each hook receives a context object with useful utilities: + +```typescript +const myPlugin = { + name: 'my-plugin', + hooks: { + onContentFetchDone: async (ctx) => { + const { + data, // Access/modify content + logger, // Plugin-specific logging + paths, // Helper functions for paths + abortSignal // Check if process is stopping + } = ctx; + + // Example: Log number of documents + logger.info(`Processing ${data.size()} documents`); + } + } +} +``` + +## Best Practices + +1. **Name your plugin clearly**: Use a descriptive, unique name +2. **Handle errors gracefully**: Use try/catch and log errors +3. **Clean up temporary files**: Use `paths.getTempPath()` for temp storage +4. **Log important operations**: Use the provided logger +5. **Check abort signal**: Respect process termination + +```typescript +const bestPracticePlugin = { + name: 'best-practice-plugin', + hooks: { + onContentFetchDone: async ({ data, logger, paths, abortSignal }) => { + try { + // Check if process is aborting + if (abortSignal.aborted) return; + + // Use temp directory + const tempDir = paths.getTempPath(); + + // Log progress + logger.info('Starting content processing...'); + + // Your logic here... + + } catch (error) { + logger.error('Plugin failed:', error); + throw error; // Re-throw to notify Launchpad + } + } + } +} +``` + +## Going Further + +- See [Plugin Reference](../reference/content/plugins/index.md) for API details +- Check [Content Plugin Context](../reference/content/plugins/index.md#content-plugin-context) for full context documentation +- Explore [Built-in Plugins](../reference/content/plugins/index.md) for examples + +>[!NOTE] +>Remember to handle errors appropriately and clean up any temporary files your plugin creates. diff --git a/docs/src/recipes/custom-content-source.md b/docs/src/recipes/custom-content-source.md new file mode 100644 index 00000000..8629c5ff --- /dev/null +++ b/docs/src/recipes/custom-content-source.md @@ -0,0 +1,136 @@ +# Creating a Custom Content Source + +This recipe explains how to create a custom content source for Launchpad. Content sources allow you to fetch data from any external system and integrate it into Launchpad's content pipeline. + +## Overview + +Content sources in Launchpad: + +- Define where and how to fetch content +- Return data in a standardized format +- Can fetch from APIs, databases, files, or any data source +- Run before content plugins process the data + +## Basic Source Structure + +Here's a minimal content source example: + +```typescript +import { defineSource } from '@bluecadet/launchpad-content'; + +export default defineSource({ + id: 'my-custom-source', + fetch: async ({ logger }) => { + return { + id: 'my-document', + data: Promise.resolve({ hello: 'world' }) + }; + } +}); +``` + +## Step-by-Step Example + +Let's create a source that fetches data from a REST API: + +```typescript +import { defineSource } from '@bluecadet/launchpad-content'; + +const myApiSource = defineSource({ + id: 'api-source', + fetch: async ({ logger }) => { + try { + // Log the fetch operation + logger.info('Fetching data from API...'); + + // Fetch data from API + const response = await fetch('https://api.example.com/data'); + const data = await response.json(); + + // Return in expected format + return { + id: 'api-data', + data: Promise.resolve(data) + }; + } catch (error) { + logger.error('Failed to fetch:', error); + throw error; + } + } +}); +``` + +>[!NOTE] +>Remember to handle errors appropriately and implement proper logging for better debugging. + +Add it to your Launchpad config: + +```typescript +import { defineConfig } from '@bluecadet/launchpad-cli'; + +export default defineConfig({ + content: { + sources: [ + myApiSource + ] + } +}); +``` + +## Multiple Documents + +Sources can return multiple documents: + +```typescript +const multiDocSource = defineSource({ + id: 'multi-doc-source', + fetch: async ({ logger }) => { + return [ + { + id: 'doc-1', + data: Promise.resolve({ title: 'First Document' }) + }, + { + id: 'doc-2', + data: Promise.resolve({ title: 'Second Document' }) + } + ]; + } +}); +``` + +## Paginated Data + +For large datasets, use AsyncIterables: + +```typescript +const streamingSource = defineSource({ + id: 'stream-source', + fetch: async ({ logger }) => ({ + id: 'large-dataset', + data: (async function* () { + for (let i = 0; i < 1000; i++) { + yield { index: i }; + } + })() + }) +}); +``` + +## Best Practices + +1. **Use Meaningful IDs**: Choose descriptive, unique IDs for your source and documents +2. **Handle Errors**: Implement proper error handling and logging +3. **Respect Rate Limits**: Add delays between API calls if needed +4. **Document Requirements**: List any API keys or configuration needed +5. **Validate Data**: Check that fetched data matches expected format + +>[!TIP] +>Use TypeScript for better type safety and autocompletion when configuring your source. + +## Next Steps + +- Learn about [Content Sources](../reference/content/sources/index.md) for full API details +- Explore [Content Plugins](../reference/content/plugins/index.md) to process your fetched data +- See the [Content Configuration Reference](../reference/content/content-config.md) for config options +- Check [Built-in Sources](../reference/content/sources/index.md) for implementation examples diff --git a/docs/src/recipes/custom-monitor-plugin.md b/docs/src/recipes/custom-monitor-plugin.md new file mode 100644 index 00000000..9fda6476 --- /dev/null +++ b/docs/src/recipes/custom-monitor-plugin.md @@ -0,0 +1,150 @@ +# Creating a Custom Monitor Plugin + +This recipe walks through creating a custom monitor plugin for Launchpad's process monitoring system. Monitor plugins let you hook into process lifecycle events and add custom functionality to your process management. + +## Overview + +Monitor plugins in Launchpad: + +- React to process lifecycle events (start, stop, errors, etc.) +- Can modify or enhance monitoring behavior +- Have access to the monitor instance and its utilities +- Run in the order they are defined in your config + +## Basic Plugin Structure + +Here's a minimal plugin example: + +```typescript +const myPlugin = { + name: 'my-monitor-plugin', + hooks: { + afterAppStart: async (ctx) => { + // Your logic here + } + } +} +``` + +## Step-by-Step Example + +Let's create a plugin that logs app restarts and sends notifications: + +```typescript +const restartNotifierPlugin = { + name: 'restart-notifier', + hooks: { + // Runs after an app starts + afterAppStart: async ({ monitor, logger }) => { + const apps = monitor.getApps(); + + for (const app of apps) { + if (app.restarts > 0) { + logger.warn( + `App ${app.name} has restarted ${app.restarts} times` + ); + // Add your notification logic here + } + } + }, + + // Runs when an app encounters an error + onAppError: async ({ app, error, logger }) => { + logger.error( + `Error in ${app.name}: ${error.message}` + ); + } + } +} +``` + +Add it to your Launchpad config: + +```typescript +import { defineConfig } from '@bluecadet/launchpad-cli'; + +export default defineConfig({ + monitor: { + apps: [ /* your apps */ ], + plugins: [ + restartNotifierPlugin + ] + } +}); +``` + +## Available Hooks + +Monitor plugins can use these hooks: + +- `beforeConnect`/`afterConnect`: PM2 connection lifecycle +- `beforeDisconnect`/`afterDisconnect`: PM2 disconnection lifecycle +- `beforeAppStart`/`afterAppStart`: App start lifecycle +- `beforeAppStop`/`afterAppStop`: App stop lifecycle +- `onAppError`: App errors +- `onAppLog`/`onAppErrorLog`: App logging +- `beforeShutdown`: System shutdown + +>[!TIP] +>Choose hooks based on what events you need to monitor. Most monitoring logic will use the app lifecycle hooks. + +## Working with the Context + +Each hook receives a context object with useful utilities: + +```typescript +const myPlugin = { + name: 'my-plugin', + hooks: { + afterAppStart: async (ctx) => { + const { + monitor, // Access monitor instance + logger, // Plugin-specific logging + app // The current app (in app-specific hooks) + } = ctx; + + // Example: Log app status + logger.info(`App ${app.name} is running`); + } + } +} +``` + +## Best Practices + +1. **Name your plugin clearly**: Use a descriptive, unique name +2. **Handle errors gracefully**: Use try/catch blocks +3. **Log important events**: Use the provided logger +4. **Clean up resources**: Handle cleanup in shutdown hooks +5. **Keep it focused**: One responsibility per plugin + +```typescript +const bestPracticePlugin = { + name: 'best-practice-plugin', + hooks: { + afterAppStart: async ({ app, logger }) => { + try { + logger.info(`Monitoring app: ${app.name}`); + // Your logic here... + } catch (error) { + logger.error('Plugin failed:', error); + throw error; // Re-throw to notify Launchpad + } + }, + + beforeShutdown: async ({ logger }) => { + // Clean up resources + logger.info('Cleaning up...'); + } + } +} +``` + +## Going Further + +- See [Monitor Plugin Reference](../reference/monitor/plugins.md) for complete API details +- Check [Monitor Reference](../reference/monitor/index.md) for monitor documentation +- Explore example plugins for real-world usage + +>[!NOTE] +>Remember to test your plugins thoroughly, especially error handling and cleanup logic. diff --git a/docs/src/reference/cli/commands.md b/docs/src/reference/cli/commands.md new file mode 100644 index 00000000..6688d78c --- /dev/null +++ b/docs/src/reference/cli/commands.md @@ -0,0 +1,82 @@ +# CLI Commands + +Launchpad provides several commands to manage your media installations. Each command can be run using `npx launchpad ` or just `launchpad ` if installed globally. + +## Start Command + +```bash +launchpad start [options] +``` + +The `start` command is the primary way to launch your application. It: + +1. Downloads fresh content from configured sources +2. Starts and monitors configured applications +3. Initializes health monitoring if configured + +## Stop Command + +```bash +launchpad stop [options] +``` + +The `stop` command gracefully shuts down all Launchpad processes: + +- Stops all monitored applications +- Kills any existing PM2 instances +- Cleans up temporary files + +## Content Command + +```bash +launchpad content [options] +``` + +Use this command to manage content independently: + +- Downloads fresh content from all configured sources +- Runs content transformations +- Updates local content cache +- Does not start or affect running applications + +This is useful for updating content without restarting applications. + +## Monitor Command + +```bash +launchpad monitor [options] +``` + +The `monitor` command focuses on application management: + +- Starts configured applications +- Monitors for crashes and restarts as needed +- Collects process statistics +- Does not download or update content + +Use this when you want to restart applications without refreshing content. + +## Scaffold Command + +```bash +launchpad scaffold [options] +``` + +This command configures Windows PCs for exhibit environments: + +- Requires administrative privileges +- Configures Windows kiosk mode +- Optimizes power settings +- Sets up common exhibit configurations +- Applies system-level settings + +## Global Options + +All commands support these options: + +| Option | Description | Type | +|--------|-------------|------| +| `--config, -c` | Path to your JS config file | string | +| `--env, -e` | Path(s) to your .env file(s) | array | +| `--env-cascade, -E` | Cascade env variables from multiple .env files | string | +| `--help` | Show help information | flag | diff --git a/docs/src/reference/cli/config-loading.md b/docs/src/reference/cli/config-loading.md new file mode 100644 index 00000000..248a50ab --- /dev/null +++ b/docs/src/reference/cli/config-loading.md @@ -0,0 +1,80 @@ +# Config Loading + +The Launchpad CLI uses a flexible configuration system that automatically searches for and loads your project configuration. The CLI supports Javascript config files. + +## Config File Search + +The CLI searches for config files in the following order: + +1. `launchpad.config.js` +2. `launchpad.config.mjs` + +The search starts in the current working directory and recursively searches up parent directories (up to 64 levels) until a config file is found. + +## Config File Format + +### JavaScript/TypeScript Config (Recommended) + +```js +import { defineConfig } from '@bluecadet/launchpad-cli'; + +export default defineConfig({ + content: { + // Content management configuration + }, + monitor: { + // Process monitoring configuration + }, +}); +``` + +## Configuration Structure + +Your config file can include settings for any of Launchpad's main modules: + +- `content` - Content management settings ([Content Config Reference](../content/content-config)) +- `monitor` - Process monitoring settings ([Monitor Config Reference](../monitor/monitor-config)) + +## Environment Variables + +Config files can reference environment variables using the `process.env` object in JavaScript configs. For managing environment variables, see the [Environment Variables](./env) documentation. + +## Type Safety + +When using TypeScript or an editor with TypeScript support (like VS Code), the `defineConfig` helper provides: + +- Full IntelliSense for all configuration options +- Type checking for configuration values +- Auto-completion suggestions +- Documentation hints + +## Example + +```js +import { defineConfig } from '@bluecadet/launchpad-cli'; +import { jsonSource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + jsonSource({ + id: "api-data", + files: { + "data.json": process.env.API_ENDPOINT + } + }) + ], + downloadPath: "./content" + }, + monitor: { + apps: [ + { + pm2: { + name: "exhibit-app", + script: "./app.exe" + } + } + ] + } +}); +``` diff --git a/docs/src/reference/cli/env.md b/docs/src/reference/cli/env.md new file mode 100644 index 00000000..a0224f55 --- /dev/null +++ b/docs/src/reference/cli/env.md @@ -0,0 +1,61 @@ +# Environment Variables + +The `launchpad-cli` package supports loading environment variables from `.env` files using the `--env` and `--env-cascade` flags. This allows you to manage different configurations for various environments (development, staging, production, etc.). + +## Usage + +To load environment variables from a `.env` file: + +```bash +npx launchpad --env .env +``` + +You can also load multiple `.env` files in sequence: + +```bash +npx launchpad --env .env.local --env .env +``` + +When loading multiple files, variables from later files will override those from earlier files if they share the same name. + +### Environment Cascading + +The `--env-cascade` flag provides an automated way to load multiple environment files in a specific order. For example: + +```bash +npx launchpad --env-cascade production +``` + +This will load files in the following order: + +1. `.env` +2. `.env.local` +3. `.env.production` +4. `.env.production.local` + +## Best Practices + +- **Environment-Specific Files**: Create separate `.env` files for different environments: + - `.env.development` for development settings + - `.env.staging` for staging settings + - `.env.production` for production settings + +- **Security**: + - Keep sensitive data (API keys, passwords) in local `.env` files that aren't committed + - Use `.env.local` for machine-specific overrides + +- **Documentation**: + - Create a `.env.example` file + - List all required variables with example values + - Include this file in version control + +Example `.env.example`: + +```sh +# API Configuration +API_KEY=your_api_key_here +API_URL=https://api.example.com + +# Database Configuration +DATABASE_URL=postgresql://user:password@localhost:5432/dbname +``` diff --git a/docs/src/reference/cli/index.md b/docs/src/reference/cli/index.md new file mode 100644 index 00000000..fe795500 --- /dev/null +++ b/docs/src/reference/cli/index.md @@ -0,0 +1,54 @@ +# @Bluecadet/Launchpad-CLI + +The CLI package provides a command-line interface for managing Launchpad installations. It serves as the primary entry point for running content management, process monitoring, and system configuration tasks. + +## Features + +- **Command Line Interface**: Easy-to-use commands for common operations: + - Start/stop content downloads and app monitoring + - Run content updates independently + - Configure system settings + - Access help documentation + +- **Configuration Management**: + - Load and parse config files + - Environment variable handling with dotenv + - Cascading configuration support + - Type-safe config validation + +- **Flexible Commands**: + - `start`: Full launchpad startup (content + apps) + - `stop`: Graceful shutdown of all processes + - `content`: Content-only operations + - `monitor`: Process monitoring operations + - `scaffold`: System configuration (Windows) + +## Installation + +The CLI can be installed globally via npm: + +```bash +npm install @bluecadet/launchpad-cli + +# install any other modules you need, depeneding on your use-case +npm install @bluecadet/launchpad-content @bluecadet/launchpad-monitor +``` + +## Usage + +Once installed, you can run commands using the `launchpad` executable: + +```bash +npx launchpad [options] +``` + +For help: + +```bash +npx launchpad help +``` + +> [!NOTE] Note: +> if installed globally (`npm install -g @bluecadet/launchpad-cli`) you don't need the `npx` prefix when running commands. + +See [Commands](./commands.md) for more information on the available commands. diff --git a/docs/src/reference/content/content-config.md b/docs/src/reference/content/content-config.md new file mode 100644 index 00000000..26a0fc25 --- /dev/null +++ b/docs/src/reference/content/content-config.md @@ -0,0 +1,81 @@ +# Content Config + +Configuration for managing content sources, plugins, and file handling settings. + +## Options + +### `sources` + +- **Type:** `Array>` +- **Default:** `[]` + +A list of content source options that defines where content is downloaded from. You can configure multiple sources and use different source types simultaneously. Each source can be either a direct ContentSource object or a Promise that resolves to a ContentSource. + +For detailed source configuration options, see [Sources Reference](./sources). + +### `plugins` + +- **Type:** `Array` +- **Default:** `[]` + +A list of content transformation plugins that process content after download. Plugins can modify, analyze, or enhance content before final output. + +See [Content Plugin Reference](./content-plugins) for available plugins and usage. + +### `downloadPath` + +- **Type:** `string` +- **Default:** `.downloads/` + +Base directory path where downloaded files are stored. Can be absolute or relative path. + +### `tempPath` + +- **Type:** `string` +- **Default:** `.tmp/%TIMESTAMP%/` + +Temporary directory path used during content processing. The `%TIMESTAMP%` token is replaced with current timestamp. + +### `backupPath` + +- **Type:** `string` +- **Default:** `.backup/%TIMESTAMP%/` + +Directory path where existing content is backed up before processing new downloads. Critical for recovery if downloads fail. + +### `keep` + +- **Type:** `string[]` +- **Default:** `[]` + +Glob patterns for files to preserve when clearing directories. + +Example: + +- `["*.json"]` - Keep all JSON files +- `["**/*.csv", "*.git*"]` - Keep all CSV files in any subdirectory, and any git related files. + +### `backupAndRestore` + +- **Type:** `boolean` +- **Default:** `true` + +When enabled: + +- Creates backup of existing files before download +- Restores backup if any source download fails +- Ensures atomic success/failure of multi-source downloads + +### `maxTimeout` + +- **Type:** `number` +- **Default:** `30000` + +Maximum time in milliseconds to wait for network requests before timing out. + +### `encodeCharacters` + +- **Type:** `string` +- **Default:** `<>:"|?*` + +Special characters to encode in file paths for both content and media downloads. Ensures valid filenames across systems. diff --git a/docs/src/reference/content/data-store.md b/docs/src/reference/content/data-store.md new file mode 100644 index 00000000..3e416ea6 --- /dev/null +++ b/docs/src/reference/content/data-store.md @@ -0,0 +1,87 @@ +# DataStore + +The DataStore is a file system-based storage system used to manage content during the fetch and transform process. It provides a simple API for storing and retrieving content, with support for namespaces and documents. + +## Core Concepts + +### Namespaces + +Namespaces represent collections of documents from a single source. Each content source gets its own namespace, identified by the source's ID. + +### Documents + +Documents are individual files containing content data. They can be either single JSON files or batched files (for paginated content). + +## API Reference + +### DataStore + +#### `createNamespace(namespaceId: string)` + +Creates a new namespace in the data store. Returns a Result containing the namespace. + +#### `namespace(namespaceId: string)` + +Gets an existing namespace. Returns a Result containing the namespace. + +#### `getDocument(namespaceId: string, documentId: string)` + +Gets a specific document from a namespace. Returns a Result containing the document. + +#### `filter(ids?: DataKeys)` + +Filters documents based on namespace and document IDs. Returns grouped results by namespace. + +### Namespace + +#### `insert(id: string, data: Promise | AsyncIterable)` + +Inserts a new document into the namespace. Data can be a Promise for single documents or an AsyncIterable for batched documents. + +#### `document(id: string)` + +Gets a document by ID from the namespace. + +#### `documents()` + +Gets all documents in the namespace. + +#### `waitFor(id: string)` + +Returns a promise that resolves when the document with the passed ID has finished being written. + +### Document + +#### `update(cb: (data: T) => T | Promise)` + +Updates document content using a callback function. + +#### `apply(pathExpression: string, fn: (x: unknown) => unknown)` + +Applies a transformation to specific paths in the document using JSONPath. + +#### `query(pathExpression: string)` + +Queries document content using JSONPath expressions. + +## Error Handling + +The DataStore uses the `neverthrow` library for error handling. Most methods return a `Result` or `ResultAsync` type: + +```typescript +const namespaceResult = dataStore.namespace('my-source'); +if (namespaceResult.isErr()) { + console.error('Error:', namespaceResult.error); +} else { + const namespace = namespaceResult.value; + // Use namespace... +} +``` + +For async operations, you can use the `andThen` method: + +```typescript +await dataStore + .createNamespace('my-source') + .andThen(namespace => namespace.insert('doc1', data)); +``` diff --git a/docs/src/reference/content/index.md b/docs/src/reference/content/index.md new file mode 100644 index 00000000..f73d6496 --- /dev/null +++ b/docs/src/reference/content/index.md @@ -0,0 +1,79 @@ +# @Bluecadet/Launchpad-Content + +The content package is a powerful tool for downloading, transforming, and managing content from various sources. It provides a flexible, plugin-based architecture for handling content pipelines. + +## Features + +- **Extensible Source System**: Easily connect to any content source: + - Build custom source adapters with a simple interface + - Includes ready-to-use adapters for popular CMSs (Contentful, Airtable, Sanity, etc.) + - Type-safe data fetching and validation + +- **Flexible Plugin Architecture**: Transform and process content your way: + - Create custom plugins with straightforward APIs + - Chain multiple transformations + - Built-in plugins for common tasks (Markdown, image processing, etc.) + - Full control over the content pipeline + +- **Robust Content Management**: + - Intelligent diffing for efficient updates + - Automatic backup and recovery + - Configurable file organization + - Temporary file cleanup + - Progress tracking and detailed logging + +## Installation + +```bash +npm install @bluecadet/launchpad-content +``` + +## Basic Usage + +```typescript +import LaunchpadContent from '@bluecadet/launchpad-content'; + +const content = new LaunchpadContent({ + sources: [ + // Content source configurations + ], + plugins: [ + // Plugin configurations + ], + downloadPath: './content' +}); + +// Start content download and processing +await content.start(); +``` + +## Configuration + +Content operations are configured through a `ContentConfig` object that specifies: + +- **Sources**: Array of content sources to fetch from +- **Plugins**: Array of plugins for content processing +- **Paths**: Various path configurations for content storage +- **Backup Options**: Settings for content backup and restoration + +See the [Content Config](./content-config) section for detailed configuration options. + +## Plugins + +The plugin system is core to the content package's functionality. Plugins can: + +- Transform content formats +- Process media files +- Add custom processing steps +- Handle errors and logging + +Learn more about available plugins and creating custom ones in the [Plugins](./plugins/index.md) section. + +## Error Handling + +The package uses the `neverthrow` library for robust error handling: + +- Type-safe error handling +- Clear error boundaries +- Graceful failure recovery +- Automatic backup restoration on errors when configured diff --git a/docs/src/reference/content/plugins/index.md b/docs/src/reference/content/plugins/index.md new file mode 100644 index 00000000..a1b71d8c --- /dev/null +++ b/docs/src/reference/content/plugins/index.md @@ -0,0 +1,99 @@ +# Content Plugins + +Content plugins are used to transform, analyze, or enhance content after it has been downloaded. These plugins can modify the content in various ways, such as converting formats, sanitizing data, or applying custom transformations. + +Content plugins can be used for a variety of tasks, including but not limited to: + +- **Format Conversion**: Converting content from one format to another, such as from Markdown to HTML. +- **Data Sanitization**: Cleaning and sanitizing content to remove unwanted or harmful data. +- **Custom Transformations**: Applying custom transformations to content, such as adding metadata, restructuring data, or enhancing content with additional information. +- **Error Handling**: Managing errors that occur during content processing, such as logging errors, retrying operations, or providing fallback content. +- **Logging and Monitoring**: Tracking the content processing lifecycle, logging important events, and monitoring the performance and success of content operations. + +By leveraging content plugins and their hooks, developers can create flexible and powerful content processing pipelines that meet the specific needs of their applications. + +## Type Reference + +```typescript +type ContentPlugin = { + name: string; + hooks: { + onSetupError?: (ctx: CombinedContentHookContext, error: ContentError) => void | PromiseLike; + onContentFetchSetup?: (ctx: CombinedContentHookContext) => void | PromiseLike; + onContentFetchDone?: (ctx: CombinedContentHookContext) => void | PromiseLike; + onContentFetchError?: ( + ctx: CombinedContentHookContext, + error: ContentError, + ) => void | PromiseLike; + } +}; +``` + +## Hooks + +### `onSetupError` + +**When:** Called when there is an error during the setup phase of the plugin. + +**Why:** This hook allows the plugin to handle setup errors gracefully, possibly by logging the error or providing fallback mechanisms. + +**Argument:** `ContentError` is a subclass of Error. + +### `onContentFetchSetup` + +**When:** Called before the content fetch process begins. + +**Why:** This hook can be used to perform any necessary preparations or initializations before fetching the content. For example, it can be used to set up authentication, configure request parameters, or log the start of the fetch process. + +### `onContentFetchDone` + +**When:** Called after the content fetch process is completed. + +**Why:** This hook provides an opportunity to process the fetched content. Plugins can use this hook to transform the content, validate it, or store it in a specific format. + +### `onContentFetchError` + +**When:** Called when there is an error during the content fetch process. + +**Why:** This hook allows the plugin to handle fetch errors, such as retrying the fetch, logging the error, or providing alternative content. + +**Argument:** `ContentError` is a subclass of Error. + +## Content Plugin Context + +```typescript +type CombinedContentHookContext = { + data: DataStore; + contentOptions: ResolvedContentConfig; + logger: Logger; + abortSignal: AbortSignal; + paths: { + getDownloadPath: (source?: string) => string; + getTempPath: (source?: string) => string; + getBackupPath: (source?: string) => string; + }; +}; +``` + +### `data` + +Access to the data store where content and metadata can be stored and retrieved. This data store is a proxy to the file-system, and exposes some helpers for easily modifying the data that was fetched during the source-fetch step. + +### `contentOptions` + +The resolved content configuration object. See [Content Config Reference](../content-config.md) for more info. + +### `paths` + +Helpers for retrieving the download, temp, and backup path. If no `source` is passed, then it will return the path to the respective root directory. + +> [!NOTE] Note: +> The `getTempPath` function returns a directory scoped to the current plugin. Plugins do not share temp directories. + +### `logger` + +A plugin-specific logger. See [Log Manager](../../shared/log-manager.md) for more info. + +### `abortSignal` + +Signals the launchpad process is aborting. Triggered on exception or manual quit. diff --git a/docs/src/reference/content/plugins/md-to-html.md b/docs/src/reference/content/plugins/md-to-html.md new file mode 100644 index 00000000..e330d4ab --- /dev/null +++ b/docs/src/reference/content/plugins/md-to-html.md @@ -0,0 +1,44 @@ +# mdToHtml Content Plugin + +The `mdToHtml` plugin is used to transform Markdown content into HTML. It supports both block and inline rendering, with optional sanitization and custom Markdown syntax extensions. + +## Usage + +To use the `mdToHtml` plugin, include it in the list of content plugins in your configuration: + +```typescript{1,6-8} +import { mdToHtml } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + mdToHtml({ + path: '$.item.description' + }) + ] + } +}); +``` + +## Options + +### `path` + +- **Type:** `string` +- **Required** + +Specifies the JSONPath to the content that needs to be transformed from Markdown to HTML. + +### `simplified` + +- **Type:** `boolean` +- **Default:** `false` + +When set to `true`, the plugin will render the Markdown content as inline HTML, suitable for single paragraph content. + +### `keys` + +- **Type:** `DataKeys` +- **Default:** `undefined` + +Specifies the data keys to which the transformation should be applied. If not provided, the transformation will be applied to all keys. diff --git a/docs/src/reference/content/plugins/media-downloader.md b/docs/src/reference/content/plugins/media-downloader.md new file mode 100644 index 00000000..43a2ae0d --- /dev/null +++ b/docs/src/reference/content/plugins/media-downloader.md @@ -0,0 +1,81 @@ +# mediaDownloader Content Plugin + +The `mediaDownloader` plugin downloads media assets (like images and videos) referenced in your content and stores them locally. This is useful for ensuring media availability and optimizing load times. + +Downloaded media files are colocated with the sources that reference them. + +## Usage + +To use the `mediaDownloader` plugin, include it in the list of content plugins in your configuration: + +```typescript{1,6-8} +import { mediaDownloader } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + mediaDownloader({ + maxConcurrent: 4 + }) + ] + } +}); +``` + +## Options + +### `keys` + +- **Type:** `string[]` +- **Default:** `undefined` + +Specifies which data keys to search for media URLs. If not provided, all keys will be searched. + +### `mediaPattern` + +- **Type:** `RegExp` +- **Default:** `/\.(jpe?g|png|webp|avi|mov|mp4|mpg|mpeg|webm)$/i` + +Regex pattern to match URLs for downloading. + +### `matchPath` + +- **Type:** `string` +- **Default:** `undefined` + +JSONPath-Plus compatible path to match URLs. Overrides `mediaPattern` if provided. + +### `maxConcurrent` + +- **Type:** `number` +- **Default:** `4` + +Number of concurrent downloads allowed. + +### `ignoreCache` + +- **Type:** `boolean` +- **Default:** `false` + +If true, always downloads files regardless of cache status. + +### `enableIfModifiedSinceCheck` + +- **Type:** `boolean` +- **Default:** `true` + +Enables HTTP if-modified-since check for cached files. + +### `maxTimeout` + +- **Type:** `number` +- **Default:** `10000` + +Maximum timeout (in milliseconds) for HTTP requests. + +### `updatePaths` + +- **Type:** `boolean` +- **Default:** `true` + +Updates downloaded media URLs in content to point to local paths. Required for using the 'sharp' plugin. diff --git a/docs/src/reference/content/plugins/sanity-image-url-transform.md b/docs/src/reference/content/plugins/sanity-image-url-transform.md new file mode 100644 index 00000000..7c51db0e --- /dev/null +++ b/docs/src/reference/content/plugins/sanity-image-url-transform.md @@ -0,0 +1,79 @@ +# sanityImageUrlTransform Content Plugin + +The `sanityImageUrlTransform` plugin transforms Sanity image references into usable URLs. It can apply image transformations like resizing, cropping, and format conversion using Sanity's image URL builder. + +## Usage + +To use the `sanityImageUrlTransform` plugin, include it in your configuration before your `mediaDownloader` plugin: + +```typescript{1,6-12} +import { sanityImageUrlTransform } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + sanityImageUrlTransform({ + projectId: 'your-project-id', + dataset: 'production', + buildUrl: (builder) => builder + .width(800) + .format('webp') + }), + mediaDownloader({ + //... + }) + ] + } +}); +``` + +## Options + +### `projectId` + +- **Type:** `string` +- **Required** + +Your Sanity project ID. + +### `dataset` + +- **Type:** `string` +- **Default:** `"production"` + +The Sanity dataset to use. + +### `apiToken` + +- **Type:** `string` +- **Optional** + +Sanity API token, required if accessing a private dataset. + +### `path` + +- **Type:** `string` +- **Default:** `'$..*[?(@._type=="image")]'` + +JSONPath to the content to transform. By default, matches all nodes with `_type` of "image". + +### `buildUrl` + +- **Type:** `(builder: ImageUrlBuilder) => ImageUrlBuilder` +- **Required** + +Function to configure image transformations using Sanity's image URL builder. + +### `newProperty` + +- **Type:** `string` +- **Default:** `"transformedUrl"` + +The property name where the transformed URL will be stored. + +### `keys` + +- **Type:** `string[]` +- **Optional** + +Specific data keys to transform. If not provided, transforms all keys. diff --git a/docs/src/reference/content/plugins/sanity-to-html.md b/docs/src/reference/content/plugins/sanity-to-html.md new file mode 100644 index 00000000..ba18d264 --- /dev/null +++ b/docs/src/reference/content/plugins/sanity-to-html.md @@ -0,0 +1,37 @@ +# sanityToHtml Content Plugin + +The `sanityToHtml` plugin is used to transform Sanity.io Portable Text content into HTML. It converts block content from Sanity's structured format into standard HTML markup. + +## Usage + +To use the `sanityToHtml` plugin, include it in the list of content plugins in your configuration: + +```typescript{1,6-8} +import { sanityToHtml } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + sanityToHtml({ + path: '$.item.content' + }) + ] + } +}); +``` + +## Options + +### `path` + +- **Type:** `string` +- **Required** + +Specifies the JSONPath to the Sanity Portable Text content that needs to be transformed to HTML. + +### `keys` + +- **Type:** `DataKeys` +- **Default:** `undefined` + +Specifies the data keys to which the transformation should be applied. If not provided, the transformation will be applied to all keys. diff --git a/docs/src/reference/content/plugins/sanity-to-md.md b/docs/src/reference/content/plugins/sanity-to-md.md new file mode 100644 index 00000000..8d254acc --- /dev/null +++ b/docs/src/reference/content/plugins/sanity-to-md.md @@ -0,0 +1,37 @@ +# sanityToMd Content Plugin + +The `sanityToMd` plugin is used to transform Sanity.io Portable Text content into Markdown. It converts block content from Sanity's structured format into standard Markdown syntax. + +## Usage + +To use the `sanityToMd` plugin, include it in the list of content plugins in your configuration: + +```typescript{1,6-8} +import { sanityToMd } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + sanityToMd({ + path: '$.item.content' + }) + ] + } +}); +``` + +## Options + +### `path` + +- **Type:** `string` +- **Required** + +Specifies the JSONPath to the Sanity Portable Text content that needs to be transformed to Markdown. + +### `keys` + +- **Type:** `DataKeys` +- **Default:** `undefined` + +Specifies the data keys to which the transformation should be applied. If not provided, the transformation will be applied to all keys. diff --git a/docs/src/reference/content/plugins/sanity-to-plain.md b/docs/src/reference/content/plugins/sanity-to-plain.md new file mode 100644 index 00000000..5953f57e --- /dev/null +++ b/docs/src/reference/content/plugins/sanity-to-plain.md @@ -0,0 +1,37 @@ +# sanityToPlain Content Plugin + +The `sanityToPlain` plugin is used to transform Sanity.io Portable Text content into plain text. It extracts text content from Sanity's structured format, removing any markup or formatting. + +## Usage + +To use the `sanityToPlain` plugin, include it in the list of content plugins in your configuration: + +```typescript{1,6-8} +import { sanityToPlain } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + sanityToPlain({ + path: '$.item.content' + }) + ] + } +}); +``` + +## Options + +### `path` + +- **Type:** `string` +- **Required** + +Specifies the JSONPath to the Sanity Portable Text content that needs to be transformed to plain text. + +### `keys` + +- **Type:** `DataKeys` +- **Default:** `undefined` + +Specifies the data keys to which the transformation should be applied. If not provided, the transformation will be applied to all keys. diff --git a/docs/src/reference/content/plugins/sharp.md b/docs/src/reference/content/plugins/sharp.md new file mode 100644 index 00000000..a2ac9e6d --- /dev/null +++ b/docs/src/reference/content/plugins/sharp.md @@ -0,0 +1,69 @@ +# Sharp Content Plugin + +The `sharp` plugin is used to transform downloaded images using the [Sharp](https://sharp.pixelplumbing.com/) image processing library. It can resize, format convert, and apply various transformations to your images. + +## Usage + +To use the `sharp` plugin, include it in the list of content plugins after the mediaDownloader in your configuration: + +```typescript{1,7-12} +import { sharp } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + plugins: [ + mediaDownloader({}), + sharp({ + buildTransform: (transform) => transform + .resize(800, 600) + .jpeg({ quality: 80 }), + updateURLs: true + }) + ] + } +}); +``` + +## Options + +### `buildTransform` + +- **Type:** `(transform: Sharp) => Sharp` +- **Required** + +A function that takes a Sharp instance and returns a transformed Sharp instance. This is where you define the image transformations to apply. + +### `mediaPattern` + +- **Type:** `RegExp` +- **Default:** `/\.(jpe?g|png|webp|tiff|gif|svg)$/i` + +Regex pattern to match image files that should be transformed. + +### `matchPath` + +- **Type:** `string` +- **Optional** + +JSONPath-Plus compatible path to match images to transform. Overrides `mediaPattern` if provided. + +### `updateURLs` + +- **Type:** `boolean` +- **Default:** `false` + +When true, updates URLs in the content to point to the transformed images. Note: if you have multiple transforms targeting the same image, you should keep this as false. + +### `keys` + +- **Type:** `string[]` +- **Optional** + +Specifies which data keys to transform. If not provided, all keys will be searched for images. + +### `concurrency` + +- **Type:** `number` +- **Default:** `4` + +The number of images to transform concurrently. diff --git a/docs/src/reference/content/sources/airtable-source.md b/docs/src/reference/content/sources/airtable-source.md new file mode 100644 index 00000000..e8caa1b9 --- /dev/null +++ b/docs/src/reference/content/sources/airtable-source.md @@ -0,0 +1,83 @@ +# Airtable Content Source + +The `airtableSource` content source is used to fetch data from Airtable. It supports fetching data from specified tables and views, and can transform the data into a simplified format. + +## Usage + +To use the `airtableSource` content source, include it in the list of content sources in your configuration: + +```typescript{1,6-12} +import { airtableSource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + airtableSource({ + id: 'myAirtableSource', + baseId: 'appXXXXXXXXXXXXXX', + apiKey: 'keyXXXXXXXXXXXXXX', + tables: ['Table1', 'Table2'], + keyValueTables: ['Settings'] + }) + ] + } +}); +``` + +## Options + +### `id` + +- **Type:** `string` +- **Required** + +Specifies the unique identifier for this source. This will be used as the download path. + +### `baseId` + +- **Type:** `string` +- **Required** + +Specifies the Airtable base ID. See [Airtable documentation](https://help.appsheet.com/en/articles/1785063-using-data-from-airtable#:~:text=To%20obtain%20the%20ID%20of,API%20page%20of%20the%20base) for more details on how to obtain this ID. + +### `defaultView` + +- **Type:** `string` +- **Default:** `'Grid view'` + +Specifies the table view to select for syncing by default. + +### `tables` + +- **Type:** `string[]` +- **Default:** `[]` + +Specifies the tables you want to fetch from. + +### `keyValueTables` + +- **Type:** `string[]` +- **Default:** `[]` + +As a convenience feature, you can store tables listed here as key/value pairs. Field names should be `key` and `value`. + +### `endpointUrl` + +- **Type:** `string` +- **Default:** `'https://api.airtable.com'` + +Specifies the API endpoint to use for Airtable. + +### `appendLocalAttachmentPaths` + +- **Type:** `boolean` +- **Default:** `true` + +Appends the local path of attachments to the saved JSON. + +### `apiKey` + +- **Type:** `string` +- **Required** + +Specifies the Airtable API Key. diff --git a/docs/src/reference/content/sources/contentful-source.md b/docs/src/reference/content/sources/contentful-source.md new file mode 100644 index 00000000..aba210dc --- /dev/null +++ b/docs/src/reference/content/sources/contentful-source.md @@ -0,0 +1,112 @@ +# Contentful Content Source + +The `contentfulSource` content source is used to fetch entries and assets from Contentful. It supports both published content (using the Content Delivery API) and draft content (using the Preview API), with built-in pagination handling. + +## Usage + +To use the `contentfulSource` content source, include it in the list of content sources in your configuration: + +```typescript{1,6-13} +import { contentfulSource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + contentfulSource({ + id: 'myContentfulSource', + space: 'spaceXXXXXXXXXXXX', + deliveryToken: 'your-delivery-token', + previewToken: 'your-preview-token', // Optional + contentTypes: ['article', 'page'], // Optional + usePreviewApi: false // Optional + }) + ] + } +}); +``` + +## Options + +### `id` + +- **Type:** `string` +- **Required** + +Specifies the unique identifier for this source. This will be used as the download path. + +### `space` + +- **Type:** `string` +- **Required** + +Your Contentful space ID. + +### `deliveryToken` + +- **Type:** `string` +- **Required** (unless using Preview API exclusively) + +Content delivery token used to access published content. + +### `previewToken` + +- **Type:** `string` +- **Required** if `usePreviewApi` is true + +Content preview token used to access draft/unpublished content. + +### `usePreviewApi` + +- **Type:** `boolean` +- **Default:** `false` + +Set to true to use the Preview API instead of the Content Delivery API. Requires `previewToken` to be set. + +### `contentTypes` + +- **Type:** `string[]` +- **Default:** `[]` + +Optionally limit queries to specific content types. This will also apply to linked assets. Types that link to other types will include up to 10 levels of child content. + +### `locale` + +- **Type:** `string` +- **Default:** `'en-US'` + +Used to pull localized content. + +### `filename` + +- **Type:** `string` +- **Default:** `'content.json'` + +The filename where content (entries and assets metadata) will be stored. + +### `protocol` + +- **Type:** `string` +- **Default:** `'https'` + +The protocol to use for API requests. + +### `host` + +- **Type:** `string` +- **Default:** `'cdn.contentful.com'` or `'preview.contentful.com'` if `usePreviewApi` is true + +The API host to use for requests. + +### `searchParams` + +- **Type:** `Record` +- **Default:** + +```typescript +{ + limit: 1000, + include: 10 +} +``` + +Additional search parameters to pass to the Contentful API. Supports all parameters from the [Contentful Content Delivery API](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters). diff --git a/docs/src/reference/content/sources/index.md b/docs/src/reference/content/sources/index.md new file mode 100644 index 00000000..d5be4a9c --- /dev/null +++ b/docs/src/reference/content/sources/index.md @@ -0,0 +1,66 @@ +# Content Sources + +Content sources are used to fetch content from various external systems or APIs. These sources define how and where to retrieve content, which is then processed and transformed by content plugins. + +## Type Reference + +```typescript +type ContentSource = { + id: string; + fetch: (context: FetchContext) => SourceFetchResult | SourceFetchResult[]; +}; +``` + +## Properties + +### `id` + +The unique identifier for this source. The documents for this source will be written to a subdirectory with this name in the configured download directory. + +### `fetch` + +A sync callback for fetching the source data. Returns either a single document instance, or an array of documents. + +## Fetch Context + +### `logger` + +A logger instance for logging messages and errors during the fetch process. + +### `dataStore` + +A data store instance for storing and retrieving fetched content. This is useful if one source needs to reference the data from a prior source. Ex: fetching shopify data based on the products referenced in a sanity api call. + +## Source Fetch Result + +### `id` + +- **Type:** `string` + +The unique identifier for the fetched document. + +### `data` + +- **Type:** `Promise | AsyncIterable` + +The fetched data, either as a promise returning a single document or an async iterable returning multiple documents. If it's a promise, it will be written to a single json file. If it's an async iterable, each yield will be written to a separate json file with a index suffix (ie `data-001.json`). + +## Example + +To define a custom content source, use the `defineSource` function: + +```typescript +import { defineSource } from '@bluecadet/launchpad-content'; + +export default defineSource({ + id: 'myCustomSource', + fetch: (context) => { + return { + id: 'documentId', + data: fetchDataFromAPI(), + }; + }, +}); +``` + +For detailed source configuration options, see the specific source documentation. diff --git a/docs/src/reference/content/sources/json-source.md b/docs/src/reference/content/sources/json-source.md new file mode 100644 index 00000000..cb596f5b --- /dev/null +++ b/docs/src/reference/content/sources/json-source.md @@ -0,0 +1,60 @@ +# JSON Content Source + +The `jsonSource` content source is used to fetch data from JSON endpoints via HTTP(S). It supports fetching multiple JSON files from different URLs and saving them with custom identifiers. + +## Usage + +To use the `jsonSource` content source, include it in the list of content sources in your configuration: + +```typescript{1,6-13} +import { jsonSource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + jsonSource({ + id: 'myJsonSource', + files: { + 'data1': 'https://api.example.com/data1.json', + 'data2': 'https://api.example.com/data2.json' + }, + maxTimeout: 60000 + }) + ] + } +}); +``` + +## Options + +### `id` + +- **Type:** `string` +- **Required** + +Specifies the unique identifier for this source. This will be used as the download path. + +### `files` + +- **Type:** `Record` +- **Required** + +A mapping of JSON keys to URLs. Each key will be used as the identifier for the downloaded JSON file, while the corresponding URL specifies where to fetch the JSON data from. + +For example: + +```typescript +{ + 'settings': 'https://api.example.com/settings.json', + 'users': 'https://api.example.com/users.json' +} +``` + +This will create files named `settings.json` and `users.json` in the output directory. + +### `maxTimeout` + +- **Type:** `number` +- **Default:** `30000` + +Specifies the maximum time (in milliseconds) to wait for a response from each JSON endpoint before timing out. The default is 30 seconds. diff --git a/docs/src/reference/content/sources/sanity-source.md b/docs/src/reference/content/sources/sanity-source.md new file mode 100644 index 00000000..a9e08dd4 --- /dev/null +++ b/docs/src/reference/content/sources/sanity-source.md @@ -0,0 +1,103 @@ +# Sanity Content Source + +The `sanitySource` content source is used to fetch data from Sanity.io. It supports fetching data using GROQ queries and can handle pagination of large datasets. + +## Usage + +To use the `sanitySource` content source, include it in the list of content sources in your configuration: + +```typescript{1,6-15} +import { sanitySource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + sanitySource({ + id: 'mySanitySource', + projectId: 'your-project-id', + dataset: 'production', + apiToken: 'your-api-token', // Required for private datasets + queries: [ + 'project', // Fetches all documents of type 'project' + { id: 'featured', query: '*[_type == "article" && featured == true]' } + ] + }) + ] + } +}); +``` + +## Options + +### `id` + +- **Type:** `string` +- **Required** + +Specifies the unique identifier for this source. This will be used as the download path. + +### `projectId` + +- **Type:** `string` +- **Required** + +Your Sanity project ID. + +### `dataset` + +- **Type:** `string` +- **Default:** `'production'` + +The name of the dataset you want to fetch from. + +### `apiToken` + +- **Type:** `string` +- **Optional** + +Sanity API token. Required if you're accessing a private dataset. + +### `apiVersion` + +- **Type:** `string` +- **Default:** `'v2021-10-21'` + +The Sanity API version to use. + +### `queries` + +- **Type:** `Array` +- **Required** + +An array of queries to fetch. Each query can be either: + +- A string representing a document type (e.g., `'article'` will fetch all documents of type 'article') +- An object with a custom GROQ query and an ID for the result set + +### `useCdn` + +- **Type:** `boolean` +- **Default:** `true` + +Set to `false` if you want to ensure fresh data instead of potentially cached responses. + +### `limit` + +- **Type:** `number` +- **Default:** `100` + +Maximum number of documents to fetch per page. + +### `maxNumPages` + +- **Type:** `number` +- **Default:** `1000` + +Maximum number of pages to fetch. + +### `mergePages` + +- **Type:** `boolean` +- **Default:** `false` + +When `true`, combines all paginated results into a single file. When `false`, each page is stored separately. diff --git a/docs/src/reference/content/sources/strapi-source.md b/docs/src/reference/content/sources/strapi-source.md new file mode 100644 index 00000000..691d92c3 --- /dev/null +++ b/docs/src/reference/content/sources/strapi-source.md @@ -0,0 +1,101 @@ +# Strapi Content Source + +The `strapiSource` content source is used to fetch data from Strapi CMS. It supports both Strapi v3 and v4, with automatic pagination and customizable query parameters. + +## Usage + +To use the `strapiSource` content source, include it in the list of content sources in your configuration: + +```typescript{1,6-13} +import { strapiSource } from '@bluecadet/launchpad-content'; + +export default defineConfig({ + content: { + sources: [ + strapiSource({ + id: 'myStrapiSource', + version: '4', + baseUrl: 'http://localhost:1337', + identifier: 'admin@example.com', + password: 'your-password', + queries: ['api/articles', 'api/categories'] + }) + ] + } +}); +``` + +## Options + +### `id` + +- **Type:** `string` +- **Required** + +Specifies the unique identifier for this source. This will be used as the download path. + +### `version` + +- **Type:** `"3" | "4"` +- **Default:** `"3"` + +Specifies the Strapi version. Supports either version 3 or 4. + +### `baseUrl` + +- **Type:** `string` +- **Required** + +The base URL of your Strapi CMS (with or without trailing slash). + +### `queries` + +- **Type:** `(string | { contentType: string, params: Record })[]` +- **Required** + +Queries for each type of content you want to fetch. You can specify either: +- A string URL path (e.g. `"api/articles"`) +- An object with `contentType` and `params` for more control over the query parameters + +### `identifier` + +- **Type:** `string` +- **Required if token not provided** + +Username or email for authentication. Should be configured via environment variables. + +### `password` + +- **Type:** `string` +- **Required if token not provided** + +Password for authentication. Should be configured via environment variables. + +### `token` + +- **Type:** `string` +- **Required if identifier/password not provided** + +A previously generated JWT token for authentication. + +### `limit` + +- **Type:** `number` +- **Default:** `100` + +Maximum number of entries to fetch per page. + +### `maxNumPages` + +- **Type:** `number` +- **Default:** `1000` + +Maximum number of pages to fetch. + +### `pageNumZeroPad` + +- **Type:** `number` +- **Default:** `2` + +Number of zeros to pad each JSON filename index with. + diff --git a/docs/src/reference/monitor/index.md b/docs/src/reference/monitor/index.md new file mode 100644 index 00000000..9e78305a --- /dev/null +++ b/docs/src/reference/monitor/index.md @@ -0,0 +1,86 @@ +# @Bluecadet/Launchpad-Monitor + +The monitor package is a robust process management and monitoring tool designed for media installations. It provides comprehensive control over application lifecycles, window management, and system monitoring. + +## Features + +- **Process Management**: Complete control over application lifecycles: + - Launch and monitor multiple applications + - Automatic crash recovery + - Graceful shutdowns and restarts + - PM2 integration for advanced process control + +- **Window Management**: Powerful window control capabilities: + - Move windows to foreground/background + - Minimize/maximize operations + - Window visibility control + +- **Logging System**: Comprehensive logging features: + - Centralized log collection + - Configurable log routing + - stdout/stderr capture + - Log file management + - Debug and error tracking + +- **Event System**: Flexible event handling: + - Process lifecycle hooks + - Plugin event integration + +## Installation + +```bash +npm install @bluecadet/launchpad-monitor +``` + +## Basic Usage + +```typescript +import LaunchpadMonitor from '@bluecadet/launchpad-monitor'; + +const monitor = new LaunchpadMonitor({ + apps: [ + { + name: "my-app", + pm2: { + script: "./app.js" + }, + windows: { + foreground: true + } + } + ] +}); + +// Start monitoring +await monitor.start(); +``` + +## Configuration + +Monitor operations are configured through a `MonitorConfig` object that specifies: + +- **Apps**: Array of applications to manage +- **Process Settings**: PM2 configuration options +- **Window Management**: Window behavior settings +- **Logging Options**: Log handling preferences + +See the [Monitor Config](./monitor-config) section for detailed configuration options. + +## Error Handling + +The package uses `neverthrow` for reliable error handling: + +- Type-safe error management +- Graceful failure recovery +- Clear error reporting +- Process recovery strategies + +## Plugin Support + +The monitor package supports plugins for extending functionality: + +- Custom process management +- Enhanced window control +- Additional monitoring capabilities +- Custom event handling +- Integration with other systems diff --git a/docs/src/reference/monitor/monitor-config.md b/docs/src/reference/monitor/monitor-config.md new file mode 100644 index 00000000..4b4e1da0 --- /dev/null +++ b/docs/src/reference/monitor/monitor-config.md @@ -0,0 +1,75 @@ +# Monitor Config + +Configuration for managing process monitoring, window management, and logging settings. + +## Options + +### `apps` + +- **Type:** `Array` +- **Default:** `[]` + +A list of apps to launch and monitor. Each app can be configured with PM2 settings, window management options, and logging preferences. + +For detailed app configuration options, see [App Config](#app-config). + +### `deleteExistingBeforeConnect` + +- **Type:** `boolean` +- **Default:** `false` + +When enabled, deletes existing PM2 processes before connecting. Useful for volatile apps or when node processes might quit unexpectedly, ensuring a clean slate on startup. + +### `windowsApi` + +- **Type:** `WindowsApiConfig` +- **Default:** `{}` + +Advanced configuration for the Windows API, used for managing foreground/minimized/hidden windows. + +### `plugins` + +- **Type:** `Array` +- **Default:** `[]` + +A list of monitor plugins for extending functionality. + +### `shutdownOnExit` + +- **Type:** `boolean` +- **Default:** `true` + +When enabled, listens for exit events to handle graceful shutdown. + +## App Config + +Each app in the `apps` array can have the following configuration: + +### `pm2` + +- **Type:** `pm2.StartOptions` + +PM2 configuration for the app. See [PM2 documentation](https://pm2.keymetrics.io/docs/usage/application-declaration/#attributes-available) for available options. + +### `windows` + +- **Type:** `WindowConfig` +- **Default:** `{}` + +Settings for window management: + +- `foreground`: Move to foreground after launch +- `minimize`: Minimize windows after launch +- `hide`: Hide windows after launch + +### `logging` + +- **Type:** `AppLogConfig` +- **Default:** `{}` + +Settings for log management: + +- `logToLaunchpadDir`: Route logs to launchpad's directory +- `mode`: Log capture method ('bus' or 'file') +- `showStdout`: Include stdout output +- `showStderr`: Include stderr output diff --git a/docs/src/reference/monitor/plugins.md b/docs/src/reference/monitor/plugins.md new file mode 100644 index 00000000..b4ff1aac --- /dev/null +++ b/docs/src/reference/monitor/plugins.md @@ -0,0 +1,101 @@ +# Monitor Plugins + +Monitor plugins are used to extend and enhance the process management and monitoring capabilities. These plugins can handle various aspects of process lifecycle, logging, and error management. + +Monitor plugins can be used for a variety of tasks, including but not limited to: + +- **Process Management**: Handling process lifecycle events like start, stop, and restart. +- **Error Handling**: Managing and responding to process errors and exceptions. +- **Log Management**: Processing and routing application logs and error messages. +- **Status Monitoring**: Tracking process health and performance metrics. +- **Event Handling**: Responding to PM2 bus events and system signals. + +By leveraging monitor plugins and their hooks, developers can create flexible and powerful process management solutions that meet the specific needs of their applications. + +## Type Reference + +```typescript +type MonitorPlugin = { + name: string; + hooks: { + beforeConnect?: (ctx: CombinedMonitorHookContext) => void | PromiseLike; + afterConnect?: (ctx: CombinedMonitorHookContext) => void | PromiseLike; + beforeDisconnect?: (ctx: CombinedMonitorHookContext) => void | PromiseLike; + afterDisconnect?: (ctx: CombinedMonitorHookContext) => void | PromiseLike; + beforeAppStart?: (ctx: CombinedMonitorHookContext, arg: { appName: string }) => void | PromiseLike; + afterAppStart?: (ctx: CombinedMonitorHookContext, arg: { appName: string; process: pm2.ProcessDescription }) => void | PromiseLike; + beforeAppStop?: (ctx: CombinedMonitorHookContext, arg: { appName: string }) => void | PromiseLike; + afterAppStop?: (ctx: CombinedMonitorHookContext, arg: { appName: string }) => void | PromiseLike; + onAppError?: (ctx: CombinedMonitorHookContext, arg: { appName: string; error: Error }) => void | PromiseLike; + onAppLog?: (ctx: CombinedMonitorHookContext, arg: { appName: string; data: string }) => void | PromiseLike; + onAppErrorLog?: (ctx: CombinedMonitorHookContext, arg: { appName: string; data: string }) => void | PromiseLike; + beforeShutdown?: (ctx: CombinedMonitorHookContext, arg: { code?: number }) => void | PromiseLike; + } +}; +``` + +## Hooks + +### `beforeConnect`/`afterConnect` + +**When:** Called before/after connecting to PM2. + +**Why:** These hooks allow plugins to perform setup/cleanup tasks when the monitor connects to PM2. + +### `beforeDisconnect`/`afterDisconnect` + +**When:** Called before/after disconnecting from PM2. + +**Why:** These hooks enable plugins to handle cleanup or state management during PM2 disconnection. + +### `beforeAppStart`/`afterAppStart` + +**When:** Called before/after starting an application. + +**Why:** These hooks allow plugins to perform setup tasks or respond to successful app launches. + +### `beforeAppStop`/`afterAppStop` + +**When:** Called before/after stopping an application. + +**Why:** These hooks enable plugins to handle cleanup or state management during app shutdown. + +### `onAppError` + +**When:** Called when an application encounters an error. + +**Why:** This hook allows plugins to handle and respond to application errors. + +### `onAppLog`/`onAppErrorLog` + +**When:** Called when an application outputs standard or error logs. + +**Why:** These hooks enable plugins to process, route, or respond to application logs. + +### `beforeShutdown` + +**When:** Called before the monitor shuts down. + +**Why:** This hook allows plugins to perform cleanup tasks before shutdown. + +## Monitor Plugin Context + +```typescript +type CombinedMonitorHookContext = { + monitor: LaunchpadMonitor; + logger: Logger; + abortSignal: AbortSignal; +}; +``` + +### `monitor` + +Access to the monitor instance, providing access to process management and monitoring capabilities. + +### `logger` + +A plugin-specific logger for recording events and errors. + +### `abortSignal` + +Signals when the monitor process is shutting down, allowing plugins to handle cleanup. diff --git a/docs/src/reference/scaffold/index.md b/docs/src/reference/scaffold/index.md new file mode 100644 index 00000000..c4816be7 --- /dev/null +++ b/docs/src/reference/scaffold/index.md @@ -0,0 +1,79 @@ +# @Bluecadet/Launchpad-Scaffold + +The scaffold package is a specialized toolset for configuring Windows systems in exhibit and kiosk environments. It provides automated setup and optimization features to prepare Windows machines for reliable, long-term operation. + +## Features + +- **Windows Kiosk Setup**: + - Automate Windows configuration for kiosk mode + - Configure auto-login and startup applications + - Disable unnecessary Windows features + - Optimize interface for touch and kiosk usage + +- **System Optimization**: + - Power settings configuration + - Windows Update management + - Service optimization + - Performance tuning + - User interface customization + +- **Security Controls**: + - System policy management + - Interface lockdown options + +## Installation + +```bash +npm install @bluecadet/launchpad-scaffold +``` + +## Basic Usage + +```typescript +import { launchScaffold } from '@bluecadet/launchpad-scaffold'; +import { LogManager } from '@bluecadet/launchpad-utils'; + +// instantiate the logger before starting the scaffold process +const logger = LogManager.getLogger('my-app'); + +// Launch the scaffold setup process +await launchScaffold(logger); +``` + +## System Requirements + +- Windows 10 or Windows 11 +- Administrative privileges +- PowerShell execution enabled +- Node.js 18 or higher + +## Configuration + +The scaffold package uses a combination of: + +- PowerShell scripts for system configuration +- Batch files for process execution +- Node.js for orchestration +- Windows Registry modifications +- System policy updates + +## Security Considerations + +- Requires elevated privileges +- Modifies system settings +- Changes Windows configurations +- Alters user permissions + +## Limitations + +- Windows-only support +- Some features require specific Windows versions +- Certain settings may require additional manual configuration +- Windows 11 has limited support for some kiosk features + +## Error Handling + +- Provides detailed logs of all operations +- Fails gracefully with clear error messages +- Supports rollback of critical changes +- Includes diagnostic information for troubleshooting diff --git a/docs/src/reference/scaffold/scaffold-config.md b/docs/src/reference/scaffold/scaffold-config.md new file mode 100644 index 00000000..7a727816 --- /dev/null +++ b/docs/src/reference/scaffold/scaffold-config.md @@ -0,0 +1,82 @@ +# Scaffold Config + +The scaffold configuration controls how Windows systems are configured for exhibit and kiosk environments. It provides extensive customization options for system settings, application installation, startup behaviors, and Windows optimizations. + +When you run the scaffold process, you'll be presented with an option to edit the default config before it's applied. + +## Example Config + +```powershell + +if (!$global:LaunchpadConfig) { $global:LaunchpadConfig = @{} } +if (!$global:LaunchpadConfig.Computer) { $global:LaunchpadConfig.Computer = @{} } +if (!$global:LaunchpadConfig.InstallApps) { $global:LaunchpadConfig.InstallApps = @{} } +if (!$global:LaunchpadConfig.Windows) { $global:LaunchpadConfig.Windows = @{} } +if (!$global:LaunchpadConfig.Windows.Windows8) { $global:LaunchpadConfig.Windows.Windows8 = @{} } +if (!$global:LaunchpadConfig.Exhibit) { $global:LaunchpadConfig.Exhibit = @{} } + +# Set this to false if you want to automatically run scripts without prompting. +$global:LaunchpadConfig.ConfirmAllScripts = $true; + +# Computer config +$global:LaunchpadConfig.Computer.ComputerName = hostname # Leave as hostname to use the current computer name +$global:LaunchpadConfig.Computer.WindowsUsername = [Environment]::UserName # Leave as [Environment]::UserName to sue the current user name +$global:LaunchpadConfig.Computer.WindowsPassword = "" +$global:LaunchpadConfig.Computer.PowerConfig = "$PSScriptRoot\presets\exhibit_power_config.pow" + +# Install start up tasks to launch your app. Paths are relative to setup.bat +$global:LaunchpadConfig.Computer.TaskSchedulerPath = "\Exhibit" # The path where startup scripts will be stored +$global:LaunchpadConfig.Computer.StartupWorkingDir = "$PSScriptRoot\..\..\..\..\..\..\" # Working dir of the startup action +$global:LaunchpadConfig.Computer.StartupCreateBat = $true # Create a bat file to launch at startup +$global:LaunchpadConfig.Computer.StartupBat = "launch.bat" # The filename used for StartupCreateBat +$global:LaunchpadConfig.Computer.StartupBatContent = "npx launchpad" # The contents of the startup bat file +$global:LaunchpadConfig.Computer.StartupAction = $global:LaunchpadConfig.Computer.StartupBat # The action to run at startup +$global:LaunchpadConfig.Computer.StartupDelay = "PT3M" # PT3M = 3 min, PT4M = 4 min, ... (stands for poll time X minutes) +$global:LaunchpadConfig.Computer.Timezone = "Eastern Standard Time" +$global:LaunchpadConfig.Computer.RebootTime = "3:00" + +# App installs +$global:LaunchpadConfig.InstallApps.Enabled = $true # Install Chocolatey; Required for any of the below app packages +$global:LaunchpadConfig.InstallApps.Apps = "nvm", "python", "vscode", "github-desktop", "git", "cmake", "visualstudio2022buildtools", "visualstudio2022-workload-nativedesktop", "visualstudio2022-workload-vctools" + +$global:LaunchpadConfig.InstallApps.InstallNodeDependencies = $true # Installs Launchpad dependencies + +# Computer/User Settings +$global:LaunchpadConfig.Windows.SetComputerName = $false +$global:LaunchpadConfig.Windows.SetPowerSettings = $true +$global:LaunchpadConfig.Windows.EnableAutoLogin = $false +$global:LaunchpadConfig.Windows.SetTimzone = $false +$global:LaunchpadConfig.Windows.EnableDailyReboot = $false +$global:LaunchpadConfig.Windows.EnableStartupTask = $true + +# Windows Settings +$global:LaunchpadConfig.Windows.ClearDesktopBackground = $true +$global:LaunchpadConfig.Windows.ClearDesktopShortcuts = $true +$global:LaunchpadConfig.Windows.ConfigureExplorer = $true +$global:LaunchpadConfig.Windows.DisableAccessibility = $true +$global:LaunchpadConfig.Windows.DisableAppInstalls = $true +$global:LaunchpadConfig.Windows.DisableAppRestore = $true +$global:LaunchpadConfig.Windows.DisableCortanaSearch = $true +$global:LaunchpadConfig.Windows.DisableEdgeSwipes = $true +$global:LaunchpadConfig.Windows.DisableErrorReporting = $true +$global:LaunchpadConfig.Windows.DisableFireWall = $false +$global:LaunchpadConfig.Windows.DisableMaxPathLength = $true +$global:LaunchpadConfig.Windows.DisableNewNetworkWindow = $true +$global:LaunchpadConfig.Windows.DisableNewsAndInterests = $true +$global:LaunchpadConfig.Windows.DisableNotifications = $true +$global:LaunchpadConfig.Windows.DisableScreensaver = $true +$global:LaunchpadConfig.Windows.DisableTouchFeedback = $true +$global:LaunchpadConfig.Windows.DisableTouchGestures = $true +$global:LaunchpadConfig.Windows.DisableUpdateCheck = $true +$global:LaunchpadConfig.Windows.DisableUpdateService = $true +$global:LaunchpadConfig.Windows.DisableWinSetupPrompt = $true +$global:LaunchpadConfig.Windows.EnableScriptExecution = $true +$global:LaunchpadConfig.Windows.ResetTextScale = $true +$global:LaunchpadConfig.Windows.UninstallBloatware = $true +$global:LaunchpadConfig.Windows.UninstallOneDrive = $true +$global:LaunchpadConfig.Windows.UnpinStartMenuApps = $true + +# Windows 8 Specific +$global:LaunchpadConfig.Windows.Windows8.DisableStartPage = $true + +``` diff --git a/package-lock.json b/package-lock.json index d56e958d..56d4cb57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9088,10 +9088,10 @@ }, "packages/cli": { "name": "@bluecadet/launchpad-cli", - "version": "2.0.0-next.2", + "version": "2.0.0-next.3", "license": "ISC", "dependencies": { - "@bluecadet/launchpad-utils": "~2.0.0-next.3", + "@bluecadet/launchpad-utils": "~2.0.0-next.4", "chalk": "^5.0.0", "dotenv": "^16.4.5", "neverthrow": "^8.1.1", @@ -9110,9 +9110,9 @@ "npm": ">=8.5.1" }, "optionalDependencies": { - "@bluecadet/launchpad-content": "~2.0.0-next.4", + "@bluecadet/launchpad-content": "~2.0.0-next.5", "@bluecadet/launchpad-monitor": "~2.0.0-next.3", - "@bluecadet/launchpad-scaffold": "~2.0.0-next.1" + "@bluecadet/launchpad-scaffold": "~2.0.0-next.2" } }, "packages/cli/node_modules/dotenv": { @@ -9129,13 +9129,12 @@ }, "packages/content": { "name": "@bluecadet/launchpad-content", - "version": "2.0.0-next.4", + "version": "2.0.0-next.5", "license": "ISC", "dependencies": { - "@bluecadet/launchpad-utils": "~2.0.0-next.3", + "@bluecadet/launchpad-utils": "~2.0.0-next.4", "@portabletext/to-html": "2.0.0", "@sanity/block-content-to-markdown": "^0.0.5", - "@sanity/image-url": "*", "chalk": "^5.0.0", "glob": "^11.0.0", "jsonpath-plus": "^10.1.0", @@ -9188,11 +9187,11 @@ "version": "2.0.0-next.2", "license": "ISC", "dependencies": { - "@bluecadet/launchpad-cli": "~2.0.0-next.2", - "@bluecadet/launchpad-content": "~2.0.0-next.4", - "@bluecadet/launchpad-dashboard": "~2.0.0-next.1", - "@bluecadet/launchpad-monitor": "~2.0.0-next.3", - "@bluecadet/launchpad-scaffold": "~2.0.0-next.1" + "@bluecadet/launchpad-cli": "2.0.0-next.2", + "@bluecadet/launchpad-content": "2.0.0-next.4", + "@bluecadet/launchpad-dashboard": "2.0.0-next.1", + "@bluecadet/launchpad-monitor": "2.0.0-next.3", + "@bluecadet/launchpad-scaffold": "2.0.0-next.1" }, "devDependencies": { "@bluecadet/launchpad-tsconfig": "0.1.0" @@ -9202,12 +9201,86 @@ "npm": ">=8.5.1" } }, + "packages/launchpad/node_modules/@bluecadet/launchpad-cli": { + "version": "2.0.0-next.2", + "resolved": "https://registry.npmjs.org/@bluecadet/launchpad-cli/-/launchpad-cli-2.0.0-next.2.tgz", + "integrity": "sha512-A8qCFWkiiYq6slE9GBGyTBe1sUyvpHLhbqeuSpZQIOYYbcRCSlBZxN1e9qE5wRnPQjk47yS91+gmYxDaZCCbTQ==", + "license": "ISC", + "dependencies": { + "@bluecadet/launchpad-utils": "~2.0.0-next.3", + "chalk": "^5.0.0", + "dotenv": "^16.4.5", + "neverthrow": "^8.1.1", + "yargs": "^17.7.2", + "zod": "^3.23.8" + }, + "bin": { + "launchpad": "dist/cli.js" + }, + "engines": { + "node": ">=17.5.0", + "npm": ">=8.5.1" + }, + "optionalDependencies": { + "@bluecadet/launchpad-content": "~2.0.0-next.4", + "@bluecadet/launchpad-monitor": "~2.0.0-next.3", + "@bluecadet/launchpad-scaffold": "~2.0.0-next.1" + } + }, + "packages/launchpad/node_modules/@bluecadet/launchpad-content": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/@bluecadet/launchpad-content/-/launchpad-content-2.0.0-next.4.tgz", + "integrity": "sha512-146pSWlSlr/tkoWHUTQNjkMLpAe5A6BH+dhHypFLV3NqlEabRjWWRlNNXPkfKJRfsZkKwjA9f6PGpny/dXXyCA==", + "license": "ISC", + "dependencies": { + "@bluecadet/launchpad-utils": "~2.0.0-next.3", + "@portabletext/to-html": "2.0.0", + "@sanity/block-content-to-markdown": "^0.0.5", + "chalk": "^5.0.0", + "glob": "^11.0.0", + "jsonpath-plus": "^10.1.0", + "ky": "^1.7.2", + "markdown-it": "^14.1.0", + "neverthrow": "^8.1.1", + "p-queue": "^7.1.0", + "qs": "^6.11.1", + "sanitize-html": "^2.5.1", + "zod": "^3.23.8" + }, + "optionalDependencies": { + "@sanity/client": "^6.4.9", + "airtable": "^0.11.1", + "contentful": "^9.0.0" + } + }, + "packages/launchpad/node_modules/@bluecadet/launchpad-scaffold": { + "version": "2.0.0-next.1", + "resolved": "https://registry.npmjs.org/@bluecadet/launchpad-scaffold/-/launchpad-scaffold-2.0.0-next.1.tgz", + "integrity": "sha512-L62v75v1RSsq1Go0J7bMKKmtLkkgYY2rHF7JXukGJ66cgeYZoUyjwY7vmOi4AjejfxPEXVIcLG7Xp5kaLpMo+w==", + "license": "ISC", + "dependencies": { + "@bluecadet/launchpad-utils": "~2.0.0-next.2", + "sudo-prompt": "^9.2.1" + } + }, + "packages/launchpad/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "packages/monitor": { "name": "@bluecadet/launchpad-monitor", "version": "2.0.0-next.3", "license": "ISC", "dependencies": { - "@bluecadet/launchpad-utils": "~2.0.0-next.3", + "@bluecadet/launchpad-utils": "~2.0.0-next.4", "auto-bind": "^5.0.1", "chalk": "^5.0.0", "cross-spawn": "^7.0.3", @@ -9231,10 +9304,10 @@ }, "packages/scaffold": { "name": "@bluecadet/launchpad-scaffold", - "version": "2.0.0-next.1", + "version": "2.0.0-next.2", "license": "ISC", "dependencies": { - "@bluecadet/launchpad-utils": "~2.0.0-next.2", + "@bluecadet/launchpad-utils": "~2.0.0-next.4", "sudo-prompt": "^9.2.1" }, "devDependencies": { @@ -9260,7 +9333,7 @@ }, "packages/utils": { "name": "@bluecadet/launchpad-utils", - "version": "2.0.0-next.3", + "version": "2.0.0-next.4", "license": "ISC", "dependencies": { "@sindresorhus/slugify": "^2.1.0", diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 00000000..b3bc05f5 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,37 @@ +# @bluecadet/launchpad-cli + +Command line interface for managing media installations with Launchpad. Provides commands for content management, process monitoring, and system configuration. + +## Documentation + +For complete documentation, configuration options, and guides, visit: +[Launchpad Documentation](https://bluecadet.github.io/launchpad/) + +## Installation + +```bash +npm install @bluecadet/launchpad-cli + +# Install additional modules as needed +npm install @bluecadet/launchpad-content @bluecadet/launchpad-monitor +``` + +## Basic Usage + +```bash +# Download content and start apps +npx launchpad start + +# Only download fresh content +npx launchpad content + +# Only manage apps +npx launchpad monitor + +# Stop all processes +npx launchpad stop +``` + +## License + +MIT © Bluecadet diff --git a/packages/cli/src/utils/config.ts b/packages/cli/src/utils/config.ts index d2127aa6..112fc7da 100644 --- a/packages/cli/src/utils/config.ts +++ b/packages/cli/src/utils/config.ts @@ -3,12 +3,7 @@ import path from "node:path"; import url from "node:url"; import chalk from "chalk"; -const DEFAULT_CONFIG_PATHS = [ - "launchpad.config.js", - "launchpad.config.mjs", - "launchpad.json", - "config.json", -]; +const DEFAULT_CONFIG_PATHS = ["launchpad.config.js", "launchpad.config.mjs"]; /** * Searches for a config file in the current and parent directories, up to a max depth of 64. diff --git a/packages/content/README.md b/packages/content/README.md index 8f391b14..fce142fa 100644 --- a/packages/content/README.md +++ b/packages/content/README.md @@ -1,117 +1,47 @@ -# Launchpad Content +# @bluecadet/launchpad-content -The [`content`](/packages/content) package downloads and locally caches content from various common web APIs. +Content management tools for Launchpad interactive installations. Fetch, transform, and manage content from any source with a flexible plugin system. -To download content, all you need to do is define content sources and provide credentials as needed. +## Documentation -The following `launchpad.json` would download jsons and images from the Flickr API to `.downloads/flickr-images` (a combination of the default `.downloads/` directory and the `id` field of the content source): +For complete documentation, configuration options, and guides, visit: +[Launchpad Documentation](https://bluecadet.github.io/launchpad/) -See [ContentOptions Parameters](#contentoptions-parameters) for a full list of content settings. +## Features -## Source Settings +- Fetch content from multiple sources (APIs, CMSs, etc.) +- Transform content with plugins +- Automatic backup and restore +- Error handling and logging +- Media file downloads and processing -Every source needs: +## Installation -- `id`: ID of the individual source (required) -- `type`: Source type (default: `json`) -- Additional source-specific settings - -Currently supported sources are: - -- [`json`](docs/json-source.md) (via HTTP) -- [`airtable`](docs/airtable-source.md) -- [`contentful`](docs/contentful-source.md) -- [`sanity`](docs/sanity-source.md) -- [`strapi`](docs/strapi-source.md) - -## Authentication - -Some content sources require credentials to access their APIs. - -These can all be stored in a `.env` or `.env.local` file which will be automatically loaded by launchpad. - -### `.env.local` - -```sh -AIRTABLE_API_KEY= - -CONTENTFUL_PREVIEW_TOKEN= -CONTENTFUL_DELIVERY_TOKEN= -CONTENTFUL_USE_PREVIEW_API=false - -SANITY_API_TOKEN= - -STRAPI_IDENTIFIER= -STRAPI_PASSWORD= +```bash +npm install @bluecadet/launchpad-content @bluecadet/launchpad-cli ``` -### `launchpad.config.js` +## Basic Usage ```js +// launchpad.config.js +import { defineConfig } from '@bluecadet/launchpad-cli'; +import { jsonSource } from '@bluecadet/launchpad-content'; + export default defineConfig({ - content: { - sources: [ - { - id: "airtable-cms", - type: "airtable", - apiKey: process.env.AIRTABLE_API_KEY, - }, - { - id: "contentful-cms", - type: "contentful", - previewToken: process.env.CONTENTFUL_PREVIEW_TOKEN, - deliveryToken: process.env.CONTENTFUL_DELIVERY_TOKEN, - usePreviewApi: false, - }, - { - id: "sanity-cms", - type: "sanity", - apiToken: process.env.SANITY_API_TOKEN, - }, - { - id: "strapi-cms", - type: "strapi", - identifier: process.env.STRAPI_IDENTIFIER, - }, - ], - }, + content: { + sources: [ + jsonSource({ + id: "api-data", + files: { + "data.json": "https://api.example.com/data" + } + }) + ] + } }); - ``` -## Post Processing - -Once content is downloaded it can be processed to transform text and images. For example, launchpad can convert markdown to html and create scaled derivatives of each image. - -- [Content Transforms Documetation](docs/content-transforms.md) -- [Image Transforms Documetation](docs/image-transforms.md) - - -## ContentOptions -Options for all content and media downloads. Each of these settings can also be configured per `ContentSource`. - - -| Property | Type | Default | Description | -| - | - | - | - | -| `sources` | Array.<SourceOptions>| [] | A list of content source options. This defines which content is downloaded from where. | -| `imageTransforms` | Array.<Object.<string, number>>| [] | A list of image transforms to apply to a copy of each downloaded image. | -| `contentTransforms` | Object.<string, string>| {} | A list of content transforms to apply to all donwloaded content. | -| `downloadPath` | string| '.downloads/' | The path at which to store all downloaded files. | -| `credentialsPath` | string| '.credentials.json' | The path to the json containing credentials for all content sources. | -| `tempPath` | boolean| '%DOWNLOAD\_PATH%/.tmp/' | Temp file directory path. | -| `backupPath` | boolean| '%DOWNLOAD\_PATH%/.backups/' | Temp directory path where all downloaded content will be backed up before removal. | -| `keep` | boolean| '' | Which files to keep in `dest` if `clearOldFilesOnSuccess` or `clearOldFilesOnStart` are `true`. E.g. `'\*.json\|\*.csv\|\*.xml\|\*.git\*'` | -| `strip` | string| '' | Strips this string from all media file paths when saving them locally | -| `backupAndRestore` | boolean| true | Back up files before downloading and restore originals for all sources on failure of any single source. | -| `maxConcurrent` | number| 4 | Max concurrent downloads. | -| `maxTimeout` | number| 30000 | Max request timeout in ms. | -| `clearOldFilesOnSuccess` | boolean| true | Remove all existing files in dest dir when downloads succeed. Ignores files that match `keep` | -| `clearOldFilesOnStart` | boolean| false | Will remove all existing files \_before\_ downloads starts. `false` will ensure that existing files are only deleted after a download succeeds. | -| `ignoreCache` | boolean| false | Will always download files regardless of whether they've been cached | -| `enableIfModifiedSinceCheck` | boolean| true | Enables the HTTP if-modified-since check. Disabling this will assume that the local file is the same as the remote file if it already exists. | -| `enableContentLengthCheck` | boolean| true | Compares the HTTP header content-length with the local file size. Disabling this will assume that the local file is the same as the remote file if it already exists. | -| `abortOnError` | boolean| true | If set to `true`, errors will cause syncing to abort all remaining tasks immediately | -| `ignoreImageTransformCache` | boolean| false | Set to `true` to always re-generate transformed images, even if cached versions of the original and transformed image already exist. | -| `ignoreImageTransformErrors` | boolean| true | Set to `false` if you want to abort a content source from downloading if any of the image transforms fail. Leaving this to `true` will allow for non-image files to fail quietly. | -| `forceClearTempFiles` | boolean| true | Set to `false` if you want to keep all contents of the tempPath dir before downloading | +## License +MIT © Bluecadet diff --git a/packages/content/src/content-config.ts b/packages/content/src/content-config.ts index 066ccb94..2f04a5b4 100644 --- a/packages/content/src/content-config.ts +++ b/packages/content/src/content-config.ts @@ -45,11 +45,6 @@ export const contentConfigSchema = z.object({ "Which files to keep in `dest` if `clearOldFilesOnSuccess` or `clearOldFilesOnStart` are `true`. E.g. `['*.json', '** /*.csv', '*.xml', '*.git*']`", ) .default([]), - /** Strips this string from all media file paths when saving them locally */ - strip: z - .string() - .describe("Strips this string from all media file paths when saving them locally") - .default(""), /** Back up files before downloading and restore originals for all sources on failure of any single source. Defaults to true. */ backupAndRestore: z .boolean() diff --git a/packages/content/src/launchpad-content.ts b/packages/content/src/launchpad-content.ts index 9ea41fef..81b8deaf 100644 --- a/packages/content/src/launchpad-content.ts +++ b/packages/content/src/launchpad-content.ts @@ -59,8 +59,11 @@ class LaunchpadContent { return this._createSourcesFromConfig(inputSources) .andThrough(() => this._pluginDriver.runHookSequential("onContentFetchSetup")) - .andThen((sources) => - this.backup(sources) + .andThen((sources) => { + const backupAndRestore = this._config.backupAndRestore; + const backupProcess = backupAndRestore ? this.backup(sources) : okAsync(undefined); + + return backupProcess .andTee(() => this._logger.info("Clearing download directory")) .andThen(() => this.clear(sources, { @@ -80,12 +83,17 @@ class LaunchpadContent { .orElse((e) => { this._pluginDriver.runHookSequential("onContentFetchError", e); this._logger.error("Error in content fetch process:", e); - this._logger.info("Restoring from backup..."); - return this.restore(sources).andThen(() => { - return err( - new ContentError("Failed to download content. Restored from backup.", { cause: e }), - ); - }); + if (backupAndRestore) { + this._logger.info("Restoring from backup..."); + return this.restore(sources).andThen(() => { + return err( + new ContentError("Failed to download content. Restored from backup.", { + cause: e, + }), + ); + }); + } + return err(e); }) .andTee(() => this._logger.info("Content fetch complete. Clearing temp and backup directories."), @@ -93,11 +101,11 @@ class LaunchpadContent { .andThen(() => this.clear(sources, { temp: true, - backups: true, + backups: backupAndRestore, downloads: false, }), - ), - ); + ); + }); } /** diff --git a/packages/launchpad/README.md b/packages/launchpad/README.md index 6a16a901..0134f091 100644 --- a/packages/launchpad/README.md +++ b/packages/launchpad/README.md @@ -1,89 +1,45 @@ -# 🚀 Launchpad +# @bluecadet/launchpad -Launchpad is a highly configurable suite of tools to manage media installations. It can: +All-in-one package for building and managing interactive media installations. This package is a convenient way to install all core Launchpad packages at once. -- Launch, control and monitor muiltiple processes (via [PM2](https://pm2.keymetrics.io/)) -- Download and locally cache content from various common web APIs -- Bootstrap Windows PCs with common exhibit settings -- Consolidate and route application logs -- [...and much more](#documentation) +## Documentation -## Getting Started +For complete documentation, configuration options, and guides, visit: +[Launchpad Documentation](https://bluecadet.github.io/launchpad/) -1. Install launchpad: `npm i @bluecadet/launchpad` -2. Create a `launchpad.config.js` config (see [configuration](#configuration)) -3. _Optional: [Bootstrap](/packages/scaffold) your PC with `npx launchpad scaffold`_ -4. Run `npx launchpad` +## Installation -_Note: Launchpad is typically triggered run by a startup task (e.g. Windows Task Scheduler) using `npx launchpad`. When installed globally (`npm i -g @bluecadet/launchpad`), you can use the `launchpad` command instead. See [config loading](#config-loading) for more info._ - -_Note: [Scaffold](/packages/scaffold) is configured separately in a PowerShell file. This is a guided process when you run `npx launchpad scaffold`._ - -### Documentation - -All available config settings across packages can be found in the links below: - -- [**`monitor`**](/packages/monitor/README.md): Run and monitor apps -- [**`content`**](/packages/content/README.md): Download and cache remote content - - `sources`: An array containing one or more of the following content source options: - - [`airtable`](/packages/content/docs/airtable-source.md): Download content from Airtable - - [`contentful`](/packages/content/docs/contentful-source.md): Download content from Contentful - - [`json`](/packages/content/docs/json-source.md): Download content from JSON endpoints - - [`strapi`](/packages/content/docs/strapi-source.md): Download content from Strapi - - [`sanity`](/packages/content/docs/sanity-source.md): Download content from Sanity -- [**`logging`**](/packages/launchpad/docs/logging.md): Route logs to the console and to files -- [**`hooks`**](/packages/launchpad/docs/hooks.md): Execute scripts before or after common events (e.g. after content has been updated) - -### Config Loading +```bash +npm install @bluecadet/launchpad +``` -- By default, Launchpad looks for `launchpad.config.js`, `launchpad.config.mjs`, `launchpad.json` or `config.json` at the cwd (where you ran `npx launchpad`/`launchpad` from) -- You can change the default path with `--config=` or `-c=` (e.g. `npx launchpad --config=../settings/my-config.json`) -- If no config is found, Launchpad will traverse up directories (up to 64) to find one -- All config values can be overridden via `--foo=bar` (e.g. `--logging.level=debug`) +This will install all core packages: -### `.env` Files +- `@bluecadet/launchpad-cli`: Command line interface +- `@bluecadet/launchpad-content`: Content management +- `@bluecadet/launchpad-monitor`: Process monitoring +- `@bluecadet/launchpad-scaffold`: System configuration -Launchpad uses [dotenv](https://github.com/motdotla/dotenv) to load in environment variables from `.env` and `.env.local` files located in the same directory as your config file. +## Basic Usage -Environment variables are loaded before the config file is parsed, so you can use them in your config file. For example, you can use `process.env.MY_ENV_VAR` in your config file to access the value of `MY_ENV_VAR` in your `.env` file. +```bash +# Download content and start apps +npx launchpad start -> [!WARNING] -> We recommend using `.env.local` for sensitive credentials that should not be committed to source control. You should add `*.local` to your `.gitignore` to avoid them being checked into git. +# Only download fresh content +npx launchpad content -All Launchpad CLI commands also accept `--env ` (alias `-e`) options to manually specify one or more `.env` files to load. These paths are relative to the CWD (where you ran `npx launchpad`/`launchpad` from). +# Only manage apps +npx launchpad monitor -```sh -# Load ../.env then ../.env.develop -npx launchpad -e ../.env -e ../.env.develop +# Stop all processes +npx launchpad stop ``` -Additionally, the `--cascade-env=` (alias `-E`) option which will load the following files located alongside your config file: - -- `.env` -- `.env.local` -- `.env.` -- `.env..local` +## Note -## Packages +This is a meta-package that includes no code of its own. It simply installs all core Launchpad packages. For more targeted installations, you can install individual packages directly. -This repo is a monorepo that includes the following packages: +## License -- [`@bluecadet/launchpad`](/packages/launchpad) -- [`@bluecadet/launchpad-content`](/packages/content) -- [`@bluecadet/launchpad-monitor`](/packages/monitor) -- [`@bluecadet/launchpad-scaffold`](/packages/scaffold) -- [`@bluecadet/launchpad-utils`](/packages/utils) - -Each of these packages can be launched and configured independently (except for utils), so if you only need app-monitoring or content updates, you can install only `@bluecadet/launchpad-monitor` or `@bluecadet/launchpad-content`. - -## Requirements - -Launchpad requires Node `>=17.5.0` and NPM `>=8.5.1` for Windows API integration and workspaces support. - -We recommend installing the latest version of NodeJS and NPM via [nvm-windows](https://github.com/coreybutler/nvm-windows): - -``` -nvm install latest -nvm use latest -npm i -g npm@latest -``` +MIT © Bluecadet diff --git a/packages/launchpad/docs/logging.md b/packages/launchpad/docs/logging.md deleted file mode 100644 index 9bbeff1a..00000000 --- a/packages/launchpad/docs/logging.md +++ /dev/null @@ -1,63 +0,0 @@ -# Logging - -All logs are routed to and handled by [Winston](https://www.npmjs.com/package/winston) by the [LogManager](packages\utils\lib\log-manager.js) class. - -Logs are funneled to: -- The console with colorization -- Launchpad log files for `info`, `error` and `debug` with no colorization -- App log files for `stdout` and `stderr` with no colorization - -## Configuration - -To log everything up to the debug level, you can start launchpad with: - -``` -npx launchpad --logging.level=debug -``` - -Available log levels are: error - -0. `error` -1. `warn` -2. `info` -3. `http` -4. `verbose` -5. `debug` -6. `silly` - -All settings can be configured via `launchpad.json` or by passing in the appropriate CLI launch flag (e.g. `--logging.fileOptions.dirname=my-logs` to save logs to `my-logs/`). - -## Capturing Application Logs - -Launchpad routes all the `stdout` and `stderr` logs of all apps to the console and file based the settings [LogManager](packages\utils\lib\log-manager.js). See [Launchpad Monitor](/packages/monitor/README.md#logging-app-output) for more info. - -## Advanced Configuration - -See below for all available options and settings for logging. - - -### LogOptions -Options object passed directly to Winston's constructor, with additional options for Launchpad logging. - -See: https://github.com/winstonjs/winston#creating-your-own-logger for all available settings supported by Winston. -| Property | Type | Default | Description | -| - | - | - | - | -| `filename` | string| `%DATE%-%LOG\_TYPE%` | Where to save logs to. | -| `fileOptions` | LogFileOptions| new LogFileOptions(fileOptions) | Options for individual files and streams. | -| `level` | string| 'info' | The maximum log level to display in all default logs. | -| `format` | winston.Logform.Format| LogOptions.DEFAULT\_LOG\_FORMAT | The format for how each line is logged. | -| `overrideConsole` | boolean| true | Route all console logs to the log manager. This helps
ensure that logs are routed to files and rotated properly.

This will also freeze the console object, so it can't be
modified further during runtime.

All console logs will be prefixed with `(console)`. | - - -### LogFileOptions - - -See: https://github.com/winstonjs/winston-daily-rotate-file#options -| Property | Type | Default | Description | -| - | - | - | - | -| `format` | winston.Logform.Format| Uncolorized variant of LogOptions.DEFAULT\_LOG\_FORMAT | The format used for individual file logs. Uses the default log format but without colorization out of the box. | -| `extension` | string| '.log' | File extension. | -| `dirname` | string| '.logs' | The directory under which all logs are saved. | -| `maxSize` | string| '20m' | The max size of each individual log file. | -| `maxFiles` | string| '28d' | The maximum number of files to save per type. | -| `datePattern` | string| 'YYYY-MM-DD' | The date pattern used in file names. | diff --git a/packages/launchpad/package.json b/packages/launchpad/package.json index 78ea1d70..0100e6a6 100644 --- a/packages/launchpad/package.json +++ b/packages/launchpad/package.json @@ -39,11 +39,11 @@ }, "homepage": "https://github.com/bluecadet/launchpad#readme", "dependencies": { - "@bluecadet/launchpad-cli": "~2.0.0-next.2", - "@bluecadet/launchpad-content": "~2.0.0-next.4", - "@bluecadet/launchpad-dashboard": "~2.0.0-next.1", - "@bluecadet/launchpad-monitor": "~2.0.0-next.3", - "@bluecadet/launchpad-scaffold": "~2.0.0-next.1" + "@bluecadet/launchpad-cli": "2.0.0-next.2", + "@bluecadet/launchpad-content": "2.0.0-next.4", + "@bluecadet/launchpad-dashboard": "2.0.0-next.1", + "@bluecadet/launchpad-monitor": "2.0.0-next.3", + "@bluecadet/launchpad-scaffold": "2.0.0-next.1" }, "devDependencies": { "@bluecadet/launchpad-tsconfig": "0.1.0" diff --git a/packages/monitor/README.md b/packages/monitor/README.md index 26d9069c..dbfad4d4 100644 --- a/packages/monitor/README.md +++ b/packages/monitor/README.md @@ -1,147 +1,41 @@ -# Launchpad Monitor +# @bluecadet/launchpad-monitor -The [`@bluecadet/launchpad-monitor`](https://www.npmjs.com/package/@bluecadet/launchpad-monitor) package launches and monitors any number of apps. +Process monitoring and management for interactive installations. Part of the Launchpad suite of tools. -Under the hood, it uses PM2 for process management, and adds a few features like window foregrounding and minimizing. +## Documentation -## Configuration +For complete documentation, examples, and API reference, visit: + -1. Create a `monitor` section in your `launchpad.json` (see [`MonitorOptions`](#MonitorOptions)). -2. Add a list of app option objects in `monitor.apps` (see [`AppOptions`](#AppOptions)). -3. Each app requires a `pm2` block, which requires a `name` and `script` as a minimum. See [PM2 docs](https://pm2.keymetrics.io/docs/usage/application-declaration/#attributes-available) for all supported settings. -4. Run `npx launchpad monitor` (or `npx launchpad` to update content first if configured) +## Features -```json -{ - "monitor": { - "apps": [ - { - "pm2": { - "name": "my-app", - "script": "my-app.exe" - } - } - ] - } -} -``` - -Apps will be relaunched individually as soon as they exit. - - -### MonitorOptions -Top-level options of Launchpad Monitor. - - -| Property | Type | Default | Description | -| - | - | - | - | -| `apps` | Array.<AppOptions>| [] | A list of `AppOptions` to configure which apps to launch and monitor. | -| `deleteExistingBeforeConnect` | boolean| false | Set this to true to delete existing PM2 processes before connecting. If you're running volatile apps or your node process might be quit unexpectedly, this can be helpful to start with a clean slate on startup. | -| `windowsApi` | WindowsApiOptions| | Advanced configuration for the Windows API, e.g. for managing foreground/minimized/hidden windows. | - -### AppOptions -Options for an individual app to monitor. - - -| Property | Type | Default | Description | -| - | - | - | - | -| `pm2` | pm2.StartOptions| null | Configure which app to launch and how to monitor it here.

See: https://pm2.keymetrics.io/docs/usage/application-declaration/#attributes-available | -| `windows` | WindowOptions| new WindowOptions() | Optional settings for moving this app's main windows to the foreground, minimize or hide them. | -| `logging` | AppLogOptions| new AppLogOptions() | Optional settings for how to log this app's output. | - -### WindowOptions -Options for how an app's windows should be managed. - - -| Property | Type | Default | Description | -| - | - | - | - | -| `foreground` | boolean| false | Move this app to the foreground once all apps have been launched. | -| `minimize` | boolean| false | Minimize this app's windows once all apps have been launched. | -| `hide` | boolean| false | Completely hide this app's windows once all apps have been launched. Helpful for headless apps, but note that this might cause issues with GUI-based apps. | +- Process management via PM2 +- Plugin system for custom monitoring behavior +- Process lifecycle hooks +- Built-in logging and error handling +- Window management capabilities -## Example: Monitor Two Apps +## Installation -The following `launchpad.json` will launch and monitor two apps. The first app window will be foregrounded after launch, the second app will be minimized. If any of the apps exit, PM2 will relaunch them. - -```json -{ - "monitor": { - "apps": [ - { - "pm2": { - "name": "main-app", - "script": "my-main-app.exe", - "cwd": "../apps/" - }, - "windows": { - "foreground": true - } - }, - { - "pm2": { - "name": "side-app", - "script": "my-side-app.exe", - "cwd": "../apps/", - "args": "--custom-arg=true" - }, - "windows": { - "minimize": true - } - } - ] - } -} +```bash +npm install @bluecadet/launchpad-monitor ``` -## Logging App Output - -To capture your apps' logs in Launchpad Monitor you need to ensure that your apps are routing them to `stdout` and `stderr`. +## Basic Usage -### Unity +```typescript +import { Monitor } from '@bluecadet/launchpad-monitor'; -To redirect Unity's logs to stdout and stderr, launch your app using the `-logfile -` argument: +const monitor = new Monitor({ + apps: [{ + name: 'my-app', + script: 'app.js' + }] +}); -```json -{ - "monitor": { - "apps": [ - { - "name": "unity-app", - "script": "UnityPM2Test.exe", - "args": "-logfile -" - } - ] - } -} +await monitor.start(); ``` -### Cinder - -- Cinder doesn't route logs directly to `std::cout` and `std::cerr` by default, so this has to be done manually. See [here](https://github.com/bluecadet/Cinder-BluecadetViews/blob/2333604abc44a719e18df67566135ea34d545085/src/bluecadet/core/BaseApp.cpp#L22-L39) for an example for how to create one, and [here](https://github.com/bluecadet/Cinder-BluecadetViews/blob/2333604abc44a719e18df67566135ea34d545085/src/bluecadet/core/BaseApp.cpp#L86) for how to add it. -- If you use [Cinder-BluecadetViews](https://github.com/bluecadet/Cinder-BluecadetViews), all logs are routed to `stdout`/`stderr` via the `logToStdOut` setting. This is set to `true` by default and can otherwise be configured in `settings.json` or via cli flag: `my-app.exe console=false logToStdOut=true` - -## Adanced Configuration - -See below for further settings that can be configured globally and on a per-app level. - - -### WindowsApiOptions -Global options for how window order should be managed. - - -| Property | Type | Default | Description | -| - | - | - | - | -| `nodeVersion` | string| '>=17.4.0' | The minimum major node version to support window ordering.
Node versions < 17 seem to have a fatal bug with the native
API, which will intermittently cause V8 to crash hard.

See: https://github.com/node-ffi-napi/ref-napi/issues/54#issuecomment-1029639256 | -| `debounceDelay` | number| 3000 | The delay until windows are ordered after launch of in ms.

If your app takes a long time to open all of its windows, set this number to a higher value to ensure it can be on top of the launchpad terminal window.

Keeping this high also reduces the CPU load if apps relaunch often. | +## License - -### AppLogOptions -Options for how an app's logs should be saved, routed and displayed. - - -| Property | Type | Default | Description | -| - | - | - | - | -| `logToLaunchpadDir` | boolean| true | Route application logs to launchpad's log dir instead of pm2's log dir. | -| `mode` | 'bus' \| 'file'| 'bus' | How to grab the app's logs. Supported values:
- `'bus'`: Logs directly from the app's stdout/stderr bus. Can result in interrupted logs if the buffer isn't consistently flushed by an app.
- `'file'`: Logs by tailing the app's log files. Slight lag, but can result in better formatting than bus. Not recommended, as logs cannot be rotated by launchpad. | -| `showStdout` | boolean| true | Whether or not to include output from `stdout` | -| `showStderr` | boolean| true | Whether or not to include output from `stderr` | +MIT © Bluecadet diff --git a/packages/scaffold/README.md b/packages/scaffold/README.md index edf3e647..a9bceefb 100644 --- a/packages/scaffold/README.md +++ b/packages/scaffold/README.md @@ -1,37 +1,32 @@ -# Launchpad Scaffolding +# @bluecadet/launchpad-scaffold -The [`@bluecadet/launchpad-scaffold`](https://www.npmjs.com/package/@bluecadet/launchpad-scaffold) package is a collection of PS1 scripts to configure Windows PCs for exhibit environments. +Windows system configuration tools for interactive media installations. This package helps automate the setup of Windows machines for kiosk and exhibit environments. -![Screen Recording of Launchpad Scaffold](https://user-images.githubusercontent.com/295789/197362787-8cd2d39e-ba36-4a05-9151-9a844f581c25.gif) +## Documentation + +For complete documentation and guides, visit: +[Launchpad Documentation](https://bluecadet.github.io/launchpad/) ## Features -- Fully configurable, with optional y/n prompts -- Disable notifications -- Disable Windows updates -- Disable Windows error reporting -- Disable sleep, set to max power -- Disable touch feedback and gestures (long press, cursor, edge-swipes, ...) -- Create daily app launch and restart tasks -- Install common apps via [chocolatey](https://chocolatey.org/) (vscode, github, ...) -- Uninstall bloatware like OneDrive -- Portable config (save to USB, run on multiple machines) -- ...and more +- Automated Windows kiosk setup +- System optimization and lockdown +- Power settings management +- Startup task configuration +- User interface customization -For all available scripts, check out [`scripts/windows`](./scripts/windows/). +## Installation -## Setup +```bash +npm install @bluecadet/launchpad-scaffold @bluecadet/launchpad-cli +``` -To run the scaffold scripts, you can call `npx launchpad scaffold`, or manually run [`packages/scaffold/setup.bat`](./setup.bat) _as administrator_. +## Basic Usage -1. On first run, you'll be prompted to edit your user config -1. Once you close the config editor, the script will continue -1. By default, all scripts must be confirmed with a y/n prompt -1. To automate execution of all scripts, set [`ConfirmAllScripts`](https://github.com/bluecadet/launchpad/blob/develop/packages/scaffold/config/defaults.ps1#L9) to `$false` -1. You can copy the generated user config from `packages/scaffold/config/user.ps1` to other PCs to apply the same settings +```bash +npx launchpad scaffold +``` -## Credit +## License -Many scripts and settings are based on examples and precedents from various existing resources. Besides [https://stackoverflow.com/](), the following two repositories have been crucial references: -- https://github.com/jayharris/dotfiles-windows -- https://github.com/morphogencc/ofxWindowsSetup/tree/master/scripts +MIT © Bluecadet diff --git a/packages/scaffold/config/defaults.ps1 b/packages/scaffold/config/defaults.ps1 index 8ce8da6f..544e1a32 100644 --- a/packages/scaffold/config/defaults.ps1 +++ b/packages/scaffold/config/defaults.ps1 @@ -27,7 +27,6 @@ $global:LaunchpadConfig.Computer.RebootTime = "3:00" # App installs $global:LaunchpadConfig.InstallApps.Enabled = $true # Install Chocolatey; Required for any of the below app packages -# $global:LaunchpadConfig.InstallApps.Apps = "nodejs", "python", "vscode", "github-desktop", "git", "cmake", "visualstudio2022community", "visualstudio2022-workload-nativedesktop", "microsoft-build-tools", "microsoft-visual-cpp-build-tools", "visualstudio2022-workload-vctools" $global:LaunchpadConfig.InstallApps.Apps = "nvm", "python", "vscode", "github-desktop", "git", "cmake", "visualstudio2022buildtools", "visualstudio2022-workload-nativedesktop", "visualstudio2022-workload-vctools" $global:LaunchpadConfig.InstallApps.InstallNodeDependencies = $true # Installs Launchpad dependencies diff --git a/packages/utils/README.md b/packages/utils/README.md index c62079ea..6b5cdfdf 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -1,3 +1,3 @@ # Launchpad Utils -Collection of utils used across [@bluecadet/launchpad](https://www.npmjs.com/package/@bluecadet/launchpad) packages. \ No newline at end of file +Collection of utils used across [@bluecadet/launchpad](https://www.npmjs.com/package/@bluecadet/launchpad) packages.