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
2 changes: 1 addition & 1 deletion .github/workflows/group-apps-packages-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
version: 11.7.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
cache: pnpm
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/project-liminal-drift-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
version: 11.7.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
cache: pnpm
Expand Down Expand Up @@ -131,7 +131,7 @@ jobs:

- name: Upload browser artifacts
if: ${{ always() && steps.browser-smoke.outcome != 'skipped' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: liminal-drift-browser-smoke-${{ github.run_attempt }}
path: apps/liminal-drift/artifacts/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/project-shika-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
version: 11.7.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22.22.2
cache: pnpm
Expand Down
20 changes: 16 additions & 4 deletions .github/workflows/project-youtube-auto-resume-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@ on:
pull_request:
paths:
- "others/userscripts/youtube-auto-resume/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "patches/**"
- "scripts/auditDependencies*.mjs"
- "scripts/checkBracesSecurity.cjs"
- ".github/workflows/project-youtube-auto-resume-ci.yml"
push:
branches:
- main
paths:
- "others/userscripts/youtube-auto-resume/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "patches/**"
- "scripts/auditDependencies*.mjs"
- "scripts/checkBracesSecurity.cjs"
- ".github/workflows/project-youtube-auto-resume-ci.yml"

permissions:
Expand Down Expand Up @@ -40,14 +50,14 @@ jobs:
version: 11.7.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Install dependencies
run: pnpm --filter @cedarflake/youtube-auto-resume --fail-if-no-match install --frozen-lockfile
run: pnpm install --frozen-lockfile

- name: Check userscript version
if: github.event_name == 'pull_request'
Expand All @@ -67,6 +77,8 @@ jobs:
- name: Check YouTube userscript
run: pnpm --filter @cedarflake/youtube-auto-resume --fail-if-no-match check

- name: Audit YouTube userscript dependencies
- name: Audit workspace dependencies
if: ${{ !cancelled() }}
run: pnpm --config.registry=https://registry.npmjs.org/ --filter @cedarflake/youtube-auto-resume --fail-if-no-match audit --audit-level high
run: |
pnpm test:dependency-audit
pnpm audit:dependencies
20 changes: 16 additions & 4 deletions .github/workflows/project-youtube-auto-resume-extension-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@ on:
pull_request:
paths:
- "apps/youtube-auto-resume-extension/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "patches/**"
- "scripts/auditDependencies*.mjs"
- "scripts/checkBracesSecurity.cjs"
- ".github/workflows/project-youtube-auto-resume-extension-ci.yml"
push:
branches:
- main
paths:
- "apps/youtube-auto-resume-extension/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "patches/**"
- "scripts/auditDependencies*.mjs"
- "scripts/checkBracesSecurity.cjs"
- ".github/workflows/project-youtube-auto-resume-extension-ci.yml"

permissions:
Expand All @@ -39,14 +49,14 @@ jobs:
version: 11.7.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Install dependencies
run: pnpm --filter @cedarflake/youtube-auto-resume-extension --fail-if-no-match install --frozen-lockfile
run: pnpm install --frozen-lockfile

- name: Check extension
run: pnpm --filter @cedarflake/youtube-auto-resume-extension --fail-if-no-match check
Expand All @@ -60,6 +70,8 @@ jobs:
- name: Test extension runtime
run: pnpm --filter @cedarflake/youtube-auto-resume-extension --fail-if-no-match test:e2e

- name: Audit extension dependencies
- name: Audit workspace dependencies
if: ${{ !cancelled() }}
run: pnpm --config.registry=https://registry.npmjs.org/ --filter @cedarflake/youtube-auto-resume-extension --fail-if-no-match audit --audit-level high
run: |
pnpm test:dependency-audit
pnpm audit:dependencies
4 changes: 2 additions & 2 deletions .github/workflows/repo-codeql-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ jobs:
persist-credentials: false

- name: Initialize CodeQL
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
build-mode: none
languages: javascript-typescript

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
category: "/language:javascript-typescript"
2 changes: 1 addition & 1 deletion .github/workflows/repo-repository-contract-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
version: 11.7.0

- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22.22.2
cache: pnpm
Expand Down
2 changes: 1 addition & 1 deletion apps/liminal-drift/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pnpm check:rules

## Verification

- `pnpm check` runs formatting checks, lint, procedural generation checks, game-rule checks, license policy checks, production build, and bundle budget checks.
- `pnpm check` runs formatting checks, lint, procedural generation checks, game-rule checks, browser-helper regressions, license policy checks, production build, and bundle budget checks.
- `pnpm check:bundle` verifies the built JS/CSS assets stay within raw and gzip size budgets.
- `pnpm check:canvas -- <url>` captures desktop and mobile screenshots, checks the 3D scene is visible and moving, verifies modal focus / telemetry / progress semantics, and covers blocked local storage, invalid best-score storage, reduced-motion CSS, and repeated Escape input.
- `pnpm check:interaction -- <url>` verifies keyboard driving, drifting, reverse darkness, single-click restart at the road end, interference reset, pause/resume, background freezing with interrupted menu transitions, gamepad input, and the mobile desktop-required fallback.
Expand Down
3 changes: 2 additions & 1 deletion apps/liminal-drift/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"preview": "vite preview",
"format": "prettier . --write",
"format:check": "prettier . --check",
"check": "pnpm run format:check && pnpm lint && pnpm run check:generation && pnpm run check:rules && pnpm run check:licenses && pnpm build && pnpm run check:bundle",
"check": "pnpm run format:check && pnpm lint && pnpm run check:generation && pnpm run check:rules && pnpm run check:browser-helpers && pnpm run check:licenses && pnpm build && pnpm run check:bundle",
"check:browser-helpers": "node --test scripts/browserDrivingChecks.test.mjs",
"check:browser": "pnpm run check:canvas -- http://127.0.0.1:4173/ && pnpm run check:interaction -- http://127.0.0.1:4173/",
"check:bundle": "node scripts/checkBundleBudget.mjs",
"check:canvas": "node scripts/checkCanvas.mjs",
Expand Down
24 changes: 24 additions & 0 deletions apps/liminal-drift/scripts/browserDrivingChecks.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** @param {Element} element */
export function observeStoppedSpeed(element) {
const initialText = element.textContent?.trim()
const initialSpeed = Number(initialText)
if (!initialText || !Number.isFinite(initialSpeed) || initialSpeed < 1) {
throw new Error(`Expected a moving car before observing its stop, got "${initialText}"`)
}
const observation = {
hasStopped: false,
disconnect: () => observer.disconnect(),
}
const observe = () => {
const text = element.textContent?.trim()
const speed = Number(text)
if (text && Number.isFinite(speed) && speed >= 0 && speed < 1) {
observation.hasStopped = true
observer.disconnect()
}
}
const observer = new MutationObserver(observe)
observer.observe(element, { childList: true, characterData: true, subtree: true })
observe()
return observation
}
72 changes: 72 additions & 0 deletions apps/liminal-drift/scripts/browserDrivingChecks.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import assert from "node:assert/strict"
import test from "node:test"
import { runInNewContext } from "node:vm"

import { observeStoppedSpeed } from "./browserDrivingChecks.mjs"

/** @param {string | null} initialSpeed */
function monitor(initialSpeed) {
const element = { textContent: initialSpeed }
/** @type {(() => void) | undefined} */
let callback
let isConnected = false
const observation = runInNewContext(`(${observeStoppedSpeed.toString()})(element)`, {
element,
MutationObserver: class {
/** @param {() => void} onMutation */
constructor(onMutation) {
callback = onMutation
}
observe() {
isConnected = true
}
disconnect() {
isConnected = false
}
},
})
return {
observation,
get isConnected() {
return isConnected
},
/** @param {string | null} speed */
update(speed) {
element.textContent = speed
if (isConnected) callback?.()
},
}
}

test("retains a real stop when delayed polling sees reverse speed", () => {
const state = monitor("42")
assert.equal(state.observation.hasStopped, false)
state.update("0")
state.update("12")
assert.equal(state.observation.hasStopped, true)
assert.equal(state.isConnected, false)
})

test("does not report a stop without a valid stopped-speed sample", () => {
const state = monitor("42")
for (const speed of ["", " ", null, "NaN", "Infinity", "-1", "1", "12"]) {
state.update(speed)
assert.equal(state.observation.hasStopped, false)
}
state.update("0")
assert.equal(state.observation.hasStopped, true)
})

test("requires a moving car before observing the stop", () => {
for (const speed of ["0", "", " ", null, "NaN", "Infinity", "-1"]) {
assert.throws(() => monitor(speed), /Expected a moving car/)
}
})

test("disconnects when a browser check fails before stopping", () => {
const state = monitor("42")
state.observation.disconnect()
state.update("0")
assert.equal(state.observation.hasStopped, false)
assert.equal(state.isConnected, false)
})
26 changes: 22 additions & 4 deletions apps/liminal-drift/scripts/checkCanvas.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url"

import { chromium, devices } from "playwright"

import { observeStoppedSpeed } from "./browserDrivingChecks.mjs"
import { browserLaunchOptions } from "./browserLaunchOptions.mjs"
import {
measurePeripheralDifference,
Expand Down Expand Up @@ -549,7 +550,6 @@ try {

await page.waitForTimeout(2600)
await page.keyboard.up("w")
await page.keyboard.down("s")

const telemetryLines = (await page.locator(".hud").innerText())
.split(/\r?\n/)
Expand Down Expand Up @@ -587,13 +587,31 @@ try {
)
}

// Stop before encoding several screenshots: real bends now require steering,
// and a slow screenshot must not leave the throttle held into the first bend.
// Latch the brief stop before holding the brake: slow protocol round trips
// must not miss it and wait forever after the brake has engaged reverse.
const stoppedSpeed = await page
.locator(".hud__metric--speed strong")
.evaluateHandle(observeStoppedSpeed)
try {
await page.keyboard.down("s")
await page.waitForFunction((observation) => observation.hasStopped, stoppedSpeed)
} finally {
try {
await page.keyboard.up("s")
} finally {
try {
await stoppedSpeed.evaluate((observation) => observation.disconnect())
} finally {
await stoppedSpeed.dispose()
}
}
}
// A delayed key release can briefly engage reverse. Verify the current car
// has coasted to rest as well, before encoding screenshots with no held keys.
await page.waitForFunction(() => {
const text = document.querySelector(".hud__metric--speed strong")?.textContent
return Boolean(text) && Number(text) < 1
})
await page.keyboard.up("s")

await page.screenshot({
path: join(outputPath, `${viewport.name}.png`),
Expand Down
Loading