Files
rust_browser/_bmad-output/implementation-artifacts/3-10-web-api-exposure.md
Zachary D. Rowitsch fb64ca1d34
All checks were successful
ci / fast (linux) (push) Successful in 7m9s
Create story files for Epic 3 stories 3.5-3.10
Create comprehensive implementation-ready story files for the remaining
Epic 3 (JavaScript Engine Maturity) stories and update sprint status
from backlog to ready-for-dev:

- 3.5: Built-in Completeness (Array/String/Object)
- 3.6: Built-in Completeness (Date/RegExp/Map/Set)
- 3.7: WeakRef, FinalizationRegistry & Strict Mode Edge Cases
- 3.8: DOM Bindings via web_api
- 3.9: Event Dispatch Completeness
- 3.10: Web API Exposure (fetch, Math, setInterval, rAF)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 08:06:03 -04:00

386 lines
23 KiB
Markdown

# Story 3.10: Web API Exposure
Status: ready-for-dev
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
## Story
As a web developer using JavaScript,
I want fetch, console, and scheduling APIs to work correctly,
So that network requests, debugging, and async task scheduling function properly.
## Acceptance Criteria
1. **fetch() API:** `fetch(url)` returns a Promise that resolves to a Response object. `response.status`, `.statusText`, `.ok`, `.headers` reflect HTTP response. `response.text()` returns Promise resolving to body string. `response.json()` returns Promise resolving to parsed JSON. `response.url` reflects final URL. Uses `NetworkStack::load_sync()` internally (synchronous load, Promise wraps result). Per Fetch Standard §5.
2. **setInterval / clearInterval:** `setInterval(callback, delay)` returns numeric ID and calls `callback` repeatedly every `delay` ms. `clearInterval(id)` stops the repeating calls. Per HTML §8.6.
3. **requestAnimationFrame:** `requestAnimationFrame(callback)` registers `callback` to run before next paint. Callback receives a `DOMHighResTimeStamp` argument (milliseconds since page load). Callbacks execute in registration order. `cancelAnimationFrame(id)` cancels a registered callback. Per HTML §8.10.
4. **Console completeness:** `console.dir(obj)` outputs object with enumerable properties. `console.table(data)` outputs tabular data (simplified: format as key-value pairs). `console.assert(condition, ...args)` logs only if condition is falsy. `console.count(label)` / `console.countReset(label)` track call counts. `console.time(label)` / `console.timeEnd(label)` measure elapsed time. Per Console Standard.
5. **Global utility functions:** `parseInt(string, radix)`, `parseFloat(string)`, `isNaN(value)`, `isFinite(value)`, `Number.isNaN(value)`, `Number.isFinite(value)`, `Number.isInteger(value)`, `Number.parseInt(string, radix)`, `Number.parseFloat(string)`. Per ECMAScript §19.2.
6. **Math object:** `Math.PI`, `Math.E`, `Math.abs`, `Math.ceil`, `Math.floor`, `Math.round`, `Math.trunc`, `Math.min`, `Math.max`, `Math.pow`, `Math.sqrt`, `Math.random`, `Math.log`, `Math.log2`, `Math.log10`, `Math.sin`, `Math.cos`, `Math.tan`, `Math.sign`, `Math.fround`, `Math.clz32`. Per ECMAScript §21.3.
7. **URI encoding:** `encodeURI(string)`, `decodeURI(string)`, `encodeURIComponent(string)`, `decodeURIComponent(string)`. Per ECMAScript §19.2.6.
8. **Promise.all / Promise.race / Promise.allSettled / Promise.any:** Complete the Promise static combinator methods. Per ECMAScript §27.2.4.
9. **Integration tests verify each API**, and `just ci` passes.
## What NOT to Implement
- **No `fetch()` with Request objects** -- only string URL argument. No custom headers, methods, or body. POST/PUT deferred.
- **No `Response.arrayBuffer()`/`Response.blob()`/`Response.formData()`** -- only `.text()` and `.json()` body methods.
- **No `Headers` object** -- `response.headers` returns a simplified object with `.get(name)` method only.
- **No streaming fetch** -- `Response.body` (ReadableStream) out of scope.
- **No `AbortController`/`AbortSignal`** -- fetch cancellation deferred.
- **No `window.location`/`window.navigator`/`window.history`** -- browser shell APIs deferred to Epic 5.
- **No `window.getComputedStyle()`** -- requires layout data access from JS, complex threading. Deferred.
- **No `localStorage`/`sessionStorage`** -- deferred to Epic 6.
- **No `URL` constructor** -- URL parsing from JS deferred.
- **No `TextEncoder`/`TextDecoder`** -- encoding APIs deferred.
- **No `atob()`/`btoa()`** -- Base64 encoding deferred.
- **No `crypto.getRandomValues()`** -- Web Crypto deferred.
- **No `performance.now()`** -- Performance API deferred.
## Files to Modify
| File | Change |
|------|--------|
| `crates/web_api/src/dom_host/host_environment.rs` | Add `fetch()` to `call_global_function()`. Add `setInterval`/`clearInterval`/`requestAnimationFrame`/`cancelAnimationFrame`. |
| `crates/web_api/src/dom_host/fetch_bridge.rs` | **New file** -- fetch() implementation: create Response HostObject, wire to NetworkStack::load_sync(), return Promise. |
| `crates/web_api/src/scheduling.rs` | Add interval task support (repeating timers). Add animation frame callback queue. |
| `crates/web_api/src/lib.rs` | Add response storage for fetch. Add rAF callback list. Add advance_animation_frame() method. |
| `crates/js_vm/src/interpreter/mod.rs` | Add `Math` object to `setup_builtins()`. Add `parseInt`/`parseFloat`/`isNaN`/`isFinite` as global functions. Add `encodeURI`/`decodeURI`/`encodeURIComponent`/`decodeURIComponent`. |
| `crates/js_vm/src/interpreter/math_builtins.rs` | **New file** -- Math object with all static methods and constants. |
| `crates/js_vm/src/interpreter/global_builtins.rs` | **New file** -- `parseInt`, `parseFloat`, `isNaN`, `isFinite`, URI encoding/decoding functions. |
| `crates/js_vm/src/interpreter/builtins.rs` | Add `console.dir`, `console.table`, `console.assert`, `console.count`/`countReset`, `console.time`/`timeEnd` to console dispatch. |
| `crates/web_api/src/promise.rs` | Add `Promise.all()`, `Promise.race()`, `Promise.allSettled()`, `Promise.any()` static methods. |
| `crates/js_vm/src/interpreter/primitive_builtins.rs` | Add `Number.isNaN`, `Number.isFinite`, `Number.isInteger`, `Number.parseInt`, `Number.parseFloat` to Number constructor. |
| `crates/js_vm/src/interpreter/tests/math_tests.rs` | **New file** -- Math object unit tests. |
| `crates/js_vm/src/interpreter/tests/global_tests.rs` | **New file** -- parseInt/parseFloat/isNaN/isFinite/URI encoding tests. |
| `tests/js_scheduling.rs` | Add setInterval, requestAnimationFrame tests. |
| `tests/js_tests.rs` | Add fetch, Promise combinator tests. |
| `docs/JavaScript_Implementation_Checklist.md` | Check off Math, parseInt/parseFloat, URI encoding, fetch, setInterval, rAF items. |
| `docs/old/js_feature_matrix.md` | Update Web API coverage. |
## Tasks / Subtasks
- [ ] Task 1: Math object (AC: #6)
- [ ] 1.1 Create `crates/js_vm/src/interpreter/math_builtins.rs`:
- `setup_math_builtins(env: &mut Environment)` -- registers `Math` as a plain object (NOT a constructor)
- Constants: `Math.PI`, `Math.E`, `Math.LN2`, `Math.LN10`, `Math.LOG2E`, `Math.LOG10E`, `Math.SQRT2`, `Math.SQRT1_2`
- All constants use Rust `std::f64::consts`
- [ ] 1.2 Implement Math static methods:
- `abs(x)`, `ceil(x)`, `floor(x)`, `round(x)`, `trunc(x)` -- use Rust f64 methods
- `min(...values)`, `max(...values)` -- handle 0 args (Infinity/-Infinity), NaN propagation
- `pow(base, exp)`, `sqrt(x)`, `cbrt(x)`, `hypot(...values)`
- `log(x)` (natural), `log2(x)`, `log10(x)`, `exp(x)`, `expm1(x)`, `log1p(x)`
- `sin(x)`, `cos(x)`, `tan(x)`, `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)`
- `sinh(x)`, `cosh(x)`, `tanh(x)`, `asinh(x)`, `acosh(x)`, `atanh(x)`
- `sign(x)`, `fround(x)`, `clz32(x)`, `imul(a, b)`
- `random()` -- use `std::collections::hash_map::RandomState` or simple PRNG (no `rand` crate dependency)
- [ ] 1.3 Wire `Math` into `setup_builtins()` in `mod.rs`
- [ ] 1.4 Implement as NativeFunction values on the Math object:
- Each method is `JsValue::NativeFunction { name, func }` set on Math object
- Math is a global object, not a constructor (no `new Math()`)
- [ ] 1.5 Add unit tests in `crates/js_vm/src/interpreter/tests/math_tests.rs`:
- Constants have correct values
- Each method with basic inputs and edge cases (NaN, Infinity, -0)
- `Math.random()` returns [0, 1)
- `Math.min()`/`Math.max()` with no args
- `Math.round(-0.5)` edge case
- [ ] Task 2: Global utility functions (AC: #5, #7)
- [ ] 2.1 Create `crates/js_vm/src/interpreter/global_builtins.rs`:
- `setup_global_builtins(env: &mut Environment)`
- [ ] 2.2 Implement `parseInt(string, radix)` (ECMAScript §19.2.5):
- Strip leading whitespace
- Handle `0x`/`0X` prefix for hex (radix=16)
- Parse digits for given radix (2-36), default radix=10
- Return NaN if no valid digits
- Handle leading `+`/`-` sign
- [ ] 2.3 Implement `parseFloat(string)` (ECMAScript §19.2.4):
- Strip leading whitespace
- Parse decimal number (including scientific notation)
- Return NaN if no valid number
- [ ] 2.4 Implement `isNaN(value)` / `isFinite(value)` (ECMAScript §19.2.2/§19.2.3):
- `isNaN`: convert to number first, then check NaN (loose)
- `isFinite`: convert to number first, then check finite (loose)
- [ ] 2.5 Add Number static methods to `primitive_builtins.rs`:
- `Number.isNaN(value)` -- strict: no type coercion, true only for actual NaN
- `Number.isFinite(value)` -- strict: no type coercion
- `Number.isInteger(value)` -- check if value is integer
- `Number.parseInt(string, radix)` -- same as global parseInt
- `Number.parseFloat(string)` -- same as global parseFloat
- `Number.MAX_SAFE_INTEGER`, `Number.MIN_SAFE_INTEGER`, `Number.EPSILON`, `Number.MAX_VALUE`, `Number.MIN_VALUE`, `Number.POSITIVE_INFINITY`, `Number.NEGATIVE_INFINITY`, `Number.NaN`
- [ ] 2.6 Implement URI encoding/decoding functions:
- `encodeURI(string)` -- encode URI, preserve `;,/?:@&=+$-_.!~*'()#` and alphanumeric
- `decodeURI(string)` -- decode %XX sequences (preserve reserved chars)
- `encodeURIComponent(string)` -- encode all except `-_.!~*'()`
- `decodeURIComponent(string)` -- decode all %XX sequences
- Use `percent-encoding` crate (already a dependency) or manual UTF-8 encoding
- [ ] 2.7 Wire all global functions into `setup_builtins()` in `mod.rs`
- [ ] 2.8 Add unit tests in `crates/js_vm/src/interpreter/tests/global_tests.rs`
- [ ] Task 3: fetch() API (AC: #1)
- [ ] 3.1 Create `crates/web_api/src/dom_host/fetch_bridge.rs`:
- Response storage: `HashMap<u64, FetchResponse>` in WebApiFacade
- `FetchResponse` struct: `status: u16`, `status_text: String`, `url: String`, `headers: HashMap<String, String>`, `body: String`, `ok: bool`
- ID range: `FETCH_RESPONSE_ID_BASE` (new range in ID space)
- [ ] 3.2 Implement `fetch(url)` in `call_global_function()`:
- Accept URL string argument
- Use `NetworkStack::load_sync()` via DomHost's network reference (wired in Story 3.4)
- If network unavailable → reject Promise with TypeError
- Create `FetchResponse` from HTTP response (status, headers, body)
- Create Promise, resolve with Response HostObject
- Return the Promise
- **Note**: load_sync() is blocking. fetch() wraps it in a Promise for API compatibility, but resolution is synchronous (same pattern as dynamic import).
- [ ] 3.3 Implement Response HostObject properties via `get_property()`:
- `"status"` → Number (HTTP status code)
- `"statusText"` → String (HTTP status text)
- `"ok"` → Boolean (status 200-299)
- `"url"` → String (final URL)
- `"headers"` → Headers HostObject (simplified)
- [ ] 3.4 Implement Response methods via `call_method()`:
- `.text()` → Promise resolving to body string
- `.json()` → Promise resolving to JSON.parse(body) result
- Promises resolve synchronously (body already available)
- [ ] 3.5 Implement simplified Headers HostObject:
- `.get(name)` → return header value string or null (case-insensitive lookup)
- `.has(name)` → boolean
- [ ] 3.6 Handle fetch errors:
- Network error → reject Promise with TypeError("Failed to fetch")
- Invalid URL → reject Promise with TypeError
- HTTP errors (4xx, 5xx) → resolve normally (Response.ok = false), NOT reject
- [ ] 3.7 Add integration tests:
- `fetch("http://...")` returns Response with status, text(), json()
- Network error rejects Promise
- 404 response resolves with `response.ok === false`
- `response.headers.get("content-type")` works
- [ ] Task 4: setInterval and requestAnimationFrame (AC: #2, #3)
- [ ] 4.1 Add interval support to `scheduling.rs`:
- `IntervalTask` struct: `id: u32`, `callback: JsValue`, `interval_ms: u64`, `next_fire_at: u64`
- When interval fires: re-enqueue with `next_fire_at += interval_ms`
- `add_interval(callback, interval_ms) -> u32` returns ID
- `cancel_interval(id)` removes from queue
- [ ] 4.2 Implement `setInterval`/`clearInterval` in `call_global_function()`:
- `setInterval(callback, delay)` → register repeating timer, return ID
- `clearInterval(id)` → cancel timer
- IDs should be in same namespace as setTimeout (unified timer ID space)
- [ ] 4.3 Add requestAnimationFrame support:
- `RafCallback` struct: `id: u32`, `callback: JsValue`
- `raf_queue: Vec<RafCallback>` in WebApiFacade
- `requestAnimationFrame(callback)` → push to raf_queue, return ID
- `cancelAnimationFrame(id)` → remove from queue
- `flush_raf_callbacks(timestamp_ms: f64)` → invoke all registered callbacks with timestamp, clear queue
- [ ] 4.4 Wire rAF into render loop:
- In `crates/app_browser/src/event_handler.rs`, call `flush_raf_callbacks()` before paint
- Pass `performance.now()` equivalent timestamp (ms since page load)
- [ ] 4.5 Add tests:
- `setInterval` fires multiple times
- `clearInterval` stops firing
- Timer IDs don't collide between setTimeout and setInterval
- `requestAnimationFrame` callback receives timestamp
- `cancelAnimationFrame` prevents callback
- [ ] Task 5: Console enhancements (AC: #4)
- [ ] 5.1 Add `console.dir(obj)` to console dispatch:
- Output object with all enumerable properties, one per line
- Format: `Object { key: value, key2: value2 }` (simplified)
- [ ] 5.2 Add `console.table(data)`:
- If array of objects: output as key-value table (simplified text format)
- If object: output key-value pairs
- Simplified: format as structured text, not actual ASCII table
- [ ] 5.3 Add `console.assert(condition, ...args)`:
- If condition is falsy: output `"Assertion failed: "` + args joined
- If condition is truthy: no output
- [ ] 5.4 Add `console.count(label)` / `console.countReset(label)`:
- Track counter per label string in interpreter state
- `count()` outputs `"label: N"` and increments
- `countReset()` resets counter to 0
- Default label: `"default"`
- [ ] 5.5 Add `console.time(label)` / `console.timeEnd(label)`:
- `time()`: record start timestamp (use `std::time::Instant::now()`)
- `timeEnd()`: output `"label: Nms"` elapsed time
- Store timers in interpreter state
- Default label: `"default"`
- [ ] 5.6 Add tests for each new console method
- [ ] Task 6: Promise combinators (AC: #8)
- [ ] 6.1 Implement `Promise.all(iterable)` in `promise.rs` or host environment:
- Accept array of Promises (or values)
- Return Promise that resolves when ALL resolve (with array of results)
- Reject immediately if ANY rejects (with first rejection reason)
- Handle non-Promise values (wrap in Promise.resolve())
- [ ] 6.2 Implement `Promise.race(iterable)`:
- Return Promise that settles as soon as first Promise settles (resolve or reject)
- [ ] 6.3 Implement `Promise.allSettled(iterable)`:
- Return Promise resolving when ALL settle
- Result array: `[{status: "fulfilled", value}, {status: "rejected", reason}]`
- [ ] 6.4 Implement `Promise.any(iterable)`:
- Resolve with first fulfillment
- Reject with `AggregateError` if ALL reject
- [ ] 6.5 Wire Promise static methods into `construct()` or `call_method()` dispatch
- [ ] 6.6 Add tests for each combinator with mixed resolve/reject scenarios
- [ ] Task 7: Testing and validation (AC: #9)
- [ ] 7.1 Add integration tests in appropriate test files:
- Math: basic operations, constants, edge cases
- parseInt/parseFloat/isNaN/isFinite with various inputs
- URI encoding/decoding roundtrip
- fetch with real HTTP server (use `tiny_http` in test, pattern from existing tests)
- setInterval fires multiple times (test with virtual clock)
- requestAnimationFrame receives timestamp
- Promise.all/race/allSettled/any with multiple Promises
- [ ] 7.2 Run all existing test suites:
- `cargo test -p js_vm`
- `cargo test -p web_api`
- `cargo test -p rust_browser --test js_tests`
- `cargo test -p rust_browser --test js_scheduling`
- `cargo test -p rust_browser --test js_dom_tests`
- `cargo test -p rust_browser --test js_events`
- `cargo test -p rust_browser --test js_async`
- `cargo test -p rust_browser --test js_modules`
- [ ] 7.3 Update `docs/JavaScript_Implementation_Checklist.md`
- [ ] 7.4 Update `docs/old/js_feature_matrix.md`
- [ ] 7.5 Run `just ci` -- full validation pass
## Dev Notes
### Key Architecture Decisions
**fetch() is synchronous under the hood.** `NetworkStack::load_sync()` blocks. `fetch()` wraps this in a Promise for API compatibility, but the Promise resolves synchronously (same pattern as `import()` dynamic import from Story 3.4). This is acceptable for the single-threaded model.
**Math object is a plain global, not a constructor.** `Math` is set up like `JSON` -- a regular object with static methods. `new Math()` should throw TypeError (or just fail silently). No prototype chain needed.
**parseInt is a global AND on Number.** `parseInt`/`parseFloat` are both global functions and `Number.parseInt`/`Number.parseFloat`. Implement once, reference twice.
**`Math.random()` without `rand` crate.** Use a simple xorshift64 PRNG seeded from `std::time::SystemTime::now()`. Store PRNG state in interpreter. No need for cryptographic quality.
**setInterval uses same timer ID space as setTimeout.** Unified `next_timer_id: u32` counter. `clearTimeout` and `clearInterval` are interchangeable per spec (clearing either type with either function works).
**requestAnimationFrame integrates with render loop.** In `event_handler.rs`, after processing events and before painting, call `flush_raf_callbacks()`. Pass elapsed time since page load as the timestamp argument.
### Implementation Patterns
**Console sentinel functions** (in `bytecode_exec.rs`):
```rust
"__console_log__" => { /* format args, write to output */ }
"__console_warn__" => { /* same with "WARN: " prefix */ }
// Add: "__console_dir__", "__console_table__", "__console_assert__", etc.
```
**Global function setup** (in `mod.rs:setup_builtins()`):
```rust
env.define_global("parseInt", JsValue::NativeFunction {
name: "parseInt".into(),
func: |args| { /* ... */ }
});
```
**Math setup** (in `math_builtins.rs`):
```rust
let math = JsObject::new();
math.set("PI", JsValue::Number(std::f64::consts::PI));
math.set("abs", JsValue::NativeFunction { name: "abs".into(), func: |args| {
let x = args.first().map_or(f64::NAN, |v| v.to_number());
Ok(JsValue::Number(x.abs()))
}});
env.define_global("Math", JsValue::Object(math));
```
### Critical Implementation Details
**parseInt radix handling:** Default radix is 10 (NOT 8 for "010"). With `0x` prefix and no explicit radix, use 16. With explicit radix, ignore prefix. Radix outside 2-36 returns NaN. Radix 0 treated as 10.
**URI encoding uses UTF-8.** Each non-ASCII character is encoded as multiple `%XX` sequences. Use Rust's string UTF-8 encoding. The `percent-encoding` crate (already a workspace dependency) provides `utf8_percent_encode()`.
**Promise.all short-circuits on first rejection.** Don't wait for all Promises to settle. As soon as one rejects, reject the aggregate. However, remaining Promises still run (no cancellation).
**fetch() needs network access.** DomHost already has `network: Option<&NetworkStack>` from Story 3.4. Reuse this. If `network` is None (no network stack available), reject with TypeError.
**console.time uses Instant, not SystemTime.** `std::time::Instant` is monotonic and suitable for elapsed time measurement. Store `HashMap<String, Instant>` in interpreter state for console timers.
### Dependencies
**Story 3.4 (ES Modules):** NetworkStack is already wired into DomHost via `with_network()`. fetch() reuses this.
**No dependency on Stories 3.5-3.9.** This story is largely independent -- Math, parseInt, fetch, setInterval are self-contained.
### Previous Story Patterns
From Story 3.4:
- Promise-wrapping synchronous operations (used for `import()` and now `fetch()`)
- NetworkStack access via DomHost.network field
From Story 3.6:
- New builtin setup pattern: create module, wire into `setup_builtins()`
### Risk Assessment
**LOW: Math object.** Pure functions wrapping Rust f64 methods. Straightforward.
**LOW: parseInt/parseFloat.** Well-defined spec, mainly string parsing.
**MEDIUM: fetch() API.** Requires Promise creation, Response HostObject storage, and network integration. Multiple ID spaces and HostObject types to manage.
**MEDIUM: setInterval.** Repeating timers need re-enqueue logic in the scheduling system. Must handle cases where callback takes longer than interval.
**MEDIUM: Promise combinators.** Promise.all/race need to track multiple Promise states. Integration with existing PromiseRegistry may be complex.
**LOW: Console enhancements.** Simple extensions to existing sentinel function pattern.
### Phased Implementation Strategy
**Phase A -- Math + Global Utilities (Tasks 1-2):** Pure JS engine additions. No web_api changes. Highest value for Test262 compliance.
**Phase B -- fetch() (Task 3):** New HostObject type. Network integration.
**Phase C -- setInterval + rAF (Task 4):** Scheduling extensions.
**Phase D -- Console + Promise Combinators (Tasks 5-6):** Enhancements to existing systems.
**Phase E -- Testing + Validation (Task 7):** After all APIs implemented.
### Project Structure Notes
- Math/parseInt/parseFloat/URI in `crates/js_vm/src/interpreter/` (Layer 1) -- pure JS engine
- fetch bridge in `crates/web_api/src/dom_host/` (Layer 1) -- needs `net` crate access
- setInterval/rAF in `crates/web_api/src/scheduling.rs` (Layer 1)
- rAF integration in `crates/app_browser/src/event_handler.rs` (Layer 3)
- No `unsafe` code needed
- No new external dependencies (percent-encoding already available)
### References
- [Fetch Standard §5 -- Fetch API](https://fetch.spec.whatwg.org/#fetch-method) -- fetch() function
- [Fetch Standard §6.4 -- Response](https://fetch.spec.whatwg.org/#response-class) -- Response interface
- [HTML §8.6 -- Timers](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) -- setTimeout, setInterval
- [HTML §8.10 -- Animation Frames](https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animation-frames) -- requestAnimationFrame
- [Console Standard](https://console.spec.whatwg.org/) -- Console API
- [ECMAScript §21.3 -- Math Object](https://tc39.es/ecma262/#sec-math-object) -- Math methods and constants
- [ECMAScript §19.2 -- Function Properties of Global Object](https://tc39.es/ecma262/#sec-function-properties-of-the-global-object) -- parseInt, parseFloat, isNaN, isFinite, URI encoding
- [ECMAScript §27.2.4 -- Promise Static Methods](https://tc39.es/ecma262/#sec-properties-of-the-promise-constructor) -- Promise.all, race, allSettled, any
- [Source: crates/js_vm/src/interpreter/mod.rs:559-583] -- Existing console implementation
- [Source: crates/web_api/src/scheduling.rs] -- TaskQueue, timer infrastructure
- [Source: crates/web_api/src/dom_host/host_environment.rs:735-756] -- setTimeout/clearTimeout
- [Source: crates/web_api/src/promise.rs] -- Promise implementation
- [Source: _bmad-output/planning-artifacts/epics.md#Story 3.10] -- Story requirements
## Dev Agent Record
### Agent Model Used
{{agent_model_name_version}}
### Debug Log References
### Completion Notes List
### File List