Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/docs-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Deploy Docs

on:
push:
branches:
- main
paths:
- 'website/**'
- 'project/src/**'
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install root dependencies
run: npm ci

- name: Build library
run: npm run build

- name: Copy bundle to website
run: cp dist/astrochart.js website/public/astrochart.js

- name: Install website dependencies
run: npm ci
working-directory: website

- name: Build Astro site
run: npm run build
working-directory: website

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: website/dist

deploy:
needs: build
runs-on: ubuntu-22.04
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
67 changes: 67 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AGENTS.md

## Environment
- Node: use `nvm use 24` if node commands fail
- Website sub-project lives in `website/` with its own `package.json`; run npm commands from there

## Build / Lint / Test
- Install: `npm ci`
- Build: `npm run build` (webpack UMD bundle → `dist/astrochart.js`)
- Lint: `npm run lint` (ESLint, TypeScript source files only)
- Test all: `npm test` (Jest + ts-jest, jsdom environment)
- Test single file: `npx jest project/src/utils.test.ts`
- Test with coverage: `npm run test:coverage`

## Code Style
- **Formatting:** 2-space indent, single quotes, no semicolons, unix line endings, no trailing commas, no `var`
- **Functions:** class methods have a space before parens (`radix (data: AstroData) {`); standalone functions use `export const fn = (...) => { ... }`
- **Naming:** Classes/interfaces PascalCase, methods/variables camelCase, settings keys UPPER_SNAKE_CASE, files lowercase single-word
- **Imports:** default imports for classes, named imports for functions, `import type` for type-only; relative `./` paths, no extensions, no aliases
- **Types:** interfaces/types live in the file where primarily used — no separate types file
- **Tests:** co-located (`foo.test.ts` next to `foo.ts`), use `describe`/`test` (not `it`), prefer `toStrictEqual`, never commit `.only`
- **Errors:** throw plain `Error('descriptive message')`, no custom error classes; null checks use loose equality (`== null`)
- **Docs:** JSDoc on public methods/classes with `@param`, `@return` tags
- **⚠️ Breaking changes:** this is a production library with many consumers — never change public API (exported types, method names, function signatures)

## Adding New Dependencies
- Never write import paths or config shapes from memory for fast-moving packages (Astro, Starlight, etc.)
- After `npm install`, verify real exports: `cat node_modules/<pkg>/package.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(list(d.get('exports',{}).keys()))"`
- Run `npm run build` (or `dev`) after creating the first file — don't build 30 files then discover the config is wrong
- Use `legacy-peer-deps=true` in `.npmrc` when a package's peer range lags behind the latest patch

## Sub-projects isolation (⚠️ hard rule)
- `website/` is a completely separate project — it must **never** affect the library build or tests
- Any new sub-project directory **must** be added to the root `tsconfig.json` `exclude` list AND to the `exclude` regex in `webpack.config.js` before committing
- After adding a sub-project, always run `npm run build` and `npm test` from the **root** to verify isolation

## Website / Astro content rules
- **MDX required for component imports:** Starlight content files that use `import` and JSX component tags **must** have a `.mdx` extension. A `.md` file will print the import statement as plain text and silently ignore all component tags.
- **Multi-instance inline script loading:** When an Astro `is:inline` script dynamically loads an external JS bundle, multiple component instances on the same page will all run simultaneously. Use a shared queue pattern to avoid race conditions:
```javascript
if (window.astrochart) {
initChart()
} else if (document.querySelector('script[src="/astrochart.js"]')) {
window.__astrochartQueue = window.__astrochartQueue || []
window.__astrochartQueue.push(initChart)
} else {
window.__astrochartQueue = [initChart]
const s = document.createElement('script')
s.src = '/astrochart.js'
s.onload = () => { (window.__astrochartQueue || []).forEach(fn => fn()); window.__astrochartQueue = [] }
document.head.appendChild(s)
}
```

## AstroChart library — data shape
The real `AstroData` type (from `project/src/radix.ts`) is:
```typescript
interface AstroData {
planets: Record<string, number[]> // key = symbol name, value = [degrees, retrograde?]
cusps: number[] // exactly 12 degree values
}
```
- **Valid planet keys** (anything else renders as a red fallback circle with no warning):
`Sun`, `Moon`, `Mercury`, `Venus`, `Mars`, `Jupiter`, `Saturn`, `Uranus`, `Neptune`, `Pluto`, `Chiron`, `Lilith`, `NNode`, `SNode`, `Fortune`
- **Cusps** must be an array of **exactly 12** numbers (degrees); fewer or more will throw via `validate()`
- **Retrograde:** second element of a planet array — negative value = retrograde (e.g. `[245.5, -1]`)
- Do **not** use the invented `Planet[]`/`Cusp[]` shape that appears in older placeholder docs — it does not match the library
2 changes: 1 addition & 1 deletion dist/astrochart.js

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions docs/docs-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Docs Deployment Guide

This document explains how the AstroChart documentation site is built and deployed to GitHub Pages directly from this repository.

The live site is served at **https://astrodraw.github.io/AstroChart/**.

---

## What the Workflow Does

The workflow file is located at `.github/workflows/docs-deploy.yml`.

It runs on every push to `main` that touches either `website/**` or `project/src/**`, and can also be triggered manually.

The workflow has two jobs:

### Job 1: `build`

1. **Checkout** — checks out the `AstroChart` repository.
2. **Set up Node.js 20** — installs Node using `actions/setup-node`.
3. **Install root dependencies** — runs `npm ci` at the repo root.
4. **Build library** — runs `npm run build` to produce `dist/astrochart.js` (webpack UMD bundle).
5. **Copy bundle to website** — copies `dist/astrochart.js` → `website/public/astrochart.js` so the live demos can load it.
6. **Install website dependencies** — runs `npm ci` inside `website/`.
7. **Build Astro site** — runs `npm run build` inside `website/`, outputting the static site to `website/dist/`.
8. **Upload Pages artifact** — uploads `website/dist/` as a GitHub Pages artifact using the official `actions/upload-pages-artifact` action.

### Job 2: `deploy`

1. **Deploy to GitHub Pages** — deploys the uploaded artifact using the official `actions/deploy-pages` action. Uses GitHub's OIDC token — no secrets or SSH keys needed.

---

## One-Time Setup (GitHub Pages source)

This only needs to be done once per repository.

1. Go to the repository **Settings → Pages**:
`https://github.com/AstroDraw/AstroChart/settings/pages`
2. Under **Source**, select **GitHub Actions** (not a branch).
3. Click **Save**.

That's it. The workflow handles everything else automatically.

> **Note:** GitHub automatically creates a `github-pages` environment on the first successful deploy. You can see it under **Settings → Environments**.

---

## How to Manually Trigger the Workflow

You can trigger a deploy at any time without pushing code:

1. Go to the **Actions** tab in the AstroChart repository:
`https://github.com/AstroDraw/AstroChart/actions/workflows/docs-deploy.yml`
2. Click **Run workflow**
3. Select the branch (typically `main`)
4. Click **Run workflow**

The workflow will run and deploy the current state of `main` to GitHub Pages.

---

## How to Debug Deploy Failures

### Step 1 — Check the workflow logs

Go to `https://github.com/AstroDraw/AstroChart/actions` and click the failed run. Each step's output is expandable. Common failure points:

| Step | Likely cause |
|---|---|
| Build library | TypeScript compile error or missing dep — run `npm run build` locally |
| Build Astro site | MDX/Astro error — run `cd website && npm run build` locally |
| Upload Pages artifact | `website/dist/` is empty or missing — check the Astro build step above it |
| Deploy to GitHub Pages | Pages source not set to "GitHub Actions" — see one-time setup above |

### Step 2 — Permissions error on deploy

If the deploy job fails with a permissions error:

- Verify the repository **Settings → Pages → Source** is set to **GitHub Actions**, not a branch.
- Verify the workflow has the correct top-level permissions (`pages: write`, `id-token: write`).
- Check that the `deploy` job declares `environment: name: github-pages`.

### Step 3 — Verify the deployed site

After a successful run:

1. Visit `https://astrodraw.github.io/AstroChart/` — homepage should load.
2. Open browser DevTools → Network tab — no 404s for `/AstroChart/astrochart.js`.
3. Click through the sidebar — navigation should work.
4. Open the browser console — no JS errors.

If the site shows stale content, GitHub Pages may have a CDN cache delay of up to 10 minutes after deployment.

---

## Astro base path configuration

The `website/astro.config.mjs` file is configured with:

```js
site: 'https://astrodraw.github.io/AstroChart',
base: '/AstroChart',
```

The `base` option tells Astro to prefix all internal links and asset URLs with `/AstroChart`, which is required for a project repository deployed to a sub-path.

If the site is ever moved to the root URL (`https://astrodraw.github.io/`), remove the `base` option and update `site` accordingly.
71 changes: 71 additions & 0 deletions library-issues/bug-settings-mutation-across-instances.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Bug: `Chart` constructor mutates the shared `default_settings` singleton

**Type:** Bug
**Affects:** `project/src/chart.ts` → `Chart` constructor
**Severity:** Medium — causes cross-instance settings bleed when multiple charts are on the same page with different custom settings

---

## Current behaviour

`default_settings` is imported as a module-level singleton object. In the `Chart` constructor, custom settings are merged into it **in-place** via `Object.assign`:

```typescript
// chart.ts — current constructor
const chartSettings = default_settings // ← just a reference, not a copy
if (settings != null) {
Object.assign(chartSettings, settings) // ← mutates the shared singleton!
...
}
```

Because `chartSettings` is a reference to the same object as `default_settings`, any settings passed to one `Chart` instance permanently modify the module-level default for all subsequent `Chart` instances in the same page/process.

## Reproduction

```javascript
import { Chart } from '@astrodraw/astrochart'

// First chart — custom red background
const chart1 = new Chart('chart1', 600, 600, { COLOR_BACKGROUND: '#ff0000' })

// Second chart — no custom settings passed, expects white background
const chart2 = new Chart('chart2', 600, 600)
// ❌ chart2 ALSO has a red background because default_settings was mutated
```

## Expected behaviour

Each `Chart` instance should have its own isolated copy of the settings. The module-level `default_settings` must remain pristine.

## Suggested fix

Replace the reference assignment with a deep copy:

```typescript
// chart.ts — fixed constructor
constructor (elementId: string, width: number, height: number, settings?: Partial<Settings>) {
// Create a fresh copy of defaults for this instance
const chartSettings: Settings = { ...default_settings }

if (settings != null) {
Object.assign(chartSettings, settings)
if (!('COLORS_SIGNS' in settings)) {
chartSettings.COLORS_SIGNS = [
chartSettings.COLOR_ARIES, chartSettings.COLOR_TAURUS,
// ... rest of sign colours
]
}
}
// ...
}
```

For nested objects (e.g. `ASPECTS`, `DIGNITIES_EXACT_EXALTATION_DEFAULT`) a shallow spread may not be enough — consider `structuredClone(default_settings)` if those nested objects are also mutated downstream.

## Notes

- The bug may be masked in most use cases where only one `Chart` instance is created per page, or where the same settings are reused
- It is reproducible whenever two `Chart` instances with *different* custom settings are created in the same JavaScript context
- Should be covered by a new test: create two Chart instances in sequence with conflicting settings and assert each uses its own values
- The `COLORS_SIGNS` re-computation block inside the `if (settings != null)` branch also references `default_settings.COLOR_*` — after the fix it should reference `chartSettings.COLOR_*` instead (already correct behaviour, just noting for the reviewer)
83 changes: 83 additions & 0 deletions library-issues/improve-validate-unknown-planet-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Improvement: `validate()` does not check for unknown planet keys

**Type:** Improvement (not a breaking bug)
**Affects:** `project/src/utils.ts` → `validate()`
**Discovered during:** Issue #94 — writing demo data for the website

---

## Current behaviour

The `validate()` function checks that:
- `data.planets` exists
- each planet value is an `Array`
- `data.cusps` is an Array of exactly 12 values

It does **not** check whether planet keys are in the set of recognised symbol names.

```typescript
// validate() — current loop (utils.ts)
for (const property in data.planets) {
if (!Array.isArray(data.planets[property])) {
status.messages.push(...)
status.hasError = true
}
// ↑ only validates the value shape — the KEY is never checked
}
```

As a result, passing an unrecognised key (e.g. `NorthNode`, `Vertex`, `PartOfFortune`) **silently succeeds** validation and the library renders a generic red fallback circle at that position — with no warning to the developer.

## How it was discovered

When writing `demoData.ts` for the website, the following keys were used by mistake:

```typescript
// ❌ These do NOT work — wrong key names
NorthNode: [95.45, 0],
SouthNode: [275.45, 0],
Vertex: [325.67, 0]

// ✅ Correct names
NNode: [95.45, 0],
SNode: [275.45, 0],
Fortune: [325.67, 0]
```

The chart appeared to load but no symbols were drawn for those planets — no console error, no validation message.

## Expected behaviour

`validate()` should emit a **warning** (not a hard error, to avoid breaking existing charts) when it encounters a key that is not in the recognised symbol list:

```
"Unknown planet key 'NorthNode'. Valid keys are: Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Chiron, Lilith, NNode, SNode, Fortune."
```

## Suggested fix

```typescript
// In utils.ts, extend validate() with an optional warning step:
const KNOWN_PLANET_KEYS = new Set([
'Sun', 'Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter',
'Saturn', 'Uranus', 'Neptune', 'Pluto', 'Chiron',
'Lilith', 'NNode', 'SNode', 'Fortune'
])

for (const property in data.planets) {
if (!Array.isArray(data.planets[property])) {
status.messages.push(`The planets property '${property}' has to be Array.`)
status.hasError = true
} else if (!KNOWN_PLANET_KEYS.has(property)) {
// Warning only — unknown keys are allowed (custom symbols),
// but we surface the information to help developers catch typos
console.warn(`[AstroChart] Unknown planet key '${property}'. It will render as a fallback symbol.`)
}
}
```

## Notes

- This should be a **`console.warn`**, not an error — unknown keys with a `CUSTOM_SYMBOL_FN` setting are a valid use case
- The `DEBUG` settings flag could gate the warning if preferred
- Should be covered by a new unit test in `utils.test.ts`
Loading
Loading