diff --git a/.changeset/config.json b/.changeset/config.json index 3bcb600..26ffa0c 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,9 +1,6 @@ { "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", - "changelog": [ - "@changesets/changelog-github", - { "repo": "level0x40/virtual-frame" } - ], + "changelog": ["@changesets/changelog-github", { "repo": "level0x40/virtual-frame" }], "commit": false, "fixed": [], "linked": [], diff --git a/.changeset/lazy-terms-invent.md b/.changeset/lazy-terms-invent.md new file mode 100644 index 0000000..5acc216 --- /dev/null +++ b/.changeset/lazy-terms-invent.md @@ -0,0 +1,19 @@ +--- +"@virtual-frame/analog": patch +"@virtual-frame/angular": patch +"virtual-frame": patch +"@virtual-frame/next": patch +"@virtual-frame/nuxt": patch +"@virtual-frame/react": patch +"@virtual-frame/react-router": patch +"@virtual-frame/react-server": patch +"@virtual-frame/solid": patch +"@virtual-frame/solid-start": patch +"@virtual-frame/store": patch +"@virtual-frame/svelte": patch +"@virtual-frame/sveltekit": patch +"@virtual-frame/tanstack-start": patch +"@virtual-frame/vue": patch +--- + +Initial publish diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index ad54d5b..757081a 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,5 +1,5 @@ name: Setup -description: Install pnpm, set up Node.js with pnpm cache, and install dependencies (frozen lockfile). +description: Install Vite+ (vp), set up Node.js with dependency caching, and install dependencies (frozen lockfile). inputs: node-version: @@ -10,15 +10,12 @@ inputs: runs: using: composite steps: - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Vite+ + uses: voidzero-dev/setup-vp@v1 with: node-version: ${{ inputs.node-version }} - cache: pnpm + cache: true - name: Install dependencies shell: bash - run: pnpm install --frozen-lockfile + run: vp install --frozen-lockfile diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000..761c2d4 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,34 @@ +name: "virtual-frame CodeQL config" + +# Preserve the query suite configured in the workflow — this file only +# narrows what gets reported, it does not choose the suite. + +query-filters: + # Polynomial-regex detector flags any regex with an unbounded repetition + # that is evaluated against library input. Across virtual-frame's + # HTML-parsing regexes (e.g. `/]*>/gi`) the pattern is always a + # single non-overlapping quantifier anchored by a literal — linear in + # input length with no ReDoS path. The rule's own description says it + # "*may* run slow" because it cannot prove otherwise; we have proved + # otherwise by construction. Suppressed globally. + - exclude: + id: js/polynomial-redos + +paths-ignore: + # Dedicated security boundary. virtual-frame is a "virtual iframe" — + # its entire purpose is to embed HTML fetched from a caller-chosen + # remote origin (see SECURITY.md, "Security model for embedded + # content"). This file contains the narrow set of intentional HTML + # sinks; reviewing it by hand is the security model, so CodeQL's + # html-injection queries are deliberately excluded here. The rest of + # the workspace stays under full analysis — any html-injection pattern + # introduced outside this file is a real finding. + - "packages/**/src/internal/html-sink.ts" + + # Tests and fixtures. Noise from fixture data outweighs signal. + - "**/*.test.ts" + - "**/*.test.tsx" + - "**/test/**" + - "**/__tests__/**" + - "e2e/**" + - "examples/**" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61bc4fd..b97003d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: pull-requests: read steps: - name: Validate Conventional Commits format - uses: amannn/action-semantic-pull-request@v5 + uses: amannn/action-semantic-pull-request@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -57,72 +57,107 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/setup - name: Check formatting - run: pnpm format:check + run: vp run format:check - name: Lint - run: pnpm lint + run: vp run lint # ────────────────────────────────────────────────────────────────────── - # Type-check across the workspace. Today this is a real CI gap. + # Build all packages and examples. Uploads dist/ artifacts for + # downstream jobs (typecheck, test, e2e) so they don't rebuild. + # ────────────────────────────────────────────────────────────────────── + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v5 + - uses: ./.github/actions/setup + + - name: Build all packages + run: vp run build + + - name: Upload build artifacts + uses: actions/upload-artifact@v5 + with: + name: build-output + path: | + packages/*/dist + retention-days: 1 + + - name: Upload generated route files + uses: actions/upload-artifact@v5 + with: + name: generated-routes + path: | + examples/*/src/routeTree.gen.ts + retention-days: 1 + + # ────────────────────────────────────────────────────────────────────── + # Type-check across the workspace. # ────────────────────────────────────────────────────────────────────── typecheck: name: Typecheck + needs: [build] runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/setup + - name: Download build artifacts + uses: actions/download-artifact@v5 + with: + name: build-output + path: packages + + - name: Download generated route files + uses: actions/download-artifact@v5 + with: + name: generated-routes + path: examples + - name: Typecheck all packages - run: pnpm typecheck + run: vp run typecheck # ────────────────────────────────────────────────────────────────────── # Unit + browser tests via vitest. # ────────────────────────────────────────────────────────────────────── test: name: Unit Tests + needs: [build] runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/setup + - name: Download build artifacts + uses: actions/download-artifact@v5 + with: + name: build-output + path: packages + - name: Install Playwright browsers # vitest browser mode uses Playwright's chromium under the hood. - run: pnpm exec playwright install --with-deps chromium + run: npx playwright install --with-deps chromium - name: Run vitest - run: pnpm test:run + run: vp run test:run - name: Upload coverage if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: coverage path: coverage/ if-no-files-found: ignore retention-days: 14 - # ────────────────────────────────────────────────────────────────────── - # Build all packages. Produces dist/ artifacts that e2e relies on - # implicitly (e2e prod-mode builds each example, which transitively - # consumes built packages). - # ────────────────────────────────────────────────────────────────────── - build: - name: Build - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup - - - name: Build all packages - run: pnpm build - # ────────────────────────────────────────────────────────────────────── # End-to-end Playwright suite — boots real example apps in dev and prod # mode against a real browser. This is a required gate. @@ -133,19 +168,27 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/setup + - name: Download build artifacts + uses: actions/download-artifact@v5 + with: + name: build-output + path: packages + - name: Install Playwright browsers working-directory: e2e - run: pnpm exec playwright install --with-deps chromium + run: npx playwright install --with-deps chromium - name: Run E2E suite - run: pnpm test:e2e + run: vp run test:e2e + env: + VF_E2E_VERBOSE: "1" - name: Upload Playwright report if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: playwright-report path: | @@ -166,14 +209,14 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: # changeset status needs main as a comparison base. fetch-depth: 0 - uses: ./.github/actions/setup - name: Verify changeset present - run: pnpm changeset status --since=origin/${{ github.base_ref }} + run: vp run changeset status --since=origin/${{ github.base_ref }} # ────────────────────────────────────────────────────────────────────── # Aggregate gate — single status for branch protection to require. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cd81fe2..1015cd7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -51,6 +51,10 @@ jobs: # queries; security-extended is a good balance for a runtime # library. queries: security-extended + # Path filters and per-rule suppressions live in a separate + # config file so the rationale is version-controlled alongside + # the exclusions. + config-file: ./.github/codeql/codeql-config.yml - name: Autobuild # JS/TS does not require a build step for analysis; autobuild is diff --git a/.gitignore b/.gitignore index 8cc1a34..66f501a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,11 +9,14 @@ packages/*/LICENSE **/.next **/.nuxt **/.output +**/.react-router **/.react-server **/.svelte-kit **/build +**/@mf-types .viteplus *.local .DS_Store *.tsbuildinfo -**/__screenshots__ \ No newline at end of file +**/__screenshots__ +**/routeTree.gen.ts \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 67f9def..1cddbb2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - # Contributor Covenant Code of Conduct ## Our Pledge @@ -11,19 +10,19 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall community +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -82,4 +81,3 @@ For answers to common questions about this code of conduct, see the FAQ at [http [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations - diff --git a/README.md b/README.md index 060ac00..73fb11d 100644 --- a/README.md +++ b/README.md @@ -102,10 +102,7 @@ npm install virtual-frame import "virtual-frame/element"; - + ``` ### React diff --git a/SECURITY.md b/SECURITY.md index ff5d943..4ea971f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,10 +6,10 @@ We take the security of Virtual Frame seriously. This document explains how to r Security fixes land on the latest minor release line of each package. We do not back-port to older minors. -| Package family | Supported | -| ---------------------------- | ---------------------------------- | -| `virtual-frame` (core) | Latest minor | -| `@virtual-frame/*` bindings | Latest minor | +| Package family | Supported | +| ---------------------------------------------------------- | ------------ | +| `virtual-frame` (core) | Latest minor | +| `@virtual-frame/*` bindings | Latest minor | | `@virtual-frame/*` integrations (Next/Nuxt/SvelteKit/etc.) | Latest minor | While the project remains pre-1.0 (`0.x`), every `0.x` minor bump may include breaking changes; security fixes target the most recent published minor only. @@ -39,11 +39,11 @@ If you'd like to encrypt your report, request our PGP key in the first message. ### What to expect -| Timeframe | What we do | -| ------------------ | -------------------------------------------------------------------------- | +| Timeframe | What we do | +| ----------------------- | -------------------------------------------------------------------------- | | Within 3 business days | Initial acknowledgement — we've received your report and are assessing it. | | Within 10 business days | Triage outcome — confirmed, declined, or duplicate, with reasoning. | -| Within 90 days | Fix released, advisory published, and credit assigned (if accepted). | +| Within 90 days | Fix released, advisory published, and credit assigned (if accepted). | For critical issues actively exploited in the wild, we'll work with you on an accelerated disclosure timeline. @@ -71,6 +71,24 @@ We support coordinated disclosure. As long as you act in good faith, comply with - Treat your testing as authorized under the Computer Fraud and Abuse Act and equivalent laws. - Work with you on disclosure timing. +## Security model for embedded content + +Virtual Frame is, by design, a mechanism for embedding HTML from a remote origin into a server-rendered response — a "virtual iframe". The trust model is the same one you accept when you write ` + ``` diff --git a/docs/guide/vue.md b/docs/guide/vue.md index 0e5bf6e..d1e0d04 100644 --- a/docs/guide/vue.md +++ b/docs/guide/vue.md @@ -20,11 +20,7 @@ import { VirtualFrame } from "@virtual-frame/vue"; ``` @@ -39,14 +35,14 @@ When the component unmounts, the iframe is torn down, mutation observers and cap ## Props -| Prop | Type | Description | -| -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------- | -| `src` | `string` | URL to load and project. Mutually exclusive with `frame`. | -| `frame` | `VirtualFrameRef` | Shared source from [`useVirtualFrame()`](#sharing-one-source-across-components). Mutually exclusive with `src`. | -| `isolate` | `"open" \| "closed"` | Shadow DOM mode for CSS isolation. Omit to render into the host `
` directly. See [Shadow DOM](/guide/shadow-dom). | -| `selector` | `string` | CSS selector — only project a matching subtree. See [Selector Projection](/guide/selector). | +| Prop | Type | Description | +| -------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `src` | `string` | URL to load and project. Mutually exclusive with `frame`. | +| `frame` | `VirtualFrameRef` | Shared source from [`useVirtualFrame()`](#sharing-one-source-across-components). Mutually exclusive with `src`. | +| `isolate` | `"open" \| "closed"` | Shadow DOM mode for CSS isolation. Omit to render into the host `
` directly. See [Shadow DOM](/guide/shadow-dom). | +| `selector` | `string` | CSS selector — only project a matching subtree. See [Selector Projection](/guide/selector). | | `streamingFps` | `number \| Record` | FPS for `` / `