{"id":51833816,"url":"https://github.com/johnwmail/notejs","last_synced_at":"2026-07-22T18:32:25.432Z","repository":{"id":357565645,"uuid":"1236462572","full_name":"johnwmail/notejs","owner":"johnwmail","description":"Lightweight serverless note-taking app — Cloudflare Workers \u0026 Vercel Edge","archived":false,"fork":false,"pushed_at":"2026-05-13T09:08:15.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-13T11:12:17.381Z","etag":null,"topics":["cloudflare-workers","edge-computing","note-taking","redis","serverless","typescript","upstash","vanilla-js","vercel-edge"],"latest_commit_sha":null,"homepage":"https://notejs-coral.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnwmail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-12T09:12:39.000Z","updated_at":"2026-05-13T09:08:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/johnwmail/notejs","commit_stats":null,"previous_names":["johnwmail/notejs"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/johnwmail/notejs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnwmail%2Fnotejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnwmail%2Fnotejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnwmail%2Fnotejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnwmail%2Fnotejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnwmail","download_url":"https://codeload.github.com/johnwmail/notejs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnwmail%2Fnotejs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35773465,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-22T02:00:06.236Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare-workers","edge-computing","note-taking","redis","serverless","typescript","upstash","vanilla-js","vercel-edge"],"created_at":"2026-07-22T18:32:24.635Z","updated_at":"2026-07-22T18:32:25.424Z","avatar_url":"https://github.com/johnwmail.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# notejs\n\nA lightweight, serverless note-taking web app written in TypeScript. Create, edit, and share notes with auto-save. Deploys on Cloudflare Workers (native KV) or Vercel Edge (Upstash Redis). No JS frameworks, no build step, zero dependencies — pure vanilla HTML/CSS/JS.\n\n## Features\n\n- **Simple Note Editor**: Lightweight web interface for creating and editing notes\n- **Auto-Save**: Automatically saves note content every second\n- **Shareable URLs**: Notes accessible via direct links with human-friendly WORDnn IDs\n- **Multi-Deployment**: Cloudflare Workers or Vercel Edge\n- **Zero Dependencies**: No npm runtime deps — KV via native bindings (Cloudflare) or REST fetch (Vercel)\n- **XSS Protection**: User content is HTML-escaped\n- **Responsive**: Works on desktop and mobile\n- **Print Support**: Print-friendly interface\n\n## Quick Start\n\n### Prerequisites\n\n- Node.js 24+\n\n### Local Development\n\nThe app uses Wrangler (Cloudflare Workers) for local dev. KV is simulated by Miniflare — no account needed:\n\n```bash\nnpm install\nnpm run dev\n```\n\nOpen http://localhost:8787.\n\n---\n\n## Deploy to Cloudflare Workers (recommended)\n\n**Storage**: Cloudflare KV — persistent, native, zero-config.\n\n### Step 1: Create a KV namespace\n\n```bash\nnpx wrangler kv:namespace create \"KV\"\n```\n\n### Step 2: Add the namespace ID to `wrangler.toml`\n\nCopy the ID from the output and paste it:\n\n```toml\n[[kv_namespaces]]\nbinding = \"KV\"\nid = \"your-namespace-id\"\n```\n\n### Step 3: Deploy\n\n```bash\nnpm run deploy\n```\n\nYour app is live at `https://your-app.your-account.workers.dev` with persistent KV storage.\n\n---\n\n## Deploy to Vercel Edge\n\n**Storage**: Upstash Redis — persistent with 7-day TTL.\n\n### Step 1: Create Upstash Redis (or Vercel KV)\n\nCreate a Redis database at [upstash.com](https://upstash.com) (free tier). Copy the `KV_REST_API_URL` and `KV_REST_API_TOKEN` from the REST API section.\n\nAlternatively, create via Vercel Dashboard: Storage → Create → Redis. This auto-injects the env vars.\n\n### Step 2: Set environment variables\n\n```bash\nnpx vercel env add KV_REST_API_URL production --value \u003cyour-url\u003e\nnpx vercel env add KV_REST_API_TOKEN production --value \u003cyour-token\u003e\n```\n\n### Step 3: Deploy\n\n```bash\nnpx vercel --prod\n```\n\n`vercel.json` builds `src/vercel.ts` as an Edge Function and routes all requests to it. The app uses `VercelKVStorage` which connects to Upstash Redis via its REST API.\n\nYour app is live at `https://your-app.vercel.app` with persistent KV storage.\n\n---\n\n## CI/CD (GitHub Actions)\n\nThe repository includes two workflows:\n\n### Test (`test.yml`)\n\nRuns on every push/PR to `feature/typescript`:\n- `npm ci` → `tsc --noEmit` → `npm test`\n\n### Deploy Cloudflare (`deploy-cloudflare.yml`)\n\nManual trigger (`workflow_dispatch`) — typecheck → deploy to Cloudflare Workers.\n\n**Required secret:** `CF_API_TOKEN` — Cloudflare API token with Workers permissions (create in Cloudflare Dashboard → My Profile → API Tokens, template: \"Edit Cloudflare Workers\").\n\n### Deploy Vercel (`deploy-vercel.yml`)\n\nManual trigger (`workflow_dispatch`) — typecheck → deploy to Vercel Edge.\n\n**Required secrets:**\n\n| Secret | Description |\n|---|---|\n| `VERCEL_TOKEN` | Vercel access token (create in Vercel Dashboard → Settings → Tokens) |\n| `VERCEL_ORG_ID` | Your Vercel team/org ID (from `.vercel/project.json`) |\n| `VERCEL_PROJECT_ID` | Your Vercel project ID (from `.vercel/project.json`) |\n\n**To set up secrets:** Go to your GitHub repo → Settings → Secrets and variables → Actions, then add each value. Run workflows via Actions tab → select workflow → Run workflow.\n\n## API\n\n### GET /noteid/{noteId} (or `/?note={noteId}`)\n\nRetrieve and display a note.\n\n**Parameters:**\n- `noteId` (path) or `note` (query): Note ID (alphanumeric, 3–32 chars, no I/O/0/1)\n\n**Notes:**\n- The preferred URL format is `/noteid/{noteId}` for shell-friendly links (e.g., `http://example.com/noteid/BLAST47`).\n- Backwards compatibility: `/?note={noteId}` still works.\n- Links use the request host or `x-forwarded-*` headers for reverse proxy support.\n- Requests with `User-Agent` containing \"curl\" return plain text instead of HTML.\n\n### POST /\n\nSave or delete a note.\n\n**Request body (JSON):**\n```json\n{\n  \"noteId\": \"BLAST47\",\n  \"content\": \"Note content here\"\n}\n```\n\n**Response (JSON):**\n```json\n{\n  \"success\": true,\n  \"noteId\": \"BLAST47\"\n}\n```\n\n**Behavior:**\n- If `noteId` is empty, a random WORDnn ID is generated (e.g., \"BLAST47\")\n- If `content` is empty or whitespace-only, the note is deleted\n- Otherwise, the note is saved\n\n**Examples:**\n```bash\n# Create new note\ncurl -X POST https://your-app.com/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"content\":\"Hello World\"}'\n\n# Returns URL for curl requests\ncurl -X POST https://your-app.com/ \\\n  -H \"Content-Type: application/json\" \\\n  -H \"User-Agent: curl/8.0\" \\\n  -d '{\"content\":\"Hello World\"}'\n# → https://your-app.com/noteid/BLAST47\n```\n\n## Project Structure\n\n```\n.\n├── src/\n│   ├── workers.ts          # Cloudflare Workers entry point\n│   ├── vercel.ts           # Vercel Edge entry point\n│   ├── handler.ts          # HTTP handlers (GET, POST, OPTIONS)\n│   ├── handler.test.ts     # Handler unit tests\n│   └── lib/\n│       ├── template.ts     # Full HTML/CSS/JS UI template\n│       ├── storage.ts      # Storage interface + KV / Vercel KV backends\n│       ├── storage.test.ts # Storage unit tests\n│       ├── utils.ts        # Note ID generation/validation, HTML escaping, ClientIP\n│       └── types.ts        # TypeScript type definitions\n├── wrangler.toml           # Cloudflare Workers configuration\n├── vercel.json             # Vercel deployment configuration\n├── .vercelignore           # Files excluded from Vercel deployment\n├── .github/workflows/      # GitHub Actions CI/CD workflows\n├── package.json            # Dependencies and scripts\n├── tsconfig.json           # TypeScript configuration\n└── README.md               # This file\n```\n\n## Scripts\n\n| Command | Description |\n|---|---|\n| `npm run dev` | Start local dev server with Wrangler |\n| `npm run deploy` | Deploy to Cloudflare Workers |\n| `npm test` | Run Vitest unit tests |\n| `npm run typecheck` | Run TypeScript type check (`tsc --noEmit`) |\n\n## Configuration\n\n### Note ID Format\n\nNote IDs use a WORDnn format — a random dictionary word (3–5 uppercase letters, excluding ambiguous characters I/O/0/1) followed by 2 digits. Examples: `BLAST47`, `ACE23`, `ZEBRA99`.\n\n### Storage\n\nBoth platforms use persistent KV storage with 7-day TTL. Cloudflare Workers uses native **Workers KV** via `env.KV`. Vercel Edge uses **Upstash Redis** via REST API (`KV_REST_API_URL` / `KV_REST_API_TOKEN`). Both implement the same `Storage` interface.\n\n## Testing\n\n```bash\nnpm test           # Run Vitest\nnpm run typecheck  # TypeScript type checking\n```\n\n## Security\n\n- Input validation: Note IDs restricted to `[A-HJ-NP-Z2-9]{3,32}`\n- XSS protection: User content server-side HTML-escaped before rendering\n- No third-party frontend dependencies\n\n## License\n\nMIT License — feel free to use and modify for your needs.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnwmail%2Fnotejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnwmail%2Fnotejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnwmail%2Fnotejs/lists"}