This document captures practical conventions, commands, and patterns for working on this repository. It is intended for both humans and automation agents.
- Stack: Nextcloud app (PHP 8.0+), Composer (dev), JS/TS with esbuild, Svelte 5 (runes API), Axios via
@nextcloud/axios, routing via@nextcloud/router, initial state via@nextcloud/initial-state. - Bundles:
js/client.js: browser client that reports frontend errors to the app backend (consumes initial state)- Event browser (Svelte) built via Vite for the admin UI
- PHP: 8.0.x supported by composer platform (tooling may run on newer PHP, but project compatibility targets 8.0)
- Node.js: >= 18 (tested with Node 20)
- Composer: use the checked-in
composer.pharfor reproducible operation
- Build frontend
- Event browser:
npm run build:event-browser - Client:
npm run build:client - All bundles:
npm run build
- Event browser:
- PHP checks
- Lint:
php composer.phar run lint - Static analysis (Psalm):
php composer.phar run psalm -n - Coding style (check):
php composer.phar run cs:check - Coding style (fix):
php composer.phar run cs:fix
- Lint:
- Dependency update
- JS:
npm outdated/npm update - PHP:
php composer.phar outdated/php composer.phar update --prefer-stable
- JS:
- JavaScript
- Use exact or caret constraints for devDependencies as appropriate.
- Svelte 5 is in use; prefer the runes API (e.g.,
$state). Ensure code remains compatible with that version. - esbuild is used both directly and via
esbuild-svelteplugin.
- PHP
composer.jsonpins platform to PHP 8.0.2 for Nextcloud app compatibility.- Keep
nextcloud/ocpat the branch matching the targeted Nextcloud stable (currentlydev-stable29). - Prefer stable updates with
--prefer-stable; avoid forcing major upgrades of transitive deps that conflict with OCP.
- Namespaces:
OCA\FrontendInsight\... - Controllers extend
OCP\AppFramework\Controller\Controllerand may use attributes like#[CORS]and#[ApiRoute]. - DB layer
- Entities in
lib/Db/*and mappers extendOCP\AppFramework\Db\QBMapper. - Always add generics annotation for mappers, e.g.,
@extends QBMapper<Event>to satisfy Psalm. - Prefer
selectDistinct('col')over lower-level helpers for portability across OCP versions.
- Entities in
- Migrations
- Use
OCP\Migration\SimpleMigrationStepwithISchemaWrapper. - Ensure
hasTable()andcreateTable()use the same table name. - Avoid referencing Doctrine classes directly in docblocks; rely on OCP abstractions. If necessary, suppress with a scoped Psalm suppression.
- Use
- Logging
- Use PSR-3
Psr\Log\LoggerInterfaceinjected via DI.
- Use PSR-3
- Svelte 5 runes: state variables use
$state(...)and lifecycle viaonMount. - Custom element:
src/backoffice/App.svelteis compiled withcustomElement: trueand exported as<fei-backoffice>. - HTTP
- Use
@nextcloud/routergenerateUrl()for app routes, including templated version segments. - Use
@nextcloud/axiosfor requests; it’s pre-configured for Nextcloud auth/headers.
- Use
- Error reporting
src/client/index.tslistens forunhandledrejectionanderrorand posts to/apps/frontend_insight/report/error.- Admin settings control what is collected via initial state provided in
Application::boot()and consumed via@nextcloud/initial-state.
- Event viewer UI
- Shows the time the first event was received (hidden if not available)
- Shows per-type counts (hidden if empty)
- Pagination uses
pageandlimitquery params
See API.md for full API reference.
- Routes are defined in
appinfo/routes.php:- GET
/client.js– serves the client bundle - POST
/report/error– receives error reports - GET
/api/1.0/events– pagination params:page,limit, optionalurlfilter - GET
/api/1.0/stats– returns{ firstTimestamp: number | null, byType: Record<string, number> } - GET/POST
/settings– admin read/save of settings (admin-only via controller check)- GET returns JSON with:
collect_errors,collect_unhandled_rejections,retention_hours. - POST accepts form fields and responds based on
Acceptheader:application/json→{"success": true}(for progressive enhancement)- otherwise → HTTP redirect back to the admin settings page
- GET returns JSON with:
- OPTIONS CORS preflight for
/api/1.0/{path}
- GET
- Use attributes in controllers:
#[ApiRoute(verb: 'GET', url: '/api/{version}/events')]#[CORS]for cross-origin where required
- CSP:
Application::boot()injects a script viaUtil::addHeaderand configures CSP using the CSP nonce. - Initial state is provided with
Util::addInitialState; do not expose secrets. - When adding external resources, update CSP to whitelist domains as needed.
- Validate/escape user-provided data surfaced in templates or logs.
- PHP lint and Psalm must pass with zero errors.
- Run code style checks and prefer
cs:fixto apply standard formatting. - There are currently no automated JS tests; consider adding lightweight checks if complexity grows.
- After changing settings behavior that affects the client, rebuild the client bundle:
npm run build:client.
- Update dependencies (npm/composer) and rebuild bundles
- Run
lint,psalm,cs:check - Verify routes and APIs still function (manual or integration environment)
- Update CHANGELOG/Release notes (if applicable)
- Psalm errors in migrations often stem from docblock types referencing Doctrine classes; prefer OCP interfaces or add a narrow
@psalm-suppress. - If DB query methods are missing (e.g.,
getColumnName), preferIQueryBuildermethods (selectDistinct,createNamedParameter, etc.). - If PHP-CS-Fixer warns about PHP version mismatch, remember project target is PHP 8.0; avoid introducing 8.1+ syntax.
- Work within repository root; do not write outside.
- Prefer Composer/Node commands over raw shell operations for dependency management.
- After making code changes, always:
- Run
npm run check(Svelte type and diagnostics) - Run
npm run build(build event browser and client bundles) - Run PHP lint, Psalm, and code style
- Run
- When upgrading dependencies, avoid major version jumps that conflict with Nextcloud OCP unless explicitly requested.
- Distinct URLs with QueryBuilder (PHP):
$qb = $this->db->getQueryBuilder(); $urls = $qb->selectDistinct('url') ->from($this->getTableName()) ->setMaxResults(10) ->executeQuery() ->fetchAll();
- Svelte data load pattern (TS):
<script lang="ts"> import { onMount } from 'svelte'; import { generateUrl } from '@nextcloud/router'; import axios from '@nextcloud/axios'; let rows = $state([]); let loading = $state(false); async function load() { loading = true; const url = generateUrl('/apps/frontend_insight/api/1.0/events'); const { data } = await axios.get(url); rows = data.values; loading = false; } onMount(load); </script>
- Keep
psalm.xmlphpVersionaligned with composer platform if the language level changes. - Maintain consistent table names between migrations, mappers, and queries (e.g.,
fe_errors). - Ensure controller attributes reflect routes in
appinfo/routes.php.