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
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ CI (`.github/workflows/ci.yml`) gates on: lint → format:check → typecheck
| `.` (root) | Aggregate barrel | re-exports everything below |
| `./core` | Scopes & digest cycle | `Scope.create`, `$watch`, `$watchGroup`, `$watchCollection`, `$digest`, `$apply`, `$eval`, `$evalAsync`, `$applyAsync`, `$on/$emit/$broadcast`, `$new`, `$destroy`, `isEqual`, `copy`, type guards |
| `./parser` | Expression parser | `parse(expr)` → `ParsedExpression` (lexer → AST → tree-walking interpreter) |
| `./di` | Dependency injection | `createModule`, `createInjector`, `annotate` |
| `./di` | Dependency injection (incl. config-phase `$provide` injectable) | `createModule`, `createInjector`, `annotate`, `ProvideService` (type) |
| `./interpolate` | String & template interpolation (`{{expr}}` resolution) | `createInterpolate`, `interpolate` (default), `InterpolateFn`, `InterpolateService`, `InterpolateOptions`, `ngModule` |
| `./sce` | Strict Contextual Escaping — trust wrappers for HTML / URL / resource URL / JS / CSS | `createSce`, `sce` (default), `createSceDelegate`, `sceDelegate`, `SCE_CONTEXTS`, `TrustedHtml`/`TrustedUrl`/`TrustedResourceUrl`/`TrustedJs`/`TrustedCss`, `isTrustedValue`, `isTrustedFor`, `isValidSceContext` |
| `./sanitize` | Opt-in HTML sanitization (companion to `$sce`) — parses + scrubs untrusted HTML against a fixed allow-list | `createSanitize`, `sanitize` (default), `$SanitizeProvider` (DI-only, not in root barrel), `ngSanitize`, `SanitizeService`, `SanitizeOptions`, `AddValidElementsArg` |
Expand All @@ -39,6 +39,7 @@ CI (`.github/workflows/ci.yml`) gates on: lint → format:check → typecheck
- **Trusted values are per-context nominal classes** — `TrustedResourceUrl extends TrustedUrl`, so a trusted resource URL is accepted where a trusted URL is expected (not vice-versa). Identity is checked via `instanceof`, not a string-based brand. Do NOT "optimize" to a single branded wrapper — the subtype rule matters for AngularJS parity.
- **`ngSanitize` is opt-in, never registered on the core `ng` module** — apps must list `'ngSanitize'` in their dependency chain. When loaded, `$sce.getTrustedHtml(plainString)` automatically routes through `$sanitize` via a lazy `$injector.has('$sanitize')` lookup in `$SceProvider.$get` — no hard dependency from `$sce` to `ngSanitize`, no decoration. Removing the `$injector` dep from `$SceProvider.$get` would silently break this integration; the regression test in `src/sanitize/__tests__/sanitize-sce.test.ts` is the guard.
- **The digest's "log and continue" contract is preserved through `$exceptionHandler`.** A failing watcher / listener / async task is reported via the configured handler and the digest proceeds; only TTL exhaustion re-throws (after first reporting via the handler, cause `'$digest'`). The default handler is `console.error`, so today's logs continue to appear unchanged. A custom handler that itself throws is caught by `invokeExceptionHandler` and degrades to `console.error` — the digest still does not crash. The eight `EXCEPTION_HANDLER_CAUSES` tokens are part of the public contract; future specs that add framework-internal call sites must extend the list as a public-API change.
- **`$provide` is config-phase only** — the six methods (`factory`, `service`, `value`, `constant`, `provider`, `decorator`) throw synchronously with `$provide.<method> is only callable during the config phase; calling it after the run phase begins is not supported` whenever they're invoked outside a `config()` block, including via a `$provide` reference captured during config and called later. This out-of-phase error is a programming error and is **not** routed through `$exceptionHandler` — it surfaces directly to the caller. Constants are protected by an override guard: `value` / `factory` / `service` / `provider` / `decorator` (whether through the module DSL or `$provide`) targeting a name already registered as a `.constant` throw `Cannot override constant "<name>" — already registered via .constant(...)`. Within the unified registration timeline, a new producer recipe wipes prior producer entries for the same name from the other backing maps so the run-phase resolver returns the most-recent producer's value; decorators stack on the current producer and are NOT evicted.

## Coding conventions

Expand Down Expand Up @@ -71,6 +72,7 @@ CI (`.github/workflows/ci.yml`) gates on: lint → format:check → typecheck
| What AST node types exist? | `src/parser/ast.ts` + `src/parser/ast-flags.ts` |
| How are watch delegates selected? | `src/core/scope-watch-delegates.ts` |
| How does `$injector` resolve `$inject` arrays / minified fns? | `src/di/annotate.ts` |
| How are services registered from inside config blocks? | `src/di/provide.ts` (the injectable), `src/di/registration.ts` (the shared recipe handler) |
| How does `$sce` decide whether a resource URL is allowed? | `src/sce/resource-url-matcher.ts` |
| How is untrusted HTML scrubbed? | `src/sanitize/sanitize.ts` (factory) + `src/sanitize/sanitize-tokenizer.ts` (regex parser) |
| How do I swap `$sanitize` for DOMPurify? | `src/sanitize/README.md` (decorator pattern + ESM-first equivalent) |
Expand Down
8 changes: 4 additions & 4 deletions context/product/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ import { createModule, getModule, createInjector } from 'my-own-angularjs/di';

`createModule(name, requires?)` returns a module object whose registration DSL grows **in-place** as new domains come online. Each domain provider owns exactly one registry, and the corresponding module-DSL method is a thin alias onto that provider:

| Module DSL method | Underlying provider / service | Lands in |
|-------------------------------------------------------------------------------|---------------------------------------------|------------------------------------------|
| `.provider` / `.factory` / `.service` / `.value` / `.constant` / `.decorator` | `$provide` | Phase 1 (already shipped spec 007–008) |
| `.config` / `.run` | module lifecycle | Phase 1 (already shipped — spec 008) |
| Module DSL method | Underlying provider / service | Lands in |
|-------------------------------------------------------------------------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `.provider` / `.factory` / `.service` / `.value` / `.constant` / `.decorator` | `$provide` | Phase 1 — module-DSL chain shipped in spec 007–008; config-phase `$provide` injectable shipped in spec 015. Both paths share `applyRegistrationRecord` from `src/di/registration.ts`. |
| `.config` / `.run` | module lifecycle | Phase 1 (already shipped — spec 008) |
| `.controller` | `$controllerProvider.register` | Phase 2 (with `$compile`) |
| `.directive` / `.component` | `$compileProvider.directive` / `.component` | Phase 2 (with `$compile`) |
| `.filter` | `$filterProvider.register` | Phase 2 (with filter pipeline) |
Expand Down
4 changes: 2 additions & 2 deletions context/product/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ _Complete the essential building blocks that everything else depends on._
- [x] **Phase tracking:** Implement `$beginPhase`, `$clearPhase`, and `$$postDigest` hooks.
- [x] **TTL configuration:** Support configurable digest TTL and cycle detection.

- [ ] **Dependency Injection**
- [x] **Dependency Injection**
- [x] **Module System:** Implement `createModule()` / `getModule()` (ES module style) with support for dependencies between modules. (spec 007)
- [x] **Injector:** Implement the injector with `invoke`, `get`, `has`, `annotate`, and support for `$inject` annotations and array-style DI. (spec 007)
- [x] **Providers & Recipes:** Implement `provider`, `factory`, `service`, `value`, `constant`, and `decorator`. _(spec 007 covers `value`, `constant`, `factory`; `service`/`provider`/`decorator` deferred to spec 008)_
- [x] **Config & Run Blocks:** Support module-level `config()` and `run()` lifecycle hooks. _(spec 008)_
- [ ] **`$provide` Service:** Register `$provide` as an injectable in `config()` blocks exposing `factory` / `service` / `value` / `constant` / `provider` / `decorator` registration recipes — the AngularJS-canonical config-phase override path (`config(['$provide', $p => $p.factory(...)])`). Today's `module.factory` chain works for pre-`createInjector` registration; `$provide` is the run-time-dynamic equivalent. Required to activate the skipped `$provide.factory` test in spec 014 (`src/exception-handler/__tests__/di.test.ts`).
- [x] **`$provide` Service:** Register `$provide` as an injectable in `config()` blocks exposing `factory` / `service` / `value` / `constant` / `provider` / `decorator` registration recipes — the AngularJS-canonical config-phase override path (`config(['$provide', $p => $p.factory(...)])`). _(spec 015)_

---

Expand Down
Loading
Loading