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
10 changes: 10 additions & 0 deletions .yarn/changelogs/common.75c8ebcb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!-- version-type: patch -->

# common

## ⬆️ Dependencies

- Updated `@furystack/core` to `^16.0.4`
- Updated `@furystack/entity-sync` to `^1.0.11`
- Updated `@furystack/rest` to `^9.0.0` (major)
- Updated dev `@types/node` to `^25.6.0` and `vitest` to `^4.1.5`
28 changes: 28 additions & 0 deletions .yarn/changelogs/frontend.75c8ebcb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- version-type: patch -->

# frontend

## ♻️ Refactoring

### Strongly-typed nested routing

Replaced the hand-rolled `AppLink` / `AppBarAppLink` double-cast wrappers in `routes/index.ts` with the upstream type-safe factories exposed by `@furystack/shades` 14 and `@furystack/shades-common-components` 16:

- Route tree is now declared through `defineNestedRoutes(...)` so per-route literal types (including future `query` / `hash` schemas) are preserved.
- `AppLink = createNestedRouteLink<typeof appRoutes & typeof authRoutes>()`
- `AppBarAppLink = createAppBarLink<typeof appRoutes & typeof authRoutes>()`
- `AppPaths` is now re-exported via the upstream `ExtractRoutePaths` helper instead of the locally duplicated type.

All `<AppLink href="..." />`, `<AppBarAppLink href="..." />` and `<NestedRouteLink href="..." />` call sites were renamed to use the new `path` prop that v14 introduced.

## ⬆️ Dependencies

- Updated `@furystack/shades` to `^14.0.0` (major β€” `href` β†’ `path` prop on `NestedRouteLink`; removed legacy flat router exports)
- Updated `@furystack/shades-common-components` to `^16.0.0` (major β€” `AppBarLink` uses `path`; removed `Grid` / `Autocomplete` / row-click options)
- Updated `@furystack/shades-lottie` to `^10.0.0` (major)
- Updated `@furystack/shades-mfe` to `^4.0.0` (major)
- Updated `@furystack/rest` to `^9.0.0` (major) and `@furystack/rest-client-fetch` to `^8.1.8`
- Updated `@furystack/cache` to `^6.1.5`, `@furystack/core` to `^16.0.4`, `@furystack/entity-sync` to `^1.0.11`, `@furystack/entity-sync-client` to `^2.0.5`, `@furystack/inject` to `^12.0.36`, `@furystack/logging` to `^8.1.5`, `@furystack/utils` to `^8.2.5`
- Updated `@codecov/vite-plugin` to `^2.0.1` (major)
- Updated `hls.js` to `^1.6.16`, `path-to-regexp` to `^8.4.2`
- Updated dev `typescript` to `^6.0.3`, `vite` to `^8.0.10`, `@types/node` to `^25.6.0`
7 changes: 7 additions & 0 deletions .yarn/changelogs/monaco-mfe.75c8ebcb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- version-type: patch -->

# monaco-mfe

## ⬆️ Dependencies

- Updated dev `typescript` to `^6.0.3` and `vite` to `^8.0.10`
17 changes: 17 additions & 0 deletions .yarn/changelogs/pi-rat.75c8ebcb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- version-type: patch -->

# pi-rat

## πŸ“¦ Build

- Bumped `packageManager` from `yarn@4.13.0` to `yarn@4.14.1` (regenerated lockfile and release bundle).

## ⬆️ Dependencies

- Updated `@furystack/eslint-plugin` to `^2.2.0` and `@furystack/yarn-plugin-changelog` to `^1.0.10`
- Updated `@playwright/test` to `^1.59.1` and `eslint-plugin-playwright` to `^2.10.2`
- Updated `eslint` to `^10.2.1`, `typescript-eslint` to `^8.59.0`, `eslint-plugin-jsdoc` to `^62.9.0`
- Updated `prettier` to `^3.8.3`
- Updated `typescript` to `^6.0.3`
- Updated `vite` to `^8.0.10`, `vitest` to `^4.1.5`, `@vitest/coverage-v8` to `^4.1.5`
- Updated `@types/node` to `^25.6.0` and `jsdom` to `^29.0.2`
26 changes: 26 additions & 0 deletions .yarn/changelogs/service.75c8ebcb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!-- version-type: patch -->

# service

## ♻️ Refactoring

### Migrated the identity `/login` endpoint to `createPasswordLoginAction`

`@furystack/rest-service` 13 removed the legacy static `LoginAction`. `setupIdentityRestApi` now wires `/login` through the factory-based API that captures the auth services once at setup time:

```ts
'/login': Validate({ schema: identityApiSchema, schemaName: 'LoginAction' })(
createPasswordLoginAction(createCookieLoginStrategy(injector)) as RequestAction<PiRatLoginAction>,
)
```

## πŸ› Bug Fixes

- `execFileAsync` and the drives `UploadAction` now always reject their promises with `Error` instances (`err instanceof Error ? err : new Error(JSON.stringify(err))`) so callers observing the rejection reason can rely on a real `Error` stack, and to satisfy `@typescript-eslint/prefer-promise-reject-errors`.

## ⬆️ Dependencies

- Updated `@furystack/rest-service` to `^13.0.0` (major β€” removed legacy `LoginAction` and Swagger-named aliases)
- Updated `@furystack/rest` to `^9.0.0` (major)
- Updated `@furystack/cache` to `^6.1.5`, `@furystack/core` to `^16.0.4`, `@furystack/entity-sync` to `^1.0.11`, `@furystack/entity-sync-service` to `^1.0.13`, `@furystack/inject` to `^12.0.36`, `@furystack/logging` to `^8.1.5`, `@furystack/repository` to `^10.1.11`, `@furystack/security` to `^7.0.9`, `@furystack/sequelize-store` to `^6.0.49`, `@furystack/utils` to `^8.2.5`, `@furystack/websocket-api` to `^13.2.8`
- Updated dev `@types/formidable` to `^3.5.1`, `@types/node` to `^25.6.0`, `typescript` to `^6.0.3`, and `vitest` to `^4.1.5`
576 changes: 288 additions & 288 deletions .yarn/releases/yarn-4.13.0.cjs β†’ .yarn/releases/yarn-4.14.1.cjs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions .yarn/versions/75c8ebcb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
releases:
common: patch
frontend: patch
monaco-mfe: patch
pi-rat: patch
service: patch
2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ plugins:
path: .yarn/plugins/@yarnpkg/plugin-changelog.cjs
spec: 'https://raw.githubusercontent.com/furystack/furystack/refs/heads/develop/packages/yarn-plugin-changelog/bundles/%40yarnpkg/plugin-changelog.js'

yarnPath: .yarn/releases/yarn-4.13.0.cjs
yarnPath: .yarn/releases/yarn-4.14.1.cjs
10 changes: 5 additions & 5 deletions common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@
"create-schemas": "node ./dist/bin/create-schemas.js"
},
"devDependencies": {
"@types/node": "^25.5.0",
"@types/node": "^25.6.0",
"ts-json-schema-generator": "^2.9.0",
"vitest": "^4.1.2"
"vitest": "^4.1.5"
},
"dependencies": {
"@furystack/core": "^16.0.2",
"@furystack/entity-sync": "^1.0.9",
"@furystack/rest": "^8.1.3",
"@furystack/core": "^16.0.4",
"@furystack/entity-sync": "^1.0.11",
"@furystack/rest": "^9.0.0",
"ollama": "^0.6.3"
}
}
4 changes: 1 addition & 3 deletions common/src/utils/media/get-fallback-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ export const getFallbackMetaWithScore = (segment: string): { meta: FallbackMetad
.join(' ')
.trim()

const { season, episode } =
new RegExp(/\.S(?<season>\d+)E(?<episode>\d+)\./gm).exec(segment)?.groups ||
({} as { [K: string]: string | undefined })
const { season, episode } = new RegExp(/\.S(?<season>\d+)E(?<episode>\d+)\./gm).exec(segment)?.groups || {}

const score = [year, resolution, season, episode].filter((a) => a).length

Expand Down
38 changes: 19 additions & 19 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@codecov/vite-plugin": "^1.9.1",
"@furystack/rest": "^8.1.3",
"typescript": "^6.0.2",
"vite": "^8.0.3"
"@codecov/vite-plugin": "^2.0.1",
"@furystack/rest": "^9.0.0",
"typescript": "^6.0.3",
"vite": "^8.0.10"
},
"dependencies": {
"@furystack/cache": "^6.1.3",
"@furystack/core": "^16.0.2",
"@furystack/entity-sync": "^1.0.9",
"@furystack/entity-sync-client": "^2.0.3",
"@furystack/inject": "^12.0.34",
"@furystack/logging": "^8.1.3",
"@furystack/rest-client-fetch": "^8.1.5",
"@furystack/shades": "^13.1.2",
"@furystack/shades-common-components": "^15.0.2",
"@furystack/shades-lottie": "^9.0.3",
"@furystack/shades-mfe": "^3.0.3",
"@furystack/utils": "^8.2.3",
"@types/node": "^25.5.0",
"@furystack/cache": "^6.1.5",
"@furystack/core": "^16.0.4",
"@furystack/entity-sync": "^1.0.11",
"@furystack/entity-sync-client": "^2.0.5",
"@furystack/inject": "^12.0.36",
"@furystack/logging": "^8.1.5",
"@furystack/rest-client-fetch": "^8.1.8",
"@furystack/shades": "^14.0.0",
"@furystack/shades-common-components": "^16.0.0",
"@furystack/shades-lottie": "^10.0.0",
"@furystack/shades-mfe": "^4.0.0",
"@furystack/utils": "^8.2.5",
"@types/node": "^25.6.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-search": "^0.16.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0",
"common": "workspace:^",
"hls.js": "^1.6.15",
"hls.js": "^1.6.16",
"ollama": "^0.6.3",
"path-to-regexp": "^8.4.0",
"path-to-regexp": "^8.4.2",
"video.js": "8.23.8"
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/ai/ai-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const AiIcon = Shade({
}

return (
<AppBarAppLink title="Ai" href="/ai">
<AppBarAppLink title="Ai" path="/ai">
{sessionState === 'authenticated' ? 'πŸ€–' : 'πŸ”’ Login to Ai'}
</AppBarAppLink>
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/chat/chat-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const ChatIcon = Shade({
}

return (
<AppBarAppLink title="Chat" href="/chat">
<AppBarAppLink title="Chat" path="/chat">
{sessionState === 'authenticated' ? 'πŸ’¬' : 'πŸ”’ Login to chat'}
</AppBarAppLink>
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/dashboard/device-availability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const DeviceAvailabilityContent = Shade<{
<AppLink
tabIndex={0}
title={device.name}
href="/iot/devices/:id"
path="/iot/devices/:id"
params={{ id: device.name }}
style={{ textDecoration: 'none' }}
>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/dashboard/icon-url-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ export const IconUrlWidget = Shade<IconUrlWidgetProps>({
return { [Symbol.dispose]: () => clearTimeout(id) }
})

const href: string = props.url
const path: string = props.url

return (
<NestedRouteLink title={props.description} href={href}>
<NestedRouteLink title={props.description} path={path}>
<div
ref={cardRef}
className="widget-card"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/dashboard/movie-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const MovieWidgetContent = Shade<{
const posterUrl = localizedData?.posterUrl

return (
<AppLink tabIndex={0} title={plot || title} href="/movies/:imdbId/overview" params={{ imdbId }}>
<AppLink tabIndex={0} title={plot || title} path="/movies/:imdbId/overview" params={{ imdbId }}>
<WidgetCard size={size} index={props.index}>
<div
className="overlay"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/dashboard/series-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const SeriesWidgetContent = Shade<{
const posterUrl = localizedData?.posterUrl

return (
<AppLink tabIndex={0} title={plot || title} href="/series/:imdbId" params={{ imdbId }}>
<AppLink tabIndex={0} title={plot || title} path="/series/:imdbId" params={{ imdbId }}>
<WidgetCard size={size} index={props.index}>
{posterUrl ? (
<img
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/generic-editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export const GenericEditor: <T, TKey extends keyof T, TReadonlyProperties extend
onFindOptionsChange={(opts) => service.findOptions.setValue(opts)}
columns={['selection', ...columns, 'actions'] as unknown as typeof columns}
headerComponents={extendedHeaderComponents as unknown as typeof headerComponents}
rowComponents={extendedRowComponents as unknown as typeof rowComponents}
rowComponents={extendedRowComponents}
styles={{
...styles,
header: { width: '128px', ...styles?.header },
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/generic-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const GenericErrorPage = Shade<GenericErrorProps>({

return (
<Result status={status} title={mainTitle} subtitle={description}>
<NestedRouteLink href="/">
<NestedRouteLink path="/">
<Button>
<Icon icon={icons.home} size="small" /> Go Home
</Button>
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const AdminLinks = Shade({
const isAdmin = currentUser?.roles?.includes('admin') ?? false

return isAdmin ? (
<AppBarAppLink href="/file-browser" title="Drives">
<AppBarAppLink path="/file-browser" title="Drives">
<Icon icon={icons.folderOpen} size="small" /> Files
</AppBarAppLink>
) : null
Expand All @@ -55,19 +55,19 @@ export const Header = Shade<HeaderProps>({

return (
<AppBar id="header">
<AppBarAppLink title={props.title} href="/" routingOptions={{ end: false }}>
<AppBarAppLink title={props.title} path="/" routingOptions={{ end: false }}>
<PiRatLogo size={24} style={{ marginRight: '8px' }} />
{props.title}
</AppBarAppLink>
{sessionState === 'authenticated' ? (
<>
{currentUser?.roles?.includes('admin') ? <AdminLinks /> : null}

<AppBarAppLink title="Movies" href="/movies">
<AppBarAppLink title="Movies" path="/movies">
<Icon icon={icons.film} size="small" /> Movies
</AppBarAppLink>

<AppBarAppLink title="Series" href="/series">
<AppBarAppLink title="Series" path="/series">
πŸ“Ί Series
</AppBarAppLink>
</>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/entities/dashboards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const DashboardsPage = Shade({
rowComponents={{
id: ({ id }) => {
return (
<AppLink href="/dashboards/:id" params={{ id }}>
<AppLink path="/dashboards/:id" params={{ id }}>
Preview
</AppLink>
)
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/pages/file-browser/drive-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { CacheView, Select, Skeleton } from '@furystack/shades-common-components
import type { Drive } from 'common'
import { DrivesService } from '../../services/drives-service.js'
import { ErrorDisplay } from '../../components/error-display.js'
import type { DriveLocation } from './index.js'

const DriveSelectorContent = Shade<{
data: CacheWithValue<GetCollectionResult<Drive>>
Expand All @@ -17,7 +16,7 @@ const DriveSelectorContent = Shade<{
const [currentDrive, setCurrentDrive] = useSearchState(props.searchStateKey, {
path: '/',
letter: props.defaultDriveLetter,
} as DriveLocation)
})

return (
<Select
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/file-browser/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const DrivesPage = Shade({
const drivesService = injector.getInstance(DrivesService)
const [drives] = useObservable('drives', drivesService.getVolumesAsObservable({}))

const [focused, setFocused] = useSearchState('focused', 'ld' as 'ld' | 'rd')
const [focused, setFocused] = useSearchState('focused', 'ld')

if (!hasCacheValue(drives)) {
return null
Expand Down
28 changes: 7 additions & 21 deletions frontend/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import './route-meta-augmentation.js'

import { NestedRouteLink, type ChildrenList, type NestedRoute, type TypedNestedRouteLinkProps } from '@furystack/shades'
import { AppBarLink, type AppBarLinkProps } from '@furystack/shades-common-components'
import { defineNestedRoutes, type ExtractRoutePaths } from '@furystack/shades'
import { createAppBarLink } from '@furystack/shades-common-components'

import { authRoutes as _authRoutes } from './auth-routes.js'
import { entityRoute } from './entity-routes.js'
Expand All @@ -14,17 +14,9 @@ import { seriesRoutes } from './series-routes.js'
import { settingsRoute } from './settings-routes.js'
import { userRoute } from './user-routes.js'

type ConcatPaths<Parent extends string, Child extends string> = Parent extends '/' ? Child : `${Parent}${Child}`
import { createNestedRouteLink } from '@furystack/shades'

type ExtractRoutePaths<T extends Record<string, NestedRoute<any>>> = {
[K in keyof T & string]:
| K
| (T[K] extends { children: infer C extends Record<string, NestedRoute<any>> }
? ConcatPaths<K, ExtractRoutePaths<C> & string>
: never)
}[keyof T & string]

export const appRoutes = {
export const appRoutes = defineNestedRoutes({
...movieRoutes,
...seriesRoutes,
'/app-settings': settingsRoute,
Expand All @@ -34,18 +26,12 @@ export const appRoutes = {
'/logging': loggingRoute,
'/user': userRoute,
...miscRoutes,
}
})

export const authRoutes = _authRoutes

export type AppPaths = ExtractRoutePaths<typeof appRoutes & typeof authRoutes>

export const AppLink = NestedRouteLink as unknown as <TPath extends AppPaths>(
props: TypedNestedRouteLinkProps<TPath>,
children?: ChildrenList,
) => JSX.Element
export const AppLink = createNestedRouteLink<typeof appRoutes & typeof authRoutes>()

export const AppBarAppLink = AppBarLink as unknown as <TPath extends AppPaths>(
props: AppBarLinkProps & { href: TPath },
children?: ChildrenList,
) => JSX.Element
export const AppBarAppLink = createAppBarLink<typeof appRoutes & typeof authRoutes>()
Loading
Loading