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
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,35 @@
- `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
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`
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"declaration": true,
"strictNullChecks": true
},
"exclude": ["project/src/**/*.test.ts", "dist", "website"]
"exclude": ["project/src/**/*.test.ts", "project/__tests__", "dist", "website"]
}
1 change: 1 addition & 0 deletions website/public/astrochart.js

Large diffs are not rendered by default.

Loading
Loading