Skip to content

feat(plugin-types): opt-in globals subpath for the host-injected URL global (SDK 3.0.1 prep) - #8

Merged
acebytes merged 5 commits into
mainfrom
feat/url-globals-subpath
Aug 13, 2026
Merged

feat(plugin-types): opt-in globals subpath for the host-injected URL global (SDK 3.0.1 prep)#8
acebytes merged 5 commits into
mainfrom
feat/url-globals-subpath

Conversation

@acebytes

@acebytes acebytes commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What

SDK side of fn-182 (host-injected Foundation-bridged URL global for the JSC plugin runtime): a new opt-in ambient-declaration subpath @appos.space/plugin-types/globals, plus the contract reword from "no ambient globals" to "one opt-in globals subpath".

  • packages/plugin-types/src/globals.ts (new) — declare-global module form, so the normal tsc build emits dist/globals.d.ts. Declares var URL: URLConstructor | undefined + the URL/URLConstructor interfaces covering the epic's v1 subset: href/protocol/hostname/host/port/pathname/search/hash/origin/username/password/toString/toJSON + static canParseno searchParams (type omits it; docblock notes the runtime getter throws TypeError), no URL.parse, all accessors readonly.
  • Docblock documents: Foundation (RFC 3986) semantics vs WHATWG polyfill; the pinned divergences (default ports retained, empty path stays "", out-of-range ports accepted, %3A%253A double-encode on href round-trip, IPv6 hostname unbracketed with host/origin re-bracketing incl. the pinned literals); why the type is optional (older hosts / menu-bar contexts / appos.jsc.urlGlobal.disabled kill switch) and minHostVersion guidance; the never-throws canParse contract and the TypeError contract.
  • package.jsonexports map gains "./globals" (types-only) + typesVersions fallback for legacy node10 resolution; npm test now also runs the consumer-fixture gate.
  • tsconfig.json — pins "lib": ["ES2020"] (the default ES2020 lib pulls in lib.dom, whose var URL collides with the new global inside the package's own build).
  • fixtures/globals/ (new, not published — files is dist-only) — 4 standalone consumer tsconfigs resolving the package BY NAME through the workspace symlink + built dist/, with a strict runner:
    1. jsc-with-globals — reference line opt-in; guarded new URL(...) typechecks; self-checking @ts-expect-error for unguarded use, searchParams, readonly assignment → clean compile
    2. jsc-types-array — tsconfig types array opt-in → clean compile
    3. jsc-without-globals — no reference, main entry imported → MUST fail with ONLY TS2304 'URL' (no leak from the main entry)
    4. webview-dom — lib.dom tsconfig, no reference → clean compile with mutable href + searchParams (browser URL untouched)
  • Contract reword (3 claims)src/index.ts:6, README.md Usage lead-in, docs-site installation.md TypeScript-setup paragraph now say module exports by default + ONE opt-in globals subpath; README + installation.md gain teaching sections (opt-in forms, guard pattern, divergences pointer, webview warning).
  • docs-site/tsconfig.typedoc.json — excludes the subpath from the TypeDoc program (deliberately outside the index.ts API entry; the TypeDoc program needs full lib for plugin-utils' globalThis.crypto, so the global would collide with lib.dom's URL and break the docs build).

Spec deviation (deliberate)

The task spec names the new file src/globals.d.ts; it is authored as src/globals.ts (declare-global module form) because tsc never emits input .d.ts files — a .ts source is the only way to satisfy the acceptance criterion "tsc emit produces dist/globals.d.ts" without adding a copy step. The epic explicitly allows "global-script or declare-global form".

Release notes — v3.0.1 (publish is USER-authorized; not performed here)

  • New: opt-in @appos.space/plugin-types/globals subpath declaring the host-injected URL global (optional-typed; guard with typeof URL === "function" or pin minHostVersion to an injecting host release — targeted host 1.1.0).
  • Changed: package contract reworded from "no ambient globals" to "one opt-in globals subpath"; nothing changes for existing consumers who don't reference the subpath (fixture-proven: no global leaks from the main entry; lib.dom/webview tsconfigs unaffected).
  • Versions: the lockstep 3.0.1 bump IS pre-staged on this branch (3 workspaces + root + lockfile, via the exact release.sh bump command), matching src/index.ts's 3.0.1 header (codex review r1). release.sh's commit step now tolerates a pre-staged bump (skips the empty commit), so after merge the release is still one user-run command on main: ./release.sh 3.0.1 (builds, tests, no-op bump, tags v3.0.1, publishes, pushes tags). If batching with fn-178's SDK change changes the number, re-run ./release.sh <version> with that number — the bump step will move the versions forward normally.
  • Review: codex impl-review SHIP after 3 rounds (r1: pre-stage the lockstep bump + make npm test self-contained via pretest; r2: require the typeof URL guard unconditionally — minHostVersion does not override the kill switch or menu-bar limitation).

Verification

  • npm run build + npm test green at root (59 runtime tests + typetests + the 4-fixture globals gate)
  • docs-site npm run check-drift green (drift gate hashes permissions.ts/core.ts/schemas only — untouched)
  • full docs-site npm run build (Astro + TypeDoc + llms.txt) green locally

Epic: fn-182-inject-foundation-bridged-url-global · Task: .3

https://claude.ai/code/session_015NhVkmXAW9YUYMp6oumnwe

…global

- new src/globals.ts (declare-global module form) emitting dist/globals.d.ts
  via the normal tsc build; exports map + typesVersions gain "./globals"
- optional-typed URL (URLConstructor | undefined) covering the epic's v1
  subset incl. canParse, minus searchParams (docblock notes the runtime
  getter throws TypeError); Foundation semantics + pinned WHATWG
  divergences + minHostVersion guidance documented in the docblock
- pin lib ES2020 in the package tsconfig (default ES2020 lib pulls in
  lib.dom, whose `var URL` would collide with the new global declaration)
- 4 consumer fixtures + strict runner wired into `npm test`: reference-line
  opt-in typechecks guarded `new URL(...)`; types-array opt-in works; NO
  reference => only TS2304 'URL' (no leak from the main entry); lib.dom
  webview tsconfig unaffected (mutable href + searchParams intact)
- reword the 3 "no ambient globals" claims (index.ts, README, docs-site
  installation.md) to "one opt-in globals subpath" + new teaching sections
- index.ts header staged at 3.0.1; package.json versions left at 3.0.0 so
  the user-authorized `./release.sh 3.0.1` performs the lockstep bump,
  tag, and publish post-merge

Task: fn-182-inject-foundation-bridged-url-global.3

Claude-Session: https://claude.ai/code/session_015NhVkmXAW9YUYMp6oumnwe
The TypeDoc tsconfig compiles all three packages' src trees under the
default full lib (DOM included — plugin-utils needs globalThis.crypto),
so plugin-types' new `declare global { var URL ... }` collided with
lib.dom's URL (TS2403) and broke the docs build. The subpath is
deliberately outside the API-reference entry point (index.ts), so
excluding it from this one program is the correct scope.

Caught by running the full docs-site build locally before opening the PR.

Task: fn-182-inject-foundation-bridged-url-global.3

Claude-Session: https://claude.ai/code/session_015NhVkmXAW9YUYMp6oumnwe
…n-types tests

Review r1 fixes (codex NEEDS_WORK):

- Major #1 (version contract inconsistency): stage the full lockstep 3.0.1
  bump (3 workspaces + root + lockfile, via the exact release.sh command:
  `npm version 3.0.1 --workspaces --include-workspace-root
  --no-git-tag-version --allow-same-version`) so package.json matches the
  src/index.ts 3.0.1 header. release.sh's commit step now tolerates a
  pre-staged bump (skips the empty commit) so the user-authorized
  `./release.sh 3.0.1` remains the single release path: build + test +
  no-op bump + tag + publish + push. Publish still NOT performed here.

- Major #2 (npm test not self-contained): plugin-types gains
  `"pretest": "npm run build"` so a clean checkout's `npm test` builds
  dist/ before the typetests + fixture gate (verified: rm -rf dist &&
  root `npm test` green). The fixture runner's explicit missing-dist
  error stays as a backstop for direct `node fixtures/globals/run.mjs`.

Task: fn-182-inject-foundation-bridged-url-global.3

Claude-Session: https://claude.ai/code/session_015NhVkmXAW9YUYMp6oumnwe

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0487dafe67

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/plugin-types/fixtures/globals/jsc-with-globals/main.ts Outdated
Comment thread docs-site/src/content/docs/getting-started/installation.md Outdated
Comment thread packages/plugin-types/src/globals.ts Outdated
codex r2 Major: the guidance said minHostVersion permits unguarded use of
the injected URL global, but the appos.jsc.urlGlobal.disabled kill switch
can leave it undefined on ANY host version (as can menu-bar contexts), so
a non-null assertion could crash at runtime. Reword all three guidance
sites (src/globals.ts docblock, README opt-in section, docs-site
installation.md) to require the typeof guard in all cases and scope
minHostVersion to removing only the older-host reason for absence.

Task: fn-182-inject-foundation-bridged-url-global.3

Claude-Session: https://claude.ai/code/session_015NhVkmXAW9YUYMp6oumnwe
- Replace fixture's bare-reference `if (URL)` guard with absence-safe typeof form; sweep found no other bare-reference examples
- Mandate DOM-free lib as the only reliable isolation; stop presenting the lib.dom declaration conflict as a fail-loud safeguard (skipLibCheck merges silently)

Claude-Session: https://claude.ai/code/session_015NhVkmXAW9YUYMp6oumnwe
@acebytes
acebytes merged commit 22dae9a into main Aug 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant