refactor(lib): enforce layered architecture, split god-module, fork the kit master
src/lib had no enforced layering: ui/ imported feature/ in ten places, app/util.svelte.ts was a 21-export god-module with 38 importers, and the vendored mono-svelte fork was aliased as if it were an external package while five of its files reached back into app state. This restructures src/lib into a strict downward-only dependency graph and adds an ESLint rule so it cannot rot back: routes -> feature -> ui -> app (state, util) -> api ui/kit <- leaf, imports none of the above Structure: - Split app/util.svelte.ts into one-concern modules under app/util/ (array, text, url, links, navigation, ssr, reactive, placeholders, error, merge). uploadImage was an HTTP call, so it moved to api/upload.ts. - Move auth/settings/instance/session/theme/i18n to app/state/, and the sort + image-proxy API mappings to api/coves/. - Move markdown/ and richtext/ to feature/ (both import feature/post and ui/), and the page compositions (PostListShell, EntityHeader, Navbar, Profile, Sidebar, commands/) to feature/shell/. The kit: - Rename ui/shared -> ui/kit and drop the `mono-svelte` alias; all 106 importers now use `$lib/ui/kit`. The fork has no upstream remote, so it stops pretending to be a dependency. - Make it app-agnostic by injection rather than import: ToastContainer takes a `content` snippet (the root layout supplies the Markdown renderer, so a toast no longer drags in the whole markdown tree), ModalContainer takes `closeLabel`, RelativeDate takes `locale` and `absolute` with ui/util/RelativeDate.svelte as the app-bound wrapper, and Option reads its label from the DOM instead of the app locale. Bugs found and fixed while reviewing the above: - modal.action() accepted an `icon` and silently dropped it (two live call sites), and stored a `close` flag that contradicted its behaviour. - errorMessage could render a raw JSON blob, or the literal string "undefined", into a user-facing toast. - ImageAttachForm returned the string 'Failed to upload' from its catch, which survived the filter and was then inserted as an image URL. - The kit RelativeDate rendered an invalid date as "Now", and treated ordinary server clock skew as a future date. - Timestamps now receive the viewer's locale; they were hardcoded to 'en'. Enforcement and tests: - Per-layer no-restricted-imports rule covering both `$lib/...` and relative spellings, with $lib/server barred from every client-shipped layer (type-only imports still allowed). - New tests for the layering rule itself (driven through the real ESLint API), the kit's formatRelativeDate, and recursiveEqual/moveItem. No behaviour change is intended beyond the fixes listed above. Verified with check, lint, 1160 tests and a production build, plus an in-browser pass against the local dev stack covering login, feed rendering, dropdowns, the absolute-dates setting, toast markdown and modal labels. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>