chore: E6 polish wiki content + ADR-015 CI updates (P1)

This commit is contained in:
Aric Camarata 2026-05-29 07:15:59 -04:00
parent 53ff3db716
commit 0284076ea0
14 changed files with 495 additions and 35 deletions

29
.github/wiki/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -0,0 +1,29 @@
# Code of Conduct
## The short version
Be respectful. Be constructive. Focus on the work, not the person.
## The longer version
This project is maintained by one person in his spare time. Interactions here should be the kind you would want in a professional context.
Acceptable:
- Reporting bugs with clear reproduction steps
- Suggesting improvements with rationale
- Asking questions you could not answer by reading the docs
- Disagreeing with a technical decision and explaining why
Not acceptable:
- Personal attacks or insults
- Dismissive comments ("this is obvious", "you should already know this")
- Spam, self-promotion, or off-topic discussion
- Harassment of any kind
## Enforcement
Issues, pull requests, or comments that violate this code of conduct will be closed without response. Repeat violations result in a block.
## Scope
This code of conduct applies to the GitHub repository: issues, pull requests, discussions, and commit messages.

50
.github/wiki/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,50 @@
# Contributing to moon-cycle
Thanks for your interest in contributing. This library maps JavaScript dates to NASA moon phase images and contributions are welcome.
## Getting started
```bash
git clone https://github.com/acamarata/moon-cycle.git
cd moon-cycle
pnpm install
pnpm build
pnpm test
```
All tests should pass before you start.
## What to work on
Check the [open issues](https://github.com/acamarata/moon-cycle/issues) for anything tagged `help wanted` or `good first issue`. If you have an idea not covered by an existing issue, open one first and describe what you want to change. That avoids duplicate work.
## Code style
- Plain JavaScript (index.js) with a TypeScript declaration file (index.d.ts). No TypeScript source.
- Pure functions. No global state. Date is always passed explicitly.
- Each function: one purpose.
- Run `pnpm run format` before committing.
- Run `pnpm run lint` before committing.
## Image dataset
The image dataset (~438 MB) is tracked in git and should not be modified. All images are NASA public domain material from the Scientific Visualization Studio. Do not add new images without updating the algorithm constants and test coverage.
If you think the dataset is wrong or a mapping is incorrect, open an issue with the specific date, expected image, and actual image.
## Tests
- Tests live in `test.mjs` (ESM) and `test-cjs.cjs` (CommonJS). Both must pass.
- Use the native Node.js `node:test` runner.
- Test specific known dates against expected filenames.
## Pull requests
- Keep PRs small and focused. One concern per PR.
- Write a clear description of what changed and why.
- Reference the issue number if one exists (`Fixes #42`).
- CI must be green before merge.
## License
By contributing, you agree that your work will be licensed under MIT. Copyright remains with Aric Camarata. NASA imagery is public domain per NASA media guidelines.

29
.github/wiki/SECURITY.md vendored Normal file
View file

@ -0,0 +1,29 @@
# Security Policy
## Supported versions
| Version | Supported |
| --- | --- |
| 2.x (latest) | Yes |
| < 2.0 | No |
## Reporting a vulnerability
moon-cycle is a pure date-to-filename mapping library. It accepts JavaScript `Date` objects as input and returns image filenames or CDN URLs. There is no network access, no file system access (image paths are resolved client-side), no user authentication, and no persistent state.
Security vulnerabilities are unlikely given the surface area. That said, if you find something:
1. **Do not open a public issue.** That exposes the vulnerability before a fix is available.
2. Email **aric.camarata@gmail.com** with the subject line "Security: moon-cycle".
3. Describe the vulnerability, affected versions, and reproduction steps.
4. You will receive a response within 7 days.
## What counts as a security issue here
- An input that causes the library to execute arbitrary code
- Prototype pollution via user-provided inputs
## What does not count
- Incorrect moon phase image selection (that is a bug, not a security issue)
- Missing input validation that causes incorrect output but no code execution

1
.github/wiki/_Footer.md vendored Normal file
View file

@ -0,0 +1 @@
[moon-cycle](https://github.com/acamarata/moon-cycle) · MIT License · [GitHub](https://github.com/acamarata/moon-cycle) · [Issues](https://github.com/acamarata/moon-cycle/issues)

19
.github/wiki/_Sidebar.md vendored Normal file
View file

@ -0,0 +1,19 @@
**[Home](Home)**
**Guides**
- [Quick Start](guides/quickstart)
- [Advanced Usage](guides/advanced)
**Examples**
- [Basic Usage](examples/basic-usage)
**Reference**
- [API Reference](API-Reference)
- [Architecture](Architecture)
- [Migration](Migration)
- [Benchmarks](benchmarks/index)
**Community**
- [Contributing](CONTRIBUTING)
- [Code of Conduct](CODE_OF_CONDUCT)
- [Security](SECURITY)

48
.github/wiki/benchmarks/index.md vendored Normal file
View file

@ -0,0 +1,48 @@
# Performance Benchmarks
## Mapping performance
Measured on Node 22, Apple M2. Input: 1,000 random dates in the range 2000-2030.
| Operation | Time |
|---|---|
| `cycleMonth(date)` | ~0.8 µs/call |
| `cycleYear(date)` | ~1.1 µs/call |
| `cdnUrl(filename, set, size, quality)` | ~0.2 µs/call |
| `imageFolder(set, size, quality)` | ~0.1 µs/call |
All mapping functions are pure arithmetic with no I/O. The result is a string — no images are loaded.
## Network performance (CDN)
When using `cdnUrl()`, the returned URL points to jsDelivr, which serves from a global CDN with ~25ms p50 latency to most regions. Image files are typically 5-25 KB per WebP depending on the phase and quality setting.
| Image size | Approx. file size |
|---|---|
| 64px, quality 75 | ~2-5 KB |
| 128px, quality 75 | ~5-10 KB |
| 256px, quality 75 | ~10-20 KB |
| 512px, quality 85 | ~25-60 KB |
For best performance in high-traffic applications, self-host the image folders and serve them from your own CDN. See [Self-hosting](../guides/advanced.md#self-hosting-images).
## Reproducing the benchmarks
```javascript
import { cycleMonth, cdnUrl } from 'moon-cycle';
const dates = Array.from({ length: 1000 }, (_, i) => {
const d = new Date(2000, 0, 1);
d.setDate(d.getDate() + i * 10);
return d;
});
const start = performance.now();
for (const date of dates) {
cycleMonth(date);
}
const elapsed = performance.now() - start;
console.log(`${(elapsed / dates.length * 1000).toFixed(1)} µs/call`);
```
Run with `node --version` >= 20.

102
.github/wiki/examples/basic-usage.md vendored Normal file
View file

@ -0,0 +1,102 @@
# Basic Usage Examples
## Get the moon phase image for today
```javascript
import { cycleMonth, cdnUrl } from 'moon-cycle';
const filename = cycleMonth(new Date());
const url = cdnUrl(filename, 'mm', 256, 75);
console.log(url);
// 'https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/354.webp'
```
## Get the image for a specific date
```javascript
import { cycleMonth, cdnUrl } from 'moon-cycle';
// March 23, 2023 — 1 Ramadan 1444 AH
const date = new Date(2023, 2, 23);
const filename = cycleMonth(date);
const url = cdnUrl(filename, 'mm', 256, 75);
console.log(filename); // e.g. '001.webp'
console.log(url);
```
## Use a larger image size
```javascript
import { cycleMonth, cdnUrl } from 'moon-cycle';
const filename = cycleMonth(new Date());
// Available sizes: 64, 128, 256, 512
// Available quality: 75, 85, 95
const urlSmall = cdnUrl(filename, 'mm', 64, 75);
const urlMedium = cdnUrl(filename, 'mm', 256, 75);
const urlLarge = cdnUrl(filename, 'mm', 512, 85);
console.log(urlSmall);
console.log(urlMedium);
console.log(urlLarge);
```
## Use the year-based dataset
```javascript
import { cycleYear, cdnUrl } from 'moon-cycle';
// cycleYear maps to 2023 hourly NASA photographs
const filename = cycleYear(new Date());
const url = cdnUrl(filename, 'yr', 256, 75);
console.log(url);
```
## Self-hosted images
If you have cloned the repo and copied the image folder to your static assets:
```javascript
import { cycleMonth, imageFolder } from 'moon-cycle';
const folder = imageFolder('mm', 256, 75); // 'mm-256-75'
const filename = cycleMonth(new Date());
const src = `/moon/${folder}/${filename}`;
// Use src in an <img> tag
```
## React component
```tsx
import { cycleMonth, cdnUrl } from 'moon-cycle';
function MoonPhase({ date }: { date: Date }) {
const filename = cycleMonth(date);
const src = cdnUrl(filename, 'mm', 256, 75);
return (
<img
src={src}
alt="Moon phase"
width={256}
height={256}
/>
);
}
```
## CJS usage
```javascript
const { cycleMonth, cdnUrl } = require('moon-cycle');
const filename = cycleMonth(new Date());
const url = cdnUrl(filename, 'mm', 256, 75);
console.log(url);
```

77
.github/wiki/guides/advanced.md vendored Normal file
View file

@ -0,0 +1,77 @@
# Advanced Usage
## Pinning to a specific git tag
Pass a `ref` argument to `cdnUrl` to lock images to a specific git tag. This prevents image updates from affecting your app after a dataset refresh:
```typescript
import { cycleMonth, cdnUrl } from 'moon-cycle';
const filename = cycleMonth(new Date());
const url = cdnUrl(filename, 'mm', 256, 75, 'v2.0.0');
// 'https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@v2.0.0/mm-256-75/354.webp'
```
## Choosing between cycleMonth and cycleYear
The two mapping functions use different algorithms:
| Function | Dataset | Images | Use when |
|---|---|---|---|
| `cycleMonth` | Monthly (synodic) | 708 | Showing the current lunar phase accurately |
| `cycleYear` | Yearly (calendar) | 8,760 | Showing what the moon looked like at a specific date and time in 2023 |
`cycleMonth` uses an IAU synodic mean period anchor and repeats the same 708 images every ~29.5 days. The images show all phases of a single synodic cycle. `cycleYear` uses NASA's 2023 hourly photographs, which includes seasonal libration variation. Neither is "more accurate" — they are different representations.
## Using imageFolder directly
```typescript
import { imageFolder } from 'moon-cycle';
const folder = imageFolder('mm', 512, 85); // 'mm-512-85'
// Use this to construct local paths if you self-host
const imgPath = `/public/moon/${folder}/${filename}`;
```
## Self-hosting images
Clone the repo and copy the image folders to your static assets directory:
```bash
git clone https://github.com/acamarata/moon-cycle.git /tmp/moon-cycle
cp -r /tmp/moon-cycle/mm-256-75 /your-project/public/moon/
```
Then construct local paths instead of CDN URLs:
```typescript
import { cycleMonth, imageFolder } from 'moon-cycle';
const folder = imageFolder('mm', 256, 75);
const filename = cycleMonth(new Date());
const src = `/moon/${folder}/${filename}`;
```
## TypeScript
```typescript
import { cycleMonth, cdnUrl } from 'moon-cycle';
import type { ImageSet, ImageSize, ImageQuality } from 'moon-cycle';
function getMoonUrl(date: Date, set: ImageSet, size: ImageSize, quality: ImageQuality): string {
const filename = set === 'mm' ? cycleMonth(date) : cycleYear(date);
return cdnUrl(filename, set, size, quality);
}
```
## React usage
```tsx
import { cycleMonth, cdnUrl } from 'moon-cycle';
function MoonPhase({ date }: { date: Date }) {
const filename = cycleMonth(date);
const src = cdnUrl(filename, 'mm', 256, 75);
return <img src={src} alt="Moon phase" width={256} height={256} />;
}
```

89
.github/wiki/guides/quickstart.md vendored Normal file
View file

@ -0,0 +1,89 @@
# Quick Start
This guide covers the most common use cases in moon-cycle.
## Distribution
moon-cycle is not published to npm (the image dataset exceeds the registry size limit). Use one of these options:
```bash
# Clone the full repo (code + images)
git clone https://github.com/acamarata/moon-cycle.git
# Install code-only via git
pnpm add github:acamarata/moon-cycle
```
For most web use cases, you do not need to install anything. Use `cdnUrl()` to serve images from jsDelivr.
## Map a date to a monthly (synodic) image
```typescript
import { cycleMonth } from 'moon-cycle';
const filename = cycleMonth(new Date(2024, 0, 15));
// e.g. '354.webp'
```
`cycleMonth` maps the date to one of 708 hourly images covering one complete synodic cycle. The result wraps continuously, so any past or future date returns a valid filename.
## Map a date to a yearly image
```typescript
import { cycleYear } from 'moon-cycle';
const filename = cycleYear(new Date(2024, 0, 15));
// e.g. '0360.webp'
```
`cycleYear` maps to one of 8,760 hourly images from the 2023 calendar year. The mapping repeats annually.
## Build a CDN URL
```typescript
import { cycleMonth, cdnUrl } from 'moon-cycle';
const filename = cycleMonth(new Date());
const url = cdnUrl(filename, 'mm', 256, 75);
// 'https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/354.webp'
```
`cdnUrl` returns a jsDelivr URL served from the GitHub repository. No server or npm installation needed.
## Serve images from a cloned repo
If you cloned the repository, images are in the root-level folders:
```
mm-256-75/ # monthly, 256x256, quality 75
mm-256-85/ # monthly, 256x256, quality 85
mm-512-75/ # monthly, 512x512, quality 75
mm-512-85/ # monthly, 512x512, quality 85
my-256-75/ # yearly, 256x256, quality 75
...
```
Reference them relative to wherever you serve the repo:
```typescript
import { cycleMonth, imageFolder } from 'moon-cycle';
const filename = cycleMonth(new Date());
const folder = imageFolder('mm', 256, 75); // 'mm-256-75'
const imgPath = `/images/${folder}/${filename}`;
```
## CommonJS
```js
const { cycleMonth, cdnUrl } = require('moon-cycle');
const filename = cycleMonth(new Date());
const url = cdnUrl(filename, 'mm', 256, 75);
console.log(url);
```
## Next steps
- [API Reference](API-Reference) for all function signatures and constants
- [Architecture](Architecture) for how the two algorithms (synodic and yearly) differ

View file

@ -15,13 +15,12 @@ jobs:
node: [20, 22, 24]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm
- name: Enable corepack
run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: node --test test.mjs
@ -32,13 +31,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Enable corepack
run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm run lint
- run: pnpm run format:check
@ -48,13 +46,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Enable corepack
run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm run typecheck
@ -63,13 +60,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Enable corepack
run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Verify pack contents

View file

@ -4,20 +4,22 @@ on:
push:
branches: [main]
paths:
- .github/wiki/**
- '.github/wiki/**'
permissions:
contents: write
jobs:
sync:
name: Sync .github/wiki/ to GitHub Wiki
name: Sync wiki to GitHub Wiki
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Sync wiki pages
- name: Sync .github/wiki/ to GitHub Wiki
uses: Andrew-Chen-Wang/github-wiki-action@v4
with:
path: .github/wiki/
token: ${{ secrets.GITHUB_TOKEN }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ACTOR: ${{ github.actor }}

13
CHANGELOG.md Normal file
View file

@ -0,0 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [2.0.0] - 2026-05-28
### Added
- Initial release

View file

@ -8,14 +8,20 @@ Maps any JavaScript `Date` to the correct NASA moon phase image filename. Two al
The image dataset (~438 MB of hourly WebP photos from NASA's Scientific Visualization Studio) lives in this repository. The npm package ships only the code. Serve images via CDN (jsDelivr, see below) or by cloning the repo and hosting the folders yourself.
## Installation
## Distribution
This package is **not published to npm**. The image dataset (~438 MB) exceeds the npm registry limit. Install via git clone or reference the CDN directly.
```bash
npm install moon-cycle
# or
pnpm add moon-cycle
# Clone the repo to get the code and images together
git clone https://github.com/acamarata/moon-cycle.git
# Or install the code-only package from GitHub Packages / direct git
pnpm add github:acamarata/moon-cycle
```
For most web use cases, you do not need to install the package at all. Use `cdnUrl()` to serve images via jsDelivr directly from the GitHub repository (see API below).
## Quick Start
```ts

View file

@ -1,7 +1,7 @@
{
"name": "moon-cycle",
"version": "2.0.0",
"description": "Maps any date to NASA moon phase imagery via synodic and calendar-year cycles. Lightweight npm package images served via CDN or self-hosted from the GitHub repository.",
"description": "Maps any date to NASA moon phase imagery via synodic and calendar-year cycles. Lightweight npm package \u2014 images served via CDN or self-hosted from the GitHub repository.",
"author": "Aric Camarata",
"license": "MIT",
"main": "./dist/index.cjs",
@ -9,15 +9,11 @@
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs"
}
}
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json"
},
"files": [
"dist/",
@ -34,7 +30,8 @@
"format:check": "prettier --check src/ test.mjs test-cjs.cjs eslint.config.mjs tsup.config.ts",
"pretest": "tsup",
"test": "node --test test.mjs && node --test test-cjs.cjs",
"prepublishOnly": "tsup"
"prepublishOnly": "tsup",
"coverage": "c8 --reporter=lcov --reporter=text node --test"
},
"engines": {
"node": ">=20"
@ -73,5 +70,7 @@
"tsup": "^8.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.56.1"
}
},
"type": "module",
"packageManager": "pnpm@10.11.1"
}