All checks were successful
ci / fast (linux) (push) Successful in 7m9s
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>
386 lines
23 KiB
Markdown
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
|