Skip to content

feat: add W3DS Gateway embeddable web component and notification helpers#890

Open
Bekiboo wants to merge 6 commits intomainfrom
feat/w3ds-gateway
Open

feat: add W3DS Gateway embeddable web component and notification helpers#890
Bekiboo wants to merge 6 commits intomainfrom
feat/w3ds-gateway

Conversation

@Bekiboo
Copy link
Collaborator

@Bekiboo Bekiboo commented Feb 26, 2026

Description of change

w3ds-gateway — a new self-contained package that translates W3DS eNames into application URLs and presents an "Open with..." chooser.
What it solves: Given an eName (W3ID) and a content type (schema ID from the Ontology), the package determines which platforms can handle that content and builds deep links into each one.

  • Implemented the W3dsGatewayChooser custom element for displaying an "Open with..." app chooser.
  • Created notification helper functions for generating links that open the chooser modal using a custom protocol.
  • Developed a resolver for mapping eNames and schema IDs to application URLs.
  • Defined schema IDs and labels for various content types used in the W3DS ecosystem.
  • Added TypeScript types for platform handling and resolved applications.
  • Configured TypeScript build settings and included a test HTML file for demonstration.

A REST API was the obvious first approach, but the capability map (which schema opens on which platform) is static and already known at build time. Platform URLs are already available from the Registry's existing GET /platforms. A dedicated backend endpoint would just be a pass-through wrapper with an extra network hop and a new service to maintain.

Going frontend-first means platforms can drop in the Web Component and resolve entirely client-side, with no new infrastructure. The resolver still supports the Registry for live URL hydration. A REST wrapper around resolveEName() remains trivial to add for any server-side use case — but it's not the default path.

image

Issue Number

Closes #780

Type of change

  • New (a change which implements a new feature)

How the change has been tested

Tested with vitest and test page to simulate notifications

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

  • New Features
    • Added a new w3ds-gateway package providing eName resolution, platform capabilities, schema labels, gateway link utilities, and an embeddable "Open with" chooser component.
    • Exposes utilities to generate gateway URIs, link data, and safe navigation across apps.
  • Documentation
    • Comprehensive README documenting usage scenarios, APIs, events, supported schemas, and architecture.
  • Tests / Demo
    • Extensive unit/component tests and a local demo page for interactive testing.

@Bekiboo Bekiboo self-assigned this Feb 26, 2026
@Bekiboo Bekiboo requested a review from coodos as a code owner February 26, 2026 16:33
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Adds a new package w3ds-gateway that implements resolver APIs, types, platform capability mappings, notification link utilities, inline SVG icons, an embeddable Web Component chooser, tests, TypeScript configs, and a demo page. No existing public API signatures outside the new package were modified.

Changes

Cohort / File(s) Summary
Package config & docs
packages/w3ds-gateway/README.md, packages/w3ds-gateway/package.json, packages/w3ds-gateway/tsconfig.json, packages/w3ds-gateway/tsconfig.build.json
New README, package manifest, and TypeScript project/build configs; package exports root and modal entry points and declares dev deps and build scripts.
Public barrel
packages/w3ds-gateway/src/index.ts
New re-export barrel exposing resolver, schemas, capabilities, notifications, icons, types, and the Web Component.
Types & schemas
packages/w3ds-gateway/src/types.ts, packages/w3ds-gateway/src/schemas.ts
Adds core TypeScript interfaces and a strongly-typed set of canonical SchemaIds with human-readable SchemaLabels.
Platform capabilities
packages/w3ds-gateway/src/capabilities.ts
Defines PLATFORM_CAPABILITIES, PLATFORM_ENV_KEYS, registry key order, default platform URLs, and runtime configure/get helpers for platform URL resolution.
Icons
packages/w3ds-gateway/src/icons.ts
Embeds inline SVG platform icons and a fallback icon; exports PLATFORM_ICONS and FALLBACK_ICON.
Resolver logic
packages/w3ds-gateway/src/resolver.ts
Adds async and sync resolution (resolveEName, resolveENameSync), registry fetch normalization, URL template interpolation, and URL merging logic.
Notifications / links
packages/w3ds-gateway/src/notifications.ts
Exports buildGatewayUri, buildGatewayData, buildGatewayLink and related option/data interfaces with safe HTML/attribute escaping.
Web Component modal
packages/w3ds-gateway/src/modal.ts
Implements W3dsGatewayChooser custom element (open/close/isOpen, observed attributes, events, inline resolve flow, UI states, SafeHTMLElement fallback).
Utilities
packages/w3ds-gateway/src/utils.ts
Adds isSafeUrl helper to validate http/https URLs.
Tests
packages/w3ds-gateway/src/__tests__/modal.test.ts, .../notifications.test.ts, .../resolver.test.ts
Vitest suites covering modal lifecycle/events/rendering, link utilities/escaping, and resolver sync/async flows with registry mocking and overrides.
Demo page
test.html
Standalone demo page wiring the built chooser and configuring dev platform URLs for manual testing.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Browser)
    participant Modal as W3dsGatewayChooser
    participant Resolver as Resolver
    participant Registry as Registry API
    participant App as Target App

    User->>Modal: open() / click "Open Chooser"
    Modal->>Modal: emit gateway-open
    Modal->>Resolver: resolveEName(ename, schemaId, options)
    Resolver->>Registry: GET /platforms (if registryUrl provided)
    Registry-->>Resolver: platformUrls map
    Resolver->>Resolver: merge URLs (defaults + registry + overrides)
    Resolver->>Resolver: select handlers & build app URLs
    Resolver-->>Modal: GatewayResolveResult (apps, schemaLabel)
    Modal->>Modal: render app list
    User->>Modal: click app entry
    Modal->>Modal: emit gateway-select / gateway-close
    Modal->>App: navigate to resolved URL
    App-->>User: open content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • coodos
  • sosweetham
  • xPathin

Poem

🐰 I hopped through code to build a gate,

eNames to apps I translate,
A chooser pops with icons bright,
Registry hums and sets URL light,
Hooray — the gateway's ready to migrate!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: introduction of a W3DS Gateway web component and notification helpers as a new feature.
Description check ✅ Passed The PR description fully addresses the template structure with all required sections: Issue Number (#780), Type of change (New), testing approach (vitest and test page), and a complete checklist.
Linked Issues check ✅ Passed The PR fulfills both acceptance criteria from #780: a client-side resolver for mapping eNames to application URLs [resolver.ts, modal.ts] and an embeddable modal 'Open with...' chooser [modal.ts, test.html].
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the W3DS Gateway package with resolver, notification helpers, schemas, types, tests, and configuration. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/w3ds-gateway

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
test.html (2)

37-46: Consider importing schema definitions to avoid drift.

The schema IDs are hardcoded here and could become out of sync with the actual SchemaIds defined in the package. For a test page this is acceptable, but consider adding a comment noting that these should match the values in schemas.ts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test.html` around lines 37 - 46, The hardcoded schema ID list in the local
variable "schemas" can drift from the canonical SchemaIds; update the test to
import or reference the authoritative values (SchemaIds) from schemas.ts instead
of duplicating literals, or at minimum add a clear inline comment above the
"schemas" array stating these IDs must match SchemaIds in schemas.ts; refer to
the "schemas" constant and the "SchemaIds"/schemas.ts export when making the
change.

19-19: Remove or uncomment the unused paragraph.

Line 19 contains a commented-out paragraph. Either remove it or uncomment it if the guidance is helpful for users of the test page.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test.html` at line 19, There is a commented-out paragraph element ("<!--
<p>Click a content type, then "Open Chooser" to see which apps can handle
it.</p> -->"); either delete this commented <p> entirely if it’s unnecessary, or
uncomment it by removing the comment markers so the paragraph becomes an active
<p> element for users; update the surrounding HTML accordingly (the
commented-out paragraph is the unique element to change).
packages/w3ds-gateway/src/icons.ts (1)

79-84: Consider adding a note about trusted input for letterIcon.

The letterIcon function interpolates letter, bg, and fg directly into the SVG template without escaping. While all current usages (line 86) use hardcoded trusted strings, adding a JSDoc note about requiring trusted input would help prevent future misuse if the function is exposed more broadly.

📝 Suggested documentation addition
 // ─── Lettermark fallback (for platforms without a brand SVG) ────────────────

+/**
+ * Generates a lettermark icon SVG.
+ * `@param` letter - The letter(s) to display (trusted input only)
+ * `@param` bg - Background color (trusted input only)
+ * `@param` fg - Foreground/text color (trusted input only)
+ * `@internal` This function does not escape inputs - use only with trusted values.
+ */
 function letterIcon(letter: string, bg: string, fg: string): string {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/icons.ts` around lines 79 - 84, The SVG template in
letterIcon directly interpolates letter, bg, and fg without escaping, so
annotate the function with a JSDoc comment on letterIcon stating that letter,
bg, and fg must be trusted or pre-escaped (or validated) before being passed in;
mention the injection risk and list expected formats (e.g., single-character
plain text for letter, valid CSS color strings for bg and fg) so future callers
know to sanitize or escape input if coming from untrusted sources.
packages/w3ds-gateway/src/__tests__/modal.test.ts (1)

112-113: Consider using vi.waitFor or explicit promise-based waiting.

Using setTimeout with arbitrary delays (50ms, 100ms) for async resolution can lead to flaky tests. Vitest provides vi.waitFor or you could await the actual resolution promise if exposed.

💡 Example using waitFor pattern
-        el.open();
-        // Wait for async resolve
-        await new Promise((r) => setTimeout(r, 50));
+        el.open();
+        // Wait for error to appear
+        await vi.waitFor(() => {
+            const error = el.shadowRoot!.querySelector(".gateway-error");
+            expect(error).not.toBeNull();
+        });

-        const error = el.shadowRoot!.querySelector(".gateway-error");
-        expect(error).not.toBeNull();

This pattern would need to be applied to lines 126, 142, 162, and 187 as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/__tests__/modal.test.ts` around lines 112 - 113,
Replace arbitrary setTimeout-based waits (e.g. await new Promise((r) =>
setTimeout(r, 50))) in the modal tests with deterministic waits: use vitest's
vi.waitFor to wait for the specific condition/assertion to become true (e.g.
await vi.waitFor(() => expect(...) or check DOM changes) or, when available,
await the actual resolution promise returned by the function under test; update
every occurrence of the setTimeout snippet (the one used in these tests) so
tests no longer rely on fixed delays.
packages/w3ds-gateway/src/capabilities.ts (1)

339-374: Module-level mutable state may cause issues in SSR or parallel test environments.

The _platformUrls state is shared across all consumers. In SSR scenarios or parallel test runs, mutations from one context could leak into another.

Consider providing a resetPlatformUrls() function for testing, or documenting that this is intentionally singleton state for browser-only use.

♻️ Optional: Add reset function for testing
 let _platformUrls: Record<string, string> = { ...DEFAULT_PLATFORM_URLS };
+
+/**
+ * Reset platform URLs to defaults. Useful for testing.
+ * `@internal`
+ */
+export function resetPlatformUrls(): void {
+    _platformUrls = { ...DEFAULT_PLATFORM_URLS };
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/capabilities.ts` around lines 339 - 374, The
module-level mutable _platformUrls may leak across SSR or tests; add and export
a reset function (e.g., resetPlatformUrls) that restores _platformUrls to {
...DEFAULT_PLATFORM_URLS } so tests/servers can explicitly reset state, and
update configurePlatformUrls (and its JSDoc) to mention singleton behavior and
recommend calling resetPlatformUrls in test teardown; reference the
_platformUrls variable and the configurePlatformUrls function when adding the
new resetPlatformUrls utility.
packages/w3ds-gateway/src/notifications.ts (1)

149-155: Consider consolidating escapeHtml and escapeAttr.

Both functions escape the same four characters (&, <, >, ") and produce identical output. You could use a single helper for both contexts or keep them separate for semantic clarity if you anticipate them diverging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/notifications.ts` around lines 149 - 155,
escapeHtml and escapeAttr perform identical escaping; consolidate them by
keeping a single helper (e.g., escapeHtml) and have the other function delegate
to it or remove the duplicate. Update references to escapeAttr to call
escapeHtml (or rename to a neutral name like escapeForHtml) and keep a comment
indicating semantic aliasing if you want to preserve both names for clarity;
ensure function signatures remain the same and include both function names
(escapeHtml, escapeAttr) in your change so callers can be found and updated.
packages/w3ds-gateway/src/modal.ts (1)

62-126: Code duplication with resolver.ts.

The fetchRegistryPlatforms, buildUrl, and resolve functions duplicate logic from resolver.ts. Consider importing and reusing the resolver functions to avoid maintenance burden.

♻️ Proposed refactor to reuse resolver
-import {
-    PLATFORM_CAPABILITIES,
-    getPlatformUrls,
-} from "./capabilities.js";
+import { resolveEName, resolveENameSync } from "./resolver.js";
 import { SchemaLabels } from "./schemas.js";
 import { PLATFORM_ICONS, FALLBACK_ICON } from "./icons.js";
 import type { ResolvedApp } from "./types.js";
 import type { SchemaId } from "./schemas.js";
-
-// ... remove fetchRegistryPlatforms, buildUrl, resolve functions ...

Then in doResolve():

-            const result = await resolve(ename, schemaId, entityId, registryUrl);
+            const result = await resolveEName(
+                { ename, schemaId, entityId },
+                { registryUrl }
+            );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/modal.ts` around lines 62 - 126, The local
implementations of fetchRegistryPlatforms, buildUrl, and resolve duplicate logic
from resolver.ts; remove these local functions and import the canonical
implementations from resolver.ts (e.g., import { fetchRegistryPlatforms,
buildUrl, resolve } from "resolver") and update any callers (such as doResolve)
to call the imported resolve; ensure you merge platform URL overrides the same
way resolver does and keep using the existing symbols platformUrls,
SchemaLabels, and PLATFORM_CAPABILITIES so callers and types remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/w3ds-gateway/src/notifications.ts`:
- Around line 128-129: The href attribute value in the attrs array is not
escaped for HTML attribute context, so update the code that builds attrs (where
`href="${uri}"` is created) to HTML-attribute-escape the `uri` returned from
`buildGatewayUri` (escape &, ", ', <, > at minimum) before interpolation; either
call an existing sanitizer (e.g., `escapeHtmlAttr`) or add a small helper and
use it when constructing `attrs` so the attribute value cannot break the HTML.

In `@packages/w3ds-gateway/src/resolver.ts`:
- Around line 56-80: The mapping in resolver.ts uses a brittle positional array
keyOrder to pair with the Registry response `urls`, causing silent mismatches;
update the code to stop relying on positional order by requesting/expecting a
keyed object from the Registry (e.g., { pictique: "...", blabsy: "..." }) or by
locating platform identifiers in the response instead of using `keyOrder`+`urls`
pairing, then adapt the resolver function (and the duplicate logic in modal.ts)
to build the result by key lookup, add validation/fallback when keys are
missing, and centralize the platform keys into a single exported constant
(replace both local `keyOrder` arrays) so changes to platform list are made in
one place.

---

Nitpick comments:
In `@packages/w3ds-gateway/src/__tests__/modal.test.ts`:
- Around line 112-113: Replace arbitrary setTimeout-based waits (e.g. await new
Promise((r) => setTimeout(r, 50))) in the modal tests with deterministic waits:
use vitest's vi.waitFor to wait for the specific condition/assertion to become
true (e.g. await vi.waitFor(() => expect(...) or check DOM changes) or, when
available, await the actual resolution promise returned by the function under
test; update every occurrence of the setTimeout snippet (the one used in these
tests) so tests no longer rely on fixed delays.

In `@packages/w3ds-gateway/src/capabilities.ts`:
- Around line 339-374: The module-level mutable _platformUrls may leak across
SSR or tests; add and export a reset function (e.g., resetPlatformUrls) that
restores _platformUrls to { ...DEFAULT_PLATFORM_URLS } so tests/servers can
explicitly reset state, and update configurePlatformUrls (and its JSDoc) to
mention singleton behavior and recommend calling resetPlatformUrls in test
teardown; reference the _platformUrls variable and the configurePlatformUrls
function when adding the new resetPlatformUrls utility.

In `@packages/w3ds-gateway/src/icons.ts`:
- Around line 79-84: The SVG template in letterIcon directly interpolates
letter, bg, and fg without escaping, so annotate the function with a JSDoc
comment on letterIcon stating that letter, bg, and fg must be trusted or
pre-escaped (or validated) before being passed in; mention the injection risk
and list expected formats (e.g., single-character plain text for letter, valid
CSS color strings for bg and fg) so future callers know to sanitize or escape
input if coming from untrusted sources.

In `@packages/w3ds-gateway/src/modal.ts`:
- Around line 62-126: The local implementations of fetchRegistryPlatforms,
buildUrl, and resolve duplicate logic from resolver.ts; remove these local
functions and import the canonical implementations from resolver.ts (e.g.,
import { fetchRegistryPlatforms, buildUrl, resolve } from "resolver") and update
any callers (such as doResolve) to call the imported resolve; ensure you merge
platform URL overrides the same way resolver does and keep using the existing
symbols platformUrls, SchemaLabels, and PLATFORM_CAPABILITIES so callers and
types remain unchanged.

In `@packages/w3ds-gateway/src/notifications.ts`:
- Around line 149-155: escapeHtml and escapeAttr perform identical escaping;
consolidate them by keeping a single helper (e.g., escapeHtml) and have the
other function delegate to it or remove the duplicate. Update references to
escapeAttr to call escapeHtml (or rename to a neutral name like escapeForHtml)
and keep a comment indicating semantic aliasing if you want to preserve both
names for clarity; ensure function signatures remain the same and include both
function names (escapeHtml, escapeAttr) in your change so callers can be found
and updated.

In `@test.html`:
- Around line 37-46: The hardcoded schema ID list in the local variable
"schemas" can drift from the canonical SchemaIds; update the test to import or
reference the authoritative values (SchemaIds) from schemas.ts instead of
duplicating literals, or at minimum add a clear inline comment above the
"schemas" array stating these IDs must match SchemaIds in schemas.ts; refer to
the "schemas" constant and the "SchemaIds"/schemas.ts export when making the
change.
- Line 19: There is a commented-out paragraph element ("<!-- <p>Click a content
type, then "Open Chooser" to see which apps can handle it.</p> -->"); either
delete this commented <p> entirely if it’s unnecessary, or uncomment it by
removing the comment markers so the paragraph becomes an active <p> element for
users; update the surrounding HTML accordingly (the commented-out paragraph is
the unique element to change).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e96130 and 2788d5b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • packages/w3ds-gateway/README.md
  • packages/w3ds-gateway/package.json
  • packages/w3ds-gateway/src/__tests__/modal.test.ts
  • packages/w3ds-gateway/src/__tests__/notifications.test.ts
  • packages/w3ds-gateway/src/__tests__/resolver.test.ts
  • packages/w3ds-gateway/src/capabilities.ts
  • packages/w3ds-gateway/src/icons.ts
  • packages/w3ds-gateway/src/index.ts
  • packages/w3ds-gateway/src/modal.ts
  • packages/w3ds-gateway/src/notifications.ts
  • packages/w3ds-gateway/src/resolver.ts
  • packages/w3ds-gateway/src/schemas.ts
  • packages/w3ds-gateway/src/types.ts
  • packages/w3ds-gateway/tsconfig.build.json
  • packages/w3ds-gateway/tsconfig.json
  • test.html

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
packages/w3ds-gateway/src/modal.ts (1)

63-121: Reuse resolver.ts instead of maintaining inline resolver duplication.

This duplicates fetch/build/resolve logic already present in packages/w3ds-gateway/src/resolver.ts, increasing drift risk and double-fix burden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/modal.ts` around lines 63 - 121, This file
duplicates resolver logic — replace the local implementations of
fetchRegistryPlatforms, buildUrl, and resolve with the shared implementations
from packages/w3ds-gateway/src/resolver.ts: remove the in-file functions
fetchRegistryPlatforms, buildUrl, and resolve, import the corresponding exported
functions (or a single resolver export) from resolver.ts, and update any call
sites to use the imported symbols so behavior is delegated to the canonical
resolver implementation; ensure exported types like ResolvedApp/SchemaId (or
equivalents) are also imported if needed.
packages/w3ds-gateway/src/resolver.ts (1)

47-53: Add a timeout to Registry fetch to avoid hanging resolution paths.

The external call currently has no timeout; stalled requests can block UI resolution indefinitely.

Suggested change
 async function fetchRegistryPlatforms(
     registryUrl: string,
     fetchFn: typeof globalThis.fetch,
 ): Promise<Record<string, string>> {
     try {
-        const response = await fetchFn(`${registryUrl}/platforms`);
+        const controller = new AbortController();
+        const timeout = setTimeout(() => controller.abort(), 5000);
+        const response = await fetchFn(`${registryUrl}/platforms`, { signal: controller.signal });
+        clearTimeout(timeout);
         if (!response.ok) return {};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/resolver.ts` around lines 47 - 53, The
fetchRegistryPlatforms function performs an external fetch without a timeout;
update it to use an AbortController to enforce a timeout (e.g., 3–5s), pass the
controller.signal to fetchFn(`${registryUrl}/platforms`, { signal }), and clear
the timer after response or error; on timeout (abort) or any fetch error return
an empty object and ensure the controller timer is cleaned up to avoid leaks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/w3ds-gateway/src/capabilities.ts`:
- Around line 345-359: DEFAULT_PLATFORM_URLS currently contains hardcoded
localhost fallbacks which can leak into production; change the default to an
empty map and ensure runtime requires explicit configuration/hydration instead
of falling back to localhost. Replace the contents of DEFAULT_PLATFORM_URLS with
an empty object ({}), update the initial _platformUrls initialization to mirror
that empty default, and audit any code that assumes those keys exist (e.g.,
places referencing DEFAULT_PLATFORM_URLS or _platformUrls) so they handle
missing entries or throw a clear error prompting explicit configuration.

In `@packages/w3ds-gateway/src/modal.ts`:
- Around line 81-86: The buildUrl/template resolution can produce non-http(s)
URLs from resolver/override base URLs; before assigning to link.href (places
that call buildUrl and set link.href), parse the resolved string with the URL
constructor and validate url.protocol is "http:" or "https:" and only then
assign link.href; if the protocol is invalid or parsing throws, do not set
link.href (or set to a safe fallback like an empty string) and render the link
as non-clickable. Update code paths that call buildUrl (and the code that sets
link.href around lines 81-86 and 531-535) to perform this validation and handle
failures safely.
- Around line 384-389: connectedCallback() always calls render() which appends a
new shadow DOM and event listeners on every reconnect; change
connectedCallback() to first detect if the element is already rendered (e.g.,
check this.shadowRoot or a boolean like this._isRendered) and only call render()
when not rendered, or alternatively have render() clear existing shadowRoot
before appending; ensure open() is still invoked when the component has the
"open" attribute after the guarded render. Apply the same guard to the other
affected block referenced (lines 448-499) so render() and event wiring are
idempotent.
- Around line 501-573: doResolve can race: a slower previous resolve() may
overwrite UI from a later run; fix by adding a per-instance "currentResolveId"
(or similar) counter/nonce on the class, increment it at the start of
doResolve(), capture the id in a local const before awaiting resolve(), and
after every await (both success and in catch) check that the captured id matches
this.currentResolveId before mutating DOM (headerText, body, footer) so stale
results are ignored; reference doResolve, resolve, this.headerText, this.body,
this.footer and ensure the check is applied both in the try path (before
updating header/apps/footer) and in the catch path (before showing error).

In `@packages/w3ds-gateway/src/resolver.ts`:
- Around line 73-83: The buildUrl function currently interpolates baseUrl
unsafely; before trimming or using baseUrl in buildUrl, parse it with the URL
constructor and ensure url.protocol is exactly "http:" or "https:" and reject
otherwise (throw a clear Error or skip emitting the link). Apply the same
validation logic to the other URL-building sites referenced (the functions at
the other ranges: lines 132-143 and 174-185) so all resolved app link builders
validate baseUrl's scheme before constructing or returning the final URL.

---

Nitpick comments:
In `@packages/w3ds-gateway/src/modal.ts`:
- Around line 63-121: This file duplicates resolver logic — replace the local
implementations of fetchRegistryPlatforms, buildUrl, and resolve with the shared
implementations from packages/w3ds-gateway/src/resolver.ts: remove the in-file
functions fetchRegistryPlatforms, buildUrl, and resolve, import the
corresponding exported functions (or a single resolver export) from resolver.ts,
and update any call sites to use the imported symbols so behavior is delegated
to the canonical resolver implementation; ensure exported types like
ResolvedApp/SchemaId (or equivalents) are also imported if needed.

In `@packages/w3ds-gateway/src/resolver.ts`:
- Around line 47-53: The fetchRegistryPlatforms function performs an external
fetch without a timeout; update it to use an AbortController to enforce a
timeout (e.g., 3–5s), pass the controller.signal to
fetchFn(`${registryUrl}/platforms`, { signal }), and clear the timer after
response or error; on timeout (abort) or any fetch error return an empty object
and ensure the controller timer is cleaned up to avoid leaks.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2788d5b and a0be299.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • packages/w3ds-gateway/src/capabilities.ts
  • packages/w3ds-gateway/src/index.ts
  • packages/w3ds-gateway/src/modal.ts
  • packages/w3ds-gateway/src/notifications.ts
  • packages/w3ds-gateway/src/resolver.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/w3ds-gateway/src/notifications.ts
  • packages/w3ds-gateway/src/index.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/w3ds-gateway/src/modal.ts (2)

109-117: Missing isSafeUrl filter in inline resolve function.

Unlike resolver.ts which filters unsafe URLs during app list construction, this inline resolve function includes all apps regardless of URL safety. The safety check happens later at render time (line 542), so there's no security issue, but this inconsistency could cause confusion if the resolve function is used elsewhere.

Consider adding the isSafeUrl filter here for consistency:

 const apps: ResolvedApp[] = handlers
-    .filter((h) => platformUrls[h.platformKey])
+    .filter((h) => platformUrls[h.platformKey] && isSafeUrl(platformUrls[h.platformKey]))
     .map((h) => ({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/modal.ts` around lines 109 - 117, The apps
construction in the inline resolve is missing the same URL safety filtering used
elsewhere: update the handlers -> map pipeline that produces the apps array (the
const apps: ResolvedApp[] assignment) to filter out entries whose built URL is
not safe by applying isSafeUrl(buildUrl(...)) (use handlers, buildUrl,
platformUrls, entityId, ename and platformKey to create the URL) before mapping
so only safe URLs are included in apps; keep the existing properties
(platformName, platformKey, url, label, icon) unchanged.

64-87: Consider extracting shared resolver utilities to avoid duplication.

fetchRegistryPlatforms and buildUrl are duplicated between modal.ts and resolver.ts. Consider importing these from the resolver module or extracting to a shared internal module to maintain a single source of truth.

Additionally, note the subtle behavioral difference: resolver.ts filters out apps with unsafe URLs entirely (line 134), while modal.ts includes them but renders them disabled (lines 541-553). This is reasonable UX, but be aware of the inconsistency if the resolver is used for non-UI purposes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/w3ds-gateway/src/modal.ts` around lines 64 - 87, The functions
fetchRegistryPlatforms and buildUrl are duplicated between modal.ts and
resolver.ts; extract them into a shared internal utility module (e.g., export
from a new file or the existing resolver module) and import those utilities into
both modal.ts and resolver.ts to avoid duplication and keep a single source of
truth; ensure you move both the fetchRegistryPlatforms implementation (including
the try/catch and REGISTRY_PLATFORM_KEY_ORDER usage) and the buildUrl
implementation, then adjust modal.ts to import and use them, and if preserving
UI-specific behavior, keep modal.ts logic that disables unsafe URLs while
resolver.ts continues to filter them so behavior remains intentional and
documented.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/w3ds-gateway/src/modal.ts`:
- Around line 109-117: The apps construction in the inline resolve is missing
the same URL safety filtering used elsewhere: update the handlers -> map
pipeline that produces the apps array (the const apps: ResolvedApp[] assignment)
to filter out entries whose built URL is not safe by applying
isSafeUrl(buildUrl(...)) (use handlers, buildUrl, platformUrls, entityId, ename
and platformKey to create the URL) before mapping so only safe URLs are included
in apps; keep the existing properties (platformName, platformKey, url, label,
icon) unchanged.
- Around line 64-87: The functions fetchRegistryPlatforms and buildUrl are
duplicated between modal.ts and resolver.ts; extract them into a shared internal
utility module (e.g., export from a new file or the existing resolver module)
and import those utilities into both modal.ts and resolver.ts to avoid
duplication and keep a single source of truth; ensure you move both the
fetchRegistryPlatforms implementation (including the try/catch and
REGISTRY_PLATFORM_KEY_ORDER usage) and the buildUrl implementation, then adjust
modal.ts to import and use them, and if preserving UI-specific behavior, keep
modal.ts logic that disables unsafe URLs while resolver.ts continues to filter
them so behavior remains intentional and documented.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0be299 and cffd7f1.

📒 Files selected for processing (5)
  • packages/w3ds-gateway/src/capabilities.ts
  • packages/w3ds-gateway/src/modal.ts
  • packages/w3ds-gateway/src/resolver.ts
  • packages/w3ds-gateway/src/utils.ts
  • test.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/w3ds-gateway/src/capabilities.ts

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.

[feature] W3DS Gateway

1 participant