aiquadtreejs 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms-full.txt CHANGED
@@ -13,139 +13,81 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
13
13
 
14
14
  # aiquadtreejs
15
15
 
16
- [![npm version](https://img.shields.io/npm/v/aiquadtreejs.svg)](https://www.npmjs.com/package/aiquadtreejs)
17
- [![CI](https://github.com/islumina/aiquadtreejs/actions/workflows/ci.yml/badge.svg)](https://github.com/islumina/aiquadtreejs/actions/workflows/ci.yml)
18
- [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
19
- [![AI Generated](https://img.shields.io/badge/AI_Generated-Claude_Code_Opus_4.7_Max-blueviolet.svg)](https://www.anthropic.com/claude-code)
20
- [![繁體中文](https://img.shields.io/badge/lang-繁體中文-red.svg)](README_ZHTW.md)
16
+ Tiny 2D quadtree for per-frame rebuild collision broadphase. Insert AABBs, retrieve candidates, then run precise collision checks yourself.
21
17
 
22
- > A tiny 2D quadtree for per-frame rebuild collision broadphase. `insert` AABBs, `retrieve` candidates, `clear`. Caller does precise hit-testing. Designed for PixiJS games with 500–10,000 active entities.
18
+ > **Status: 0.5.8 - stable 1.0-track surface.** The root entry is the public API.
23
19
 
24
- Part of the [ai\*js micro-runtime ecosystem](https://github.com/islumina) — see also [aifsmjs](https://github.com/islumina/aifsmjs) (FSM), [aiecsjs](https://github.com/islumina/aiecsjs) (ECS), [aibridgejs](https://github.com/islumina/aibridgejs) (cross-context RPC), [aieventjs](https://github.com/islumina/aieventjs) (event emitter), [aipooljs](https://github.com/islumina/aipooljs) (object pool), and [aiaudiojs](https://github.com/islumina/aiaudiojs) (Web Audio shell).
25
-
26
- > **Status: 0.5.6.** `insert()` and `retrieve()` / `retrieveInto()` validate geometry (non-finite coords or negative dimensions throw `QuadtreeError`). `retrieveInto(region, target)` is a steady-state zero-allocation broadphase (reused internal scratch + caller buffer); property-based dedup invariants. ≥95% coverage, ≤2 KB gzip.
27
-
28
- ---
29
-
30
- ## Why aiquadtreejs
31
-
32
- Naïve pairwise collision detection on `N` entities is `O(N²)`. At 1,000 entities that's a million comparisons per frame, which at 60 Hz is already a budget killer; at 10,000 entities it's a non-starter. A quadtree replaces the dense outer loop with a spatial filter: each entity asks "who could I possibly collide with?" and the tree returns a small candidate set — sub-linear on average for well-distributed inputs (worst case is `O(N)` when every object overlaps the query region). The precise hit test then runs only on candidates, dropping the actual comparisons by one or two orders of magnitude in typical game distributions.
33
-
34
- `aiquadtreejs` makes four deliberate trade-offs:
35
-
36
- - **Per-frame rebuild, not move-tracking.** Tracking which leaf an entity migrated into between frames is doable but error-prone; `clear()` + re-`insert()` is faster in practice and easier to reason about. This mirrors the Kontra.js philosophy. (`clear()` resets the root node and drops the child array; subdividing fresh next frame is cheap because the inner subdivision logic touches at most `maxLevels = 4` levels.)
37
- - **Set-based dedup on `retrieve`.** An AABB that straddles a quadrant boundary lands in multiple leaves. Without dedup the caller sees the same candidate two or four times and pays double for the precise hit test. The Set guarantees each candidate appears once.
38
- - **2D AABB only — no 3D, no R-tree, no KD-tree, no Circle / Line primitives.** Those are real techniques for real problems but they sit in a different size class. Keep this library at ≤ 2 KB gzipped and let user-land bring in heavier broadphase when needed.
39
- - **No precise hit-test.** Broadphase libraries that also do collision response always grow. The contract here ends at "here are the candidates"; pixel-perfect or shape-specific tests belong to whatever physics layer you already have.
40
-
41
- Why not just import `@timohausmann/quadtree-ts`? That library is solid and you should use it for stand-alone work. `aiquadtreejs` exists so that an ai*js stack can talk to entity IDs from `aiecsjs` without per-frame object adaptation — `insert({ id: eid, x, y, width, height })` lines up with the SoA columns you already maintain.
42
-
43
- > `aiquadtreejs` is one of the four 0.3-cycle siblings joining the family — alongside [aipooljs](https://github.com/islumina/aipooljs) (object pool), `aieventjs` (typed events; self-built, not a `mitt` fork), and `aiaudiojs` (Web Audio shell over a Howler.js `peerDependency`).
44
-
45
- ---
46
-
47
- ## Quick Start
20
+ ## Install
48
21
 
49
22
  ```bash
50
23
  pnpm add aiquadtreejs
51
24
  ```
52
25
 
53
- ```typescript
26
+ ```ts
54
27
  import { createQuadtree, type AABB } from "aiquadtreejs";
28
+ ```
55
29
 
56
- type Body = { id: number } & AABB;
30
+ ## Quick Start
31
+
32
+ ```ts
33
+ interface Body extends AABB {
34
+ id: number;
35
+ }
57
36
 
58
- // 1. Build the tree once.
59
- const qt = createQuadtree<Body>({
37
+ const tree = createQuadtree<Body>({
60
38
  bounds: { x: 0, y: 0, width: 800, height: 600 },
61
39
  maxObjects: 10,
62
40
  maxLevels: 4,
63
41
  });
64
42
 
65
- // 2. Per frame: clear and re-insert.
66
- function rebuild(entities: Body[]) {
67
- qt.clear();
68
- for (const e of entities) qt.insert(e);
69
- }
70
-
71
- // 3. Per query: broadphase, then precise hit-test.
72
- function nearbyEnemies(player: Body, enemies: Body[]): Body[] {
73
- const region: AABB = { x: player.x - 50, y: player.y - 50, width: 100, height: 100 };
74
- const candidates = qt.retrieve(region);
75
- // Caller filters with precise AABB / pixel test:
76
- return candidates.filter((c) => aabbOverlap(c, region));
77
- }
78
- ```
79
-
80
- Right-open coordinate semantics: `x + width` and `y + height` are exclusive (renderer-neutral; matches common conventions such as PixiJS `getBounds()`).
43
+ const bodies: Body[] = [
44
+ { id: 1, x: 100, y: 100, width: 32, height: 32 },
45
+ { id: 2, x: 400, y: 250, width: 32, height: 32 },
46
+ ];
81
47
 
82
- ---
83
-
84
- ## Capabilities / Limitations
85
-
86
- | Will do (v1) | Won't do |
87
- | --------------------------------------------------------- | ----------------------------------------------------- |
88
- | 2D rectangle quadtree | 3D octree / R-tree / KD-tree |
89
- | `insert()` / `retrieve()` / `clear()` / `dispose()` | Move-tracking (use `clear()` + re-`insert()`) |
90
- | Set-based dedup on `retrieve` (each candidate once) | Precise hit-test (broadphase only) |
91
- | `maxObjects` + `maxLevels` knobs | Auto-rebalance / dynamic depth growth |
92
- | Reuse the root node across frames; resubdivide cheaply | Circle / Line / polygon primitives |
93
- | `dispose()` idempotent; post-dispose calls throw | Persistence / snapshot / serialise (out of scope) |
94
-
95
- ---
96
-
97
- ## API sketch
48
+ tree.clear();
49
+ for (const body of bodies) tree.insert(body);
98
50
 
99
- ```typescript
100
- interface AABB {
101
- x: number;
102
- y: number;
103
- width: number; // x + width is exclusive
104
- height: number; // y + height is exclusive
105
- }
106
-
107
- interface QuadtreeOptions {
108
- bounds: AABB;
109
- maxObjects?: number; // default 10
110
- maxLevels?: number; // default 4 — spanning objects replicate into every
111
- // overlapping child; cost is ~4^L nodes in the worst
112
- // case. Raise with caution (16 → ~4 B nodes).
113
- }
51
+ const candidates = tree.retrieve({ x: 80, y: 80, width: 120, height: 120 });
52
+ ```
114
53
 
115
- interface Quadtree<T extends AABB> {
116
- insert(obj: T): void;
117
- retrieve(region: AABB): T[];
118
- retrieveInto(region: AABB, target: T[]): T[]; // ← new in 0.3.0
119
- clear(): void;
120
- dispose(): void;
121
- readonly disposed: boolean;
122
- }
54
+ ## Core API
123
55
 
124
- class QuadtreeError extends Error {}
125
- class QuadtreeDisposedError extends Error {}
56
+ - `createQuadtree<T extends AABB>({ bounds, maxObjects?, maxLevels? })` creates a tree.
57
+ - `insert(obj)` stores an object reference in overlapping nodes.
58
+ - `retrieve(region)` returns a deduplicated broadphase candidate array.
59
+ - `retrieveInto(region, target)` reuses a caller-owned result array.
60
+ - `clear()` empties the tree for the next frame and clears scratch buffers.
61
+ - `dispose()` is idempotent permanent teardown.
62
+ - Errors: `QuadtreeError`, `QuadtreeDisposedError`.
126
63
 
127
- function createQuadtree<T extends AABB>(opts: QuadtreeOptions): Quadtree<T>;
128
- ```
64
+ ## Model
129
65
 
130
- Full JSDoc lives in [`src/index.ts`](src/index.ts).
66
+ - Coordinates are right-open: `{ x, y, width, height }` covers `[x, x + width)` and `[y, y + height)`.
67
+ - This is a broadphase only. Returned candidates may not actually overlap the query region.
68
+ - Expected usage is per-frame rebuild: `clear()`, insert active bodies, query.
69
+ - Objects spanning quadrant boundaries can be stored in multiple child nodes; results are deduplicated.
70
+ - `maxLevels` has no hard cap. Very high values plus spanning objects can create huge node counts.
131
71
 
132
- ---
72
+ ## Sharp Edges
133
73
 
134
- ## Roadmap
74
+ - Known bug: a zero-size point exactly on the root `left/top` boundary, such as `{ x: bounds.x, y: bounds.y, width: 0, height: 0 }`, is currently ignored by the root overlap check. Zero-size objects away from that root minimum edge are covered by tests. Next code pass should fix the root containment helper and add boundary tests.
75
+ - Fully outside objects are ignored by retrieval.
76
+ - Negative width/height and non-finite coordinates throw.
77
+ - `retrieveInto()` clears the target array before writing results.
78
+ - After `dispose()`, all methods except `dispose()` throw `QuadtreeDisposedError`.
135
79
 
136
- | Version | Adds |
137
- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------- |
138
- | **0.1.0** | `createQuadtree`, `insert` / `retrieve` / `clear` / `dispose`, Set-based dedup, ≥95% coverage, ≤2 KB gzip. |
139
- | **0.3.0** | `retrieveInto(region, target)` zero-alloc API; property-based tests (`fast-check`); STABILITY.md tracking. |
140
- | **0.4.0** | Dependency hygiene (removed unused `tsx`, aligned `fast-check`); 0.3.x public surface frozen for the 1.x track. No runtime API change. |
141
- | **0.5.1** | `insert()` input validation: throws `QuadtreeError` for non-finite coords or negative dimensions; 22 new tests (J1–J15, K1–K7). |
142
- | **0.6+** | Evaluate 3D octree variant (`createOctree<T extends AABB3>`); see `STABILITY.md` for current draft. |
80
+ ## AI Context
143
81
 
144
- ---
82
+ - Short index: [`llms.txt`](llms.txt)
83
+ - Full generated context: [`llms-full.txt`](llms-full.txt)
84
+ - Stability contract: [`STABILITY.md`](STABILITY.md)
85
+ - Current review backlog: [`REVIEW.md`](REVIEW.md)
86
+ - Release history: [`CHANGELOG.md`](CHANGELOG.md)
145
87
 
146
88
  ## License
147
89
 
148
- [MIT](LICENSE).
90
+ MIT
149
91
 
150
92
  ---
151
93
 
@@ -153,216 +95,26 @@ Full JSDoc lives in [`src/index.ts`](src/index.ts).
153
95
 
154
96
  # Changelog
155
97
 
156
- All notable changes to this project are documented here. The format follows
157
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
158
- adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
98
+ All notable changes to aiquadtreejs are summarized here.
159
99
 
160
100
  ## [Unreleased]
161
101
 
162
- ## [0.5.6] - 2026-06-10
163
-
164
- ### Fixed
165
-
166
- - **`retrieve()` / `retrieveInto()` region validation** — both now throw `QuadtreeError` on non-finite coordinates (`NaN`, `±Infinity`) or negative dimensions, mirroring the `insert()` validation shipped in 0.5.1. Previously a `NaN` region silently returned `[]` and a negative-size region produced ghost candidates. Zero-extent regions remain valid. (Review wave 2026-06-10, QDT-S-01.)
167
- - `clear()` now drains the internal dedup scratch and DFS stack, extending the GC guarantee `dispose()` already provided: a tree held alive but not queried after `clear()` no longer pins the previous query's object references. (QDT-B-01.)
168
- - `retrieveSet` snapshots the query region's fields once into a reusable internal scratch region, so a structurally-typed region with re-entrant getters cannot corrupt a walk in progress; steady-state queries remain zero-allocation. (QDT-B-02.)
169
-
170
- ### Changed
171
-
172
- - Supply-chain and release hardening: CI/publish actions SHA-pinned, npm CLI pinned (`11.16.0`) in the OIDC publish job, `permissions: contents: read` on CI, job timeouts, tag↔package.json version guard, `npm publish --ignore-scripts`, and manual publish dispatch now defaults to dry-run. New `verify:docs` gate keeps the README status banners in lockstep with `package.json`. `typecheck` now also type-checks the test suite; `llms-full.txt` embeds `STABILITY.md`.
173
-
174
- ### Docs
175
-
176
- - README status banners refreshed (EN + ZHTW); `maxLevels` JSDoc/README now document the ~4^L spanning-replication cost trade-off; two steady-state test assertions strengthened from lower bounds to exact counts.
177
-
178
- ## [0.5.5] - 2026-06-08
102
+ ## [0.5.8] - 2026-06-14
179
103
 
180
- ### Changed
104
+ - Fixed: a zero-size point on the root left/top minimum boundary (`{ x: bounds.x, y: bounds.y, width: 0, height: 0 }`) is now inserted and retrievable. The root insert gate's right-open overlap test dropped it; a new `rootContains` check is inclusive on the minimum edge and exclusive on the maximum edge, preserving right-open `[x, x+width)` semantics. Boundary regression tests added.
105
+ - Documentation-only slimming pass across README, stability notes, review backlog, and LLM context.
181
106
 
182
- - Project home migrated to the [`islumina`](https://github.com/islumina) GitHub org; the package is now published from there via npm trusted publisher (OIDC + SLSA provenance). Family-wide version alignment at `0.5.5` — no runtime or API changes.
183
-
184
- ## [0.5.2] - 2026-06-05
185
-
186
- ### Docs
187
-
188
- - Review-driven documentation fixes (`README.md`, `README_ZHTW.md`, `llms-full.txt`) and corrected a stale version reference in the `src/index.ts` header comment. No functional change; `dist` byte-identical to 0.5.1.
189
-
190
- ## [0.5.1] - 2026-06-02
191
-
192
- ### Fixed
193
-
194
- - **`insert()` input validation** — `insert()` now throws `QuadtreeError`
195
- when the inserted object contains a non-finite coordinate (`NaN`,
196
- `Infinity`, `-Infinity` in any of `x`, `y`, `width`, `height`) or a
197
- negative dimension (`width < 0` or `height < 0`). Previously, such
198
- objects were silently mishandled (kept before subdivide, silently dropped
199
- after). Zero-extent objects (points / lines with `width=0` and/or
200
- `height=0`) remain valid and are not rejected. The `QuadtreeError`
201
- JSDoc already noted this use was reserved; the behaviour is now
202
- implemented and documented in `STABILITY.md`.
203
-
204
- ### Tests
205
-
206
- - Added J-group (J1–J15): `insert()` throws `QuadtreeError` for
207
- `width<0`, `height<0`, `NaN`/`Infinity` in any of `x/y/width/height`, and
208
- for `null`/`undefined` objects (J14/J15); valid zero-extent objects
209
- (width=0, height=0, mixed) are accepted.
210
- - Added K-group (K1–K7): oversized object appears exactly once in
211
- full-region and per-quadrant sub-queries after subdivide; `retrieveInto`
212
- buffer identity preserved across 60 clear+insert+retrieveInto frames;
213
- negative-origin bounds with negative-coord objects and queries; zero-
214
- extent point at a non-midpoint survives deep subdivision (maxLevels=4)
215
- and is retrievable with a tight region; maxLevels=1 with many same-
216
- quadrant objects — all retrievable, zero duplicates; non-origin bounds
217
- insert+retrieve correctness.
218
-
219
- ## [0.4.0] - 2026-05-29
220
-
221
- Dependency hygiene + stability freeze. **No runtime API addition or behaviour
222
- change** — `dist/index.js` is byte-identical to 0.3.1; consumers see no
223
- difference. This release unifies the ai*js family version line at 0.4.0 and
224
- formally freezes the public surface for the 1.x track.
225
-
226
- ### Changed
227
-
228
- - **devDependencies** — removed unused `tsx` (no script, config, or example in
229
- this package invoked it) and aligned `fast-check` `^3.23.0` → `^4.8.0` to
230
- match the rest of the ai*js family. Both are dev-only and absent from the
231
- published tarball (`files` ships `dist` + docs only), so consumers are
232
- unaffected.
233
-
234
- ### Stability
235
-
236
- - The 0.3.x public surface — `createQuadtree`, the `Quadtree<T>` interface and
237
- all its members (`insert` / `retrieve` / `retrieveInto` / `clear` /
238
- `dispose` / `disposed`), `AABB`, `QuadtreeOptions`, `QuadtreeError`,
239
- `QuadtreeDisposedError` — is declared **frozen for the 1.x track**: it will
240
- not break before a 1.0.0+ major. The 3D octree variant remains a draft
241
- (target v0.6+).
242
-
243
- ### Internal (not shipped to npm)
244
-
245
- - `pnpm audit` reports no known vulnerabilities; `pnpm-lock.yaml` regenerated
246
- after the devDependency changes. Property tests re-run green under
247
- fast-check v4.
248
-
249
- ## [0.3.1] - 2026-05-29
250
-
251
- ### Changed
107
+ ## [0.5.6] - 2026-06-10
252
108
 
253
- - **`retrieveInto` is now genuinely zero-allocation in steady state.** The
254
- internal dedup `Set` and DFS stack are hoisted to the tree instance and
255
- reused across calls (cleared, not re-created) instead of being allocated
256
- per query. Combined with the caller-owned `target` buffer, a per-frame
257
- broadphase loop issuing thousands of `retrieveInto` queries now allocates
258
- nothing once result sizes stabilise — the design goal stated in 0.3.0.
259
- Purely internal: no API, signature, or observable-behaviour change.
260
- `retrieve` still returns a fresh array each call.
261
- - **`dispose()`** now also clears the internal scratch, preserving the
262
- "drops references so the GC can reclaim everything" guarantee.
263
- - **Docs** — `AABB` JSDoc leads with a renderer-neutral right-open
264
- definition (PixiJS `getBounds()` demoted to a compatibility note);
265
- `dispose()` JSDoc now lists `retrieveInto` among the post-dispose
266
- throwers; the 0.3.0 changelog note reworded ("observationally identical"
267
- rather than "byte-for-byte"); `STABILITY.md` exports list now includes
268
- the `Quadtree<T>` interface.
109
+ - Hardened retrieve validation, scratch cleanup, and size-budget docs.
110
+ - Kept root quadtree API stable and regenerated generated LLM context.
269
111
 
270
- ### Internal (not shipped to npm)
271
-
272
- - Property test `prop2` uses `fc.uniqueArray` keyed on `id` instead of an
273
- early-return guard that skipped ~39% of runs on id collisions; the
274
- id-uniqueness invariant now runs on every case. Property generators mix
275
- in zero-extent midpoint points (`fc.oneof`) so the dedup invariants
276
- exercise the G4 regression shape, not just deterministic fixtures.
277
- - Tightened `I4` (`toBe(1)`); added `I13` (retrieve fresh-array vs
278
- retrieveInto buffer-reuse contrast — backward-compat lock) and `I14`
279
- (interleaved retrieve / retrieveInto correctness — shared-scratch guard).
280
-
281
- ### Notes
282
-
283
- - `dist/index.js` runtime differs from 0.3.0 only by the scratch-hoist
284
- optimisation; the public API and all observable behaviour are unchanged.
285
-
286
- ## [0.3.0] - 2026-05-29
287
-
288
- ### Added
289
-
290
- - **`retrieveInto(region: AABB, target: T[]): T[]`** — zero-allocation
291
- variant of `retrieve` for hot-path callers. Clears `target`, walks
292
- the same iterative DFS + Set dedup as `retrieve`, writes results
293
- into `target`, returns `target`. The returned reference equals the
294
- argument — callers can hold a permanent buffer and pass it every
295
- frame to eliminate the result-array allocation churn (5,000+ calls
296
- per frame in typical bullet-hell broadphase loops).
297
- - **`STABILITY.md`** — explicit stable vs experimental API tracking.
298
- Includes a v0.6+ 3D octree draft (no source code in this release).
299
- - **Property-based tests** via `fast-check` — 4 invariants covering
300
- `retrieve` dedup and `retrieveInto` identity / length / content
301
- equivalence. Adds `fast-check` to `devDependencies`.
302
-
303
- ### Changed
304
-
305
- - **`Quadtree.clear` JSDoc** — corrected the claim that "internal
306
- node objects are reused across frames"; only the root node is
307
- reused, child nodes are released on `clear()` and re-created when
308
- subdivision next triggers. No runtime behaviour change.
309
- - **README Roadmap / Status / API sketch** — synced to v0.3.0,
310
- including the `retrieveInto(region, target)` signature (the
311
- pre-0.2 Roadmap entry was a single-argument draft).
312
-
313
- ### Notes
314
-
315
- - `retrieve` behaviour is observationally identical to 0.1.1: the
316
- internal refactor extracts a shared `retrieveSet` helper that both
317
- `retrieve` and `retrieveInto` call, but `Set` insertion order →
318
- `Array.from` order is preserved by spec.
319
- - Bundle size: ≤ 2 KB gzip budget still ~50% headroom after this
320
- release (expected ~1050-1090 B gzip).
321
-
322
- ## [0.1.1] - 2026-05-28
323
-
324
- ### Changed (CI)
325
-
326
- - **`publish.yml` now triggers on `push: tags: ["v*"]`** (was `workflow_dispatch` only). Aligns with the trigger used by `aifsmjs` / `aiecsjs` / `aibridgejs`. Tag push now automatically runs the OIDC trusted publish.
327
- - **`npm publish --provenance --access public`** — the workflow now emits a [sigstore provenance attestation](https://docs.npmjs.com/generating-provenance-statements) so consumers can verify the tarball was built by this workflow on this commit.
328
-
329
- No runtime / source / API changes from 0.1.0. **0.1.1 is also the first version to actually land on npm — 0.1.0 was tagged in git but never published to npm.** Production bundles are byte-identical to the 0.1.0 git tag.
330
-
331
- ## [0.1.0] - 2026-05-28
332
-
333
- ### Added
334
-
335
- - `createQuadtree({ bounds, maxObjects, maxLevels })` factory — 2D AABB broadphase.
336
- - `insert(obj)` / `retrieve(region)` / `clear()` / `dispose()` lifecycle.
337
- - Set-based dedup on `retrieve()` so objects spanning multiple quadrants are returned once.
338
- - Per-frame rebuild model — `clear()` reuses internal node objects (zero GC churn frame-to-frame).
339
- - `disposed` read-only flag; post-dispose calls throw `QuadtreeDisposedError`.
340
- - Test coverage ≥95% statements / lines / functions / ≥90% branches (~30 it() blocks, groups A–H).
341
- - Size budget: ≤2 KB gzip.
342
- - Dual ESM + CJS build via `tsup` with `minify: true`; `sideEffects: false`; zero runtime dependencies.
343
-
344
- ## [0.0.1] - 2026-05-28
345
-
346
- ### Added (scaffold)
347
-
348
- - Full package scaffold landed (`package.json`, `tsconfig.json`,
349
- `tsconfig.test.json`, `tsup.config.ts`, `vitest.config.ts`, `biome.json`,
350
- `scripts/{verify-exports,check-size,build-llms-full}.mjs`,
351
- `test/scaffold.test.ts`, `examples/.gitkeep`, `.github/workflows/{ci,publish}.yml`,
352
- `llms.txt`, `llms-full.txt`).
353
- - `src/index.ts` remains a `throw` stub exposing the frozen 0.1.0 API surface
354
- (`createQuadtree`, `Quadtree<T>`, `QuadtreeOptions`, `AABB`,
355
- `QuadtreeError`, `QuadtreeDisposedError`).
356
- - `pnpm typecheck && pnpm lint && pnpm coverage && pnpm build &&
357
- pnpm verify:exports && pnpm verify:llms && pnpm check:size` walks clean
358
- against a single placeholder test.
359
- - Coverage thresholds temporarily set to `0/0/0/0`; tightened to
360
- `95/90/100/100` in 0.1.0 with real tests.
361
- - Size budget temporarily set to 3 KB gzip; tightened to the 2 KB README
362
- target in 0.1.0.
363
- - Publish workflow exists but trigger is `workflow_dispatch` only — no
364
- accidental npm release on tag push until 0.1.0.
112
+ ## Older releases
365
113
 
114
+ - `0.5.5` through `0.5.1` focused on release hygiene, docs accuracy, and validation/retrieve regressions.
115
+ - `0.4.0` declared the stable ai*js quadtree surface.
116
+ - `0.3.x` added `retrieveInto()` and zero-allocation query paths.
117
+ - `0.1.x` introduced `createQuadtree`, `AABB`, `Quadtree`, and error classes.
366
118
 
367
119
  ---
368
120
 
@@ -370,118 +122,31 @@ No runtime / source / API changes from 0.1.0. **0.1.1 is also the first version
370
122
 
371
123
  # aiquadtreejs Stability
372
124
 
373
- This document tracks which public API is **stable** (subject to
374
- semver-major break only) vs **experimental** (subject to change without
375
- notice). Consumers should treat anything outside the Stable section as
376
- unfit for production reliance.
377
-
378
- ---
379
-
380
- ## Stable
381
-
382
- The following are stable (since v0.3.0) and, as of **v0.4.0**, formally
383
- **frozen for the 1.x track** — they will not break before a major version
384
- bump (v1.0.0+).
385
-
386
- ### Exports
387
-
388
- - `createQuadtree<T extends AABB>(opts: QuadtreeOptions): Quadtree<T>`
389
- - `Quadtree<T extends AABB>` — the tree interface (`insert` / `retrieve` / `retrieveInto` / `clear` / `dispose` / `disposed`)
390
- - `AABB` — `{ x, y, width, height }`, right-open semantics
391
- - `QuadtreeOptions` — `{ bounds, maxObjects?, maxLevels? }`
392
- - `QuadtreeError` — thrown by validation failures in `createQuadtree` and by `insert()` for invalid object geometry (non-finite coordinates, negative width/height)
393
- - `QuadtreeDisposedError` — thrown by any method on a disposed tree
394
-
395
- ### `Quadtree<T>` interface
396
-
397
- | Member | Stability | Since |
398
- |---|---|---|
399
- | `insert(obj)` | Stable | 0.1.0 |
400
- | `retrieve(region)` | Stable | 0.1.0 |
401
- | `retrieveInto(region, target)` | Stable | 0.3.0 |
402
- | `clear()` | Stable | 0.1.0 |
403
- | `dispose()` | Stable | 0.1.0 |
404
- | `disposed` (readonly) | Stable | 0.1.0 |
405
-
406
- ### Behaviour guarantees
407
-
408
- - Dedup: `retrieve` / `retrieveInto` return each candidate exactly once
409
- even if it spans multiple quadrants.
410
- - Right-open AABB: `x + width` and `y + height` are exclusive
411
- (renderer-neutral; matches common conventions such as PixiJS
412
- `getBounds()`).
413
- - `insert()` validates the inserted object: throws `QuadtreeError` if any
414
- of `x`, `y`, `width`, or `height` is non-finite (`NaN`, `Infinity`,
415
- `-Infinity`), or if `width` or `height` is negative. Zero-extent objects
416
- (points with `width=0` and/or `height=0`) are valid and accepted.
417
- - `retrieve()` and `retrieveInto()` validate the query region with the same
418
- rules: non-finite coordinates or negative dimensions throw `QuadtreeError`.
419
- Zero-extent regions are valid (they still query any overlapping node).
420
- - `maxLevels` has no upper-bound cap. Callers that raise it above the default
421
- of `4` should be aware that spanning objects (those overlapping multiple
422
- quadrant boundaries) are copied into every overlapping child node;
423
- worst-case node count is ~4^maxLevels. The default is safe for typical game
424
- scenes with 500–10,000 entities.
425
- - `dispose()` is idempotent; subsequent public-method calls throw
426
- `QuadtreeDisposedError`.
427
- - All methods destructure cleanly: `const { insert } = qt; insert(obj)`
428
- works without `this` binding.
125
+ ## Stable Surface
429
126
 
430
- ---
431
-
432
- ## Experimental / Draft
433
-
434
- These ideas are **not implemented** and have **no source code** in the
435
- package. They are recorded here so consumers and contributors can see
436
- the intended direction.
127
+ | Surface | Status | Notes |
128
+ | --- | --- | --- |
129
+ | `createQuadtree()` | Stable | Root factory. |
130
+ | `AABB`, `QuadtreeOptions`, `Quadtree<T>` | Stable | Public types. |
131
+ | `insert`, `retrieve`, `retrieveInto`, `clear`, `dispose` | Stable | Main methods. |
132
+ | Error classes | Stable | `QuadtreeError`, `QuadtreeDisposedError`. |
437
133
 
438
- ### 3D octree variant (target: v0.6+)
134
+ ## Behavioral Contract
439
135
 
440
- A 3D variant for platformer / 2.5D broadphase queries.
136
+ - Bounds use right-open coordinates.
137
+ - Inserted object references are not cloned.
138
+ - Retrieval is broadphase and deduplicated.
139
+ - `retrieveInto()` preserves target array identity and clears it first.
140
+ - `clear()` drains node contents and scratch references.
141
+ - `dispose()` is idempotent and permanent.
441
142
 
442
- ```typescript
443
- // Draft only — no implementation in v0.3.x.
444
- interface AABB3 {
445
- x: number; y: number; z: number;
446
- width: number; height: number; depth: number;
447
- }
448
-
449
- interface Octree<T extends AABB3> {
450
- insert(obj: T): void;
451
- retrieve(region: AABB3): T[];
452
- retrieveInto(region: AABB3, target: T[]): T[];
453
- clear(): void;
454
- dispose(): void;
455
- readonly disposed: boolean;
456
- }
143
+ ## Current Caveat
457
144
 
458
- export function createOctree<T extends AABB3>(opts: {
459
- bounds: AABB3;
460
- maxObjects?: number;
461
- maxLevels?: number;
462
- }): Octree<T>;
463
- ```
464
-
465
- Open questions:
466
- - Is a 2.5D quadtree with z-binning sufficient for typical platformer
467
- collision (likely yes)?
468
- - Acceptable size increase for the octree path (target ≤ 1500 B gzip
469
- additional)?
470
-
471
- Implementation is gated on a v0.5+ game actually needing it. Until
472
- then, consumers requiring 3D broadphase should pull a dedicated
473
- library (e.g. `octree-ts`).
474
-
475
- ---
145
+ A zero-size point on the root minimum `x/y` boundary is known to be ignored by the current root overlap check. Treat this as a documented bug, not a stable behavior.
476
146
 
477
- ## Out of scope (will not implement)
147
+ ## Out of Scope
478
148
 
479
- - Move-tracking (per-frame rebuild via `clear()` + `insert()` is the
480
- intended pattern; see README "Why aiquadtreejs").
481
- - Precise hit-test (broadphase only; caller does narrow-phase).
482
- - Circle / Line / polygon primitives.
483
- - Persistence / serialisation.
484
- - KD-tree / R-tree variants.
149
+ Precise collision checks, physics integration, spatial hashing, and 3D octrees are outside the current package.
485
150
 
486
151
  ---
487
152
 
@@ -489,80 +154,31 @@ library (e.g. `octree-ts`).
489
154
 
490
155
  # Contributing to aiquadtreejs
491
156
 
492
- Thanks for taking the time to look. aiquadtreejs is a deliberately small
493
- library (target ≤ 2 KB gzip); contributions that keep the surface narrow
494
- are easier to accept than ones that expand it.
157
+ Keep the tree small, allocation-aware, and explicit about broadphase semantics.
495
158
 
496
- ## Quick start
159
+ ## Local workflow
497
160
 
498
161
  ```bash
499
162
  pnpm install
500
- pnpm test # vitest
501
- pnpm coverage # vitest with v0.1.0 thresholds (95/90/100/100)
502
- pnpm typecheck # tsc --noEmit on strict mode
503
- pnpm lint # biome check
504
- pnpm build # tsup; dual ESM/CJS + .d.ts
505
- pnpm verify:exports # ensures package.json#exports matches dist/
506
- pnpm verify:llms # ensures llms-full.txt is in sync with README + CHANGELOG
507
- pnpm check:size # gzip per subpath against the size budget
163
+ pnpm typecheck
164
+ pnpm test
165
+ pnpm verify:docs
166
+ pnpm build:llms
167
+ pnpm verify:llms
168
+ pnpm check:size
508
169
  ```
509
170
 
510
- The full pre-publish gate is `pnpm prepublishOnly`, which runs typecheck,
511
- lint, coverage (with thresholds), build, exports verification, llms drift
512
- check, and size budget check — in that order.
513
-
514
- ## What gets in easily
515
-
516
- - Bug fixes with a failing test added first
517
- - README / typing corrections
518
- - Tests that lock down existing behaviour (especially the Set-dedup
519
- invariant on `retrieve`)
520
- - Performance work that keeps per-frame `clear()` + bulk `insert()` zero-GC
521
-
522
- ## What needs discussion first
523
-
524
- - Anything that changes the public surface (`createQuadtree`, `Quadtree<T>`,
525
- `AABB`, `QuadtreeOptions`, error classes)
526
- - Move-tracking / dynamic update (explicit non-goal — `clear()` + re-insert
527
- is faster in practice for ≤ 10k entities)
528
- - Circle / Line / Polygon shape primitives (out of 2D AABB broadphase
529
- scope; bring your own user-land wrapper)
530
- - 3D octree / R-tree / KD-tree (different size class; out of scope)
531
- - Anything that pushes the core gzip past 2 KB
532
-
533
- ## Design principles
534
-
535
- aiquadtreejs follows the ai*js library-core priority order:
536
-
537
- > Security > Correctness > Simplicity > YAGNI > Performance
538
-
539
- Key invariants:
540
-
541
- - `retrieve()` deduplicates with a `Set` so each candidate appears once
542
- regardless of how many quadrants it spans.
543
- - `clear()` reuses internal node objects across frames; no per-frame node
544
- allocation.
545
- - `x + width` and `y + height` are **right-open** (exclusive), matching
546
- PixiJS `getBounds()` semantics.
547
- - `dispose()` is idempotent.
548
-
549
- ## Commit & PR style
550
-
551
- - Commit messages: imperative subject under 70 chars; body explains *why*.
552
- - PRs: keep scope to one topic. Link the issue if any.
553
- - Tests required for any behaviour change. Property-based tests welcome
554
- for invariants (especially the Set-dedup property).
171
+ Run `pnpm lint` before PRs. If docs change, regenerate `llms-full.txt`.
555
172
 
556
- ## Reporting issues
173
+ ## Rules
557
174
 
558
- - Minimal reproduction welcome (paste the smallest insert / retrieve
559
- sequence that shows the bug).
560
- - For security issues, please email the maintainer rather than filing
561
- publicly.
175
+ - Preserve right-open coordinate semantics.
176
+ - Add tests for root edges, quadrant boundaries, zero-size objects, `retrieveInto()`, `clear()`, and dispose.
177
+ - Do not turn broadphase results into precise collision promises.
178
+ - Keep allocation behavior visible in docs and tests.
562
179
 
563
180
  ## License
564
181
 
565
- By contributing, you agree your changes will be licensed under the MIT
566
- license that covers this project.
182
+ MIT
567
183
 
568
184
  ---
package/llms.txt CHANGED
@@ -1,5 +1,9 @@
1
1
  # aiquadtreejs
2
2
 
3
- > A tiny 2D quadtree for per-frame rebuild collision broadphase. Insert AABBs, retrieve candidates with Set-based dedup, clear, dispose. Caller does precise hit-testing. Designed for PixiJS games with 500–10,000 active entities. Zero runtime dependencies. Part of the ai*js micro-runtime ecosystem.
3
+ Small 2D quadtree broadphase with insert, retrieve, retrieveInto, clear, and dispose.
4
4
 
5
- For the full LLM context (README + CHANGELOG + CONTRIBUTING concatenated), fetch [llms-full.txt](llms-full.txt).
5
+ - Start: README.md
6
+ - Stability: STABILITY.md
7
+ - Current backlog: REVIEW.md
8
+ - Changelog: CHANGELOG.md
9
+ - Full generated context: llms-full.txt