diff --git a/examples/csv-importer/package.json b/examples/csv-importer/package.json index c98e90a..fa2870e 100644 --- a/examples/csv-importer/package.json +++ b/examples/csv-importer/package.json @@ -7,7 +7,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "framer-api": "^0.0.1-beta.0", + "framer-api": "^0.0.1-beta.1", "papaparse": "^5.5.3", "typescript": "^5.9.3" }, diff --git a/examples/json-api/package.json b/examples/json-api/package.json index 0663411..260d990 100644 --- a/examples/json-api/package.json +++ b/examples/json-api/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@hono/node-server": "^1.14.1", - "framer-api": "^0.0.1-beta.0", + "framer-api": "^0.0.1-beta.1", "hono": "^4.7.10", "typescript": "^5.9.3" }, diff --git a/examples/publish/README.md b/examples/publish/README.md new file mode 100644 index 0000000..5b72158 --- /dev/null +++ b/examples/publish/README.md @@ -0,0 +1,42 @@ +# Publish + +Publishes and deploys a Framer project. Designed to run as a one-shot script, making it ideal for cron jobs, CI/CD pipelines, and systemd timers. + +## Usage + +```bash +node --env-file=../../.env index.ts + +bun --env-file=../../.env run index.ts + +deno --env-file=../../.env run index.ts +``` + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `EXAMPLE_PROJECT_URL` | Yes | Your Framer project URL | + +## Scheduling Examples + +### Cron + +Publish every 4 hours: + +```bash +# Edit crontab +crontab -e + +# Add this line (adjust paths as needed) +0 */4 * * * cd /path/to/examples/publish && node --env-file=../../.env index.ts >> /var/log/framer-publish.log 2>&1 +``` + +Common cron schedules: + +```bash +0 */4 * * * # Every 4 hours +0 9-18/2 * * 1-5 # Every 2 hours between 9:00 and 18:00, mon-fri +0 9 * * * # Daily at 9:00 +0 9 * * 1-5 # Weekdays at 9:00 +``` diff --git a/examples/publish/package.json b/examples/publish/package.json new file mode 100644 index 0000000..e63bf07 --- /dev/null +++ b/examples/publish/package.json @@ -0,0 +1,16 @@ +{ + "name": "publish", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "framer-api": "^0.0.1-beta.1", + "typescript": "^5.9.3" + }, + "devDependencies": { + "@types/node": "^22.10.2" + } +} diff --git a/examples/publish/publish.ts b/examples/publish/publish.ts new file mode 100644 index 0000000..0e0f88b --- /dev/null +++ b/examples/publish/publish.ts @@ -0,0 +1,34 @@ +import assert from "node:assert"; +import { connect } from "framer-api"; + +const projectUrl = process.env["EXAMPLE_PROJECT_URL"]; +assert(projectUrl, "EXAMPLE_PROJECT_URL environment variable is required"); + +using framer = await connect(projectUrl); + +// Show changes +const changedPaths = await framer.getChangedPaths(); +const entries = Object.entries(changedPaths); +const totalChanges = entries.reduce((sum, [, paths]) => sum + paths.length, 0); + +if (totalChanges === 0) { + console.log("⛔️ No changes to publish."); + process.exit(0); +} + +console.log(`📄 ${totalChanges} change(s):`); +for (const [type, paths] of entries) { + for (const path of paths) { + console.log(` ${type}: ${path}`); + } +} + +// Publish +const { deployment } = await framer.publish(); +console.log(`🚀 Published deployment ${deployment.id}`); + +const deployedHostnames = await framer.deploy(deployment.id); +console.log(`✅ Deployed to:`); +for (const hostname of deployedHostnames) { + console.log(` https://${hostname.hostname}`); +} diff --git a/examples/publish/tsconfig.json b/examples/publish/tsconfig.json new file mode 100644 index 0000000..ff466ce --- /dev/null +++ b/examples/publish/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["publish.ts"] +} diff --git a/package-lock.json b/package-lock.json index 1349b99..62d4101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "examples/csv-importer": { "version": "0.0.1", "dependencies": { - "framer-api": "^0.0.1-beta.0", + "framer-api": "^0.0.1-beta.1", "papaparse": "^5.5.3", "typescript": "^5.9.3" }, @@ -28,7 +28,7 @@ "version": "0.0.1", "dependencies": { "@hono/node-server": "^1.14.1", - "framer-api": "^0.0.1-beta.0", + "framer-api": "^0.0.1-beta.1", "hono": "^4.7.10", "typescript": "^5.9.3" }, @@ -36,6 +36,16 @@ "@types/node": "^22.10.2" } }, + "examples/publish": { + "version": "0.0.1", + "dependencies": { + "framer-api": "^0.0.1-beta.1", + "typescript": "^5.9.3" + }, + "devDependencies": { + "@types/node": "^22.10.2" + } + }, "node_modules/@biomejs/biome": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.9.tgz", @@ -242,9 +252,9 @@ "license": "MIT" }, "node_modules/framer-api": { - "version": "0.0.1-beta.0", - "resolved": "https://registry.npmjs.org/framer-api/-/framer-api-0.0.1-beta.0.tgz", - "integrity": "sha512-rSTIX4KOCf9zRMGzeI92b/+dAG67JibFYQjTlJlG119fA4nfDlFWclZIxaBdFRnQZIckrlE+GHvcym2T+dCItQ==", + "version": "0.0.1-beta.1", + "resolved": "https://registry.npmjs.org/framer-api/-/framer-api-0.0.1-beta.1.tgz", + "integrity": "sha512-AgCbORmJJ3fytV3xtCkESVzl0N3T68ad/tVr6Nbjf5g3B+xPxBT88EKajxOlspblLiROIvtryRx+Dyl4ZzL85w==", "dependencies": { "devalue": "^5.4.2", "unenv": "^2.0.0-rc.24", @@ -299,6 +309,10 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, + "node_modules/publish": { + "resolved": "examples/publish", + "link": true + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",