Instruction file imported from RajwanYair/FamilyDashBoard (
.github/instructions/typescript.instructions.md). Copyright stays with the author.
TypeScript Instructions
Apply these rules to every
.tsfile undersrc/. Rules incopilot-instructions.mdtake precedence for cross-cutting concerns.
Strict Mode Baseline
- Target:
ES2024— useArray.at(),Object.hasOwn(),structuredClone(),crypto.randomUUID(),Promise.withResolvers()freely - Always use
"verbatimModuleSyntax"— import types withimport type { … } - Avoid
any. Useunknownat system boundaries; narrow immediately with type guards - Never use
@ts-ignoreor@ts-expect-errorwithout a comment explaining the root cause - Never silence with
// eslint-disable— fix the underlying issue instead
Module & Import Conventions
- Use
@/path alias for allsrc/imports:import { cGet } from "@/core/cache" - Use
import typefor type-only imports:import type { DashboardConfig } from "@/types/config" - Never use bare relative
../../../paths across module layers - Export order: types first, then values, then helpers
Async & Error Handling
- All async loaders: guard with
if (!_pageVisible) return;at the top of the function - All fetches:
try/catch+ proxy fallback (PROXIES) +diagLog() - Use
await+safeLoad()— no raw.then().catch()chains in loader functions - Never nest
try/catchmore than 2 levels — extract a helper cGet()/cGetStale()returnnull(notundefined) on cache miss — check!== null
DOM Access Patterns
- DOM refs go in the
elobject at module scope — no repeatedgetElementByIdcalls - Use
textContentnotinnerHTMLfor user-visible text — never interpolate unsanitized data - New overlays: use
<dialog>with.showModal()/.close()— not<div>visibility toggling - Validate that DOM element IDs exist in
index.htmlbefore writing any loader code (rule 14)
Naming Conventions
| Context | Convention |
| -------------------- | ----------------------------------------------------------- | -------------------------- |
| Module-private state | _camelCase prefix |
| Test-only exports | _resetForTest / _*ForTest pattern |
| Config toggle unit | \_tempUnit = 'C' | 'F'(not\_useFahrenheit) |
| Cache reads | cGet(key, ttl) / cGetStale(key) / cGetAsync(key, ttl) |
| Cache writes | cSet(key, data) |
| Sync indicator | setSync(id, state) (not setSyncStatus) |
| Card loader | loadAllX() (not loadX()) |
Type Guards
- Write a named type guard function (
isXyzResponse) for every external API response type - Place guards in
src/types/api.tsnext to the interface definition - Guard pattern:
function isObj(v): v is Record<string, unknown>+ field-by-field checks - Never cast
as Tdirectly onJSON.parse()output — always validate first
Card Architecture
- Cards registered via
registerCard()insrc/core/card-registry.ts - New cards extend
FdbCard(src/core/fdb-card.ts) — do not use oldinitX()file-scoped pattern for new cards data-card-idmust match the registry ID exactly (e.g."hebrew-cal","calendar","motivation")- Card content layout: rectangular tile/grid blocks — never plain vertical line lists (rule 25)
Cache & State Access
- Dual-layer cache: in-memory
Map(fast) +localStorage(persistent) + IDB (L2 async) - All API data:
cSet/cGet/cGetStalefor sync reads;cSetAsync/cGetAsync/cGetStaleAsyncfor async IDB writes - Card loaders use
await cGetAsync(key, ttl)for fresh reads,await cGetStaleAsync(key)for stale reads,await cSetAsync(key, data)for writes cGetAsync()/cGetStaleAsync()returnnull(notundefined) on cache miss — check!== null- Never write to
localStoragedirectly from a card — always use the cache API - Reactive state singleton:
state.get()/state.set()/state.on()— no global variables for UI state state._resetForTest()/cache._resetForTest()in testafterEach— notvi.resetModules()
What NOT to Do
- No
console.log/console.warn/console.errorinsrc/— usediagLog()from@/core/diag - No hardcoded colors — CSS custom properties only
- No
self.skipWaiting()in SW install handler — only viaSKIP_WAITINGmessage - No external JS/CSS libraries — zero runtime dependencies
- Root-only development tools belong in
FamilyDashBoard/package.json; browser runtime dependencies remain forbidden
Service Worker (sw.ts)
- Canonical source:
sw.ts— compiled todist/sw.jsviascripts/build-sw.mjsduringvite build - Typed global:
const sw = self as unknown as ServiceWorkerGlobalScope;— usesw.*everywhere (notself.*) - Version injection:
declare const __APP_VERSION__: string;at top — never hardcode a version string in sw.ts - SyncEvent: declared inline —
interface SyncEvent extends ExtendableEvent { readonly tag: string; readonly lastChance: boolean; } - tsconfig:
tsconfig.sw.jsonuseslib: ["ES2020","WebWorker"]— runnpm run typecheck:swto verify - Build script:
node scripts/build-sw.mjs <version>— uses TypeScripttranspileModulefrom localnode_modules - Never use
esbuilddirectly — it is embedded in Vite and not available as a standalone package in this monorepo
Extension Integration
- ESLint (
dbaeumer.vscode-eslint): inline diagnostics surface inget_errors— preferget_errorsover terminalnpx eslintfor single-file validation. - Error Lens (
usernamehw.errorlens): shows errors inline in the editor — ensures issues are visible immediately during editing. - Console Ninja (
wallabyjs.console-ninja): inlineconsole.log/diagLog()output in the editor — use for debugging data flow without switching to terminal. - Path IntelliSense (
christian-kohler.path-intellisense): auto-completes@/import paths — reduces typos in module references. - Version Lens (
pflannery.vscode-versionlens): shows latest package versions inpackage.json— use to check for outdated deps. - Bookmarks (
alefragnani.bookmarks): mark key code locations during debugging sessions — use to navigate complex card/adapter chains. - Code Spell Checker (
streetsidesoftware.code-spell-checker): surfaces spelling errors in comments and string literals viaget_errors.
Worker Zod Schemas (worker/src/utils/schemas.ts)
- All worker route handlers validate upstream responses with
safeParse(Schema, data)before forwarding - Schema naming:
FooBarSchema— Zodz.object({...}).passthrough()for JSON;z.string().refine(...)for text safeParse()returns{ ok: true, data }or{ ok: false, error }— never throws- Return HTTP 502 with
{ error: "...", detail: validated.error }when validation fails - News/RSS: use
NewsRssSchema(structural XML marker check:<channel>+<item>or<feed>+<entry>) - Worker typecheck:
npx tsc --project worker/tsconfig.json --noEmit