Files
tibi-svelte-starter/.agents/skills/tibi-hook-authoring/SKILL.md
T
apairon 491f495c66 feat: enhance project setup and architecture documentation
- Updated `tibi-project-setup` skill to clarify project initialization goals and steps.
- Improved `tibi-ssr-caching` skill to detail SSR architecture, responsibilities, and caching mechanisms.
- Introduced `website-solution-architecture` skill for translating website requirements into coherent solutions.
- Refined `AGENTS.md` to provide a structured roadmap for project development phases.
- Added `ADMIN_ASSET_VERSION` to `api/config.yml.env` for asset versioning.
- Updated SSR request flow and cache invalidation logic in `api/hooks/ssr/AGENTS.md`.
- Removed obsolete `esbuild.config.admin.js` and integrated asset versioning into the main `esbuild.config.js`.
- Adjusted `api/collections/content.yml` to utilize asset versioning for admin scripts.
2026-05-12 20:01:22 +00:00

5.0 KiB

name, description
name description
tibi-hook-authoring Write and debug server-side hooks for tibi-server (goja Go JS runtime). Covers IIFE structure, HookResponse/HookException types, context.filter Go-object quirk, single-item vs list retrieval, and MongoDB filter patterns. Use when creating or modifying files in api/hooks/.

tibi-hook-authoring

Use this skill for current tibi-server hook architecture, not just simple CRUD filters. A real website project on this starter typically needs hooks for public filtering, SSR invalidation, action endpoints, validation, and editor safety.

Hook file structure

Wrap every hook in an IIFE:

;(function () {
    /** @type {HookResponse} */
    const response = { status: 200 }

    // ... hook logic ...

    return response
})()

Always return a HookResponse or throw a HookException.

For many hooks, throwing is the normal control flow, especially in SSR hooks where HTML/status are returned via a thrown object.

Type safety

  • Use inline JSDoc type casting: /** @type {TypeName} */ (value).
  • Reference typed collection entries from types/global.d.ts.
  • Avoid @ts-ignore; use proper casting instead.
  • Use const and let instead of var — the goja runtime supports them.

context.filter — Go object quirk

context.filter is a Go object, not a regular JS object. Even when empty, it is truthy.

Always check with Object.keys():

const requestedFilter =
    context.filter &&
    typeof context.filter === "object" &&
    !Array.isArray(context.filter) &&
    Object.keys(context.filter).length > 0
        ? context.filter
        : null

Never use context.filter || null — it is always truthy and produces an empty filter inside $and, which crashes the Go server.

Single-item vs. list retrieval

For GET /:collection/:id, the Go server sets _id automatically from the URL parameter.

GET read hooks should not set their own _id filter for req.param("id"). Only add authorization filters (e.g. { userId: userId }).

Current hook surfaces that matter for website projects

  • Collection CRUD hooks under get, post, put, delete
  • Bulk hooks for optimized bulk operations
  • audit.return hooks for stripping sensitive data from audit output
  • actions: hook chains for endpoint-like behavior without a backing CRUD collection

For website builds on this starter, do not force everything into collections. Contact forms, newsletter signups, webhook receivers, import jobs, calculators, or other endpoint-style logic often belong into actions: instead.

HookResponse fields (GET hooks)

Field Purpose
filter MongoDB filter (list retrieval, or restrict single-item)
selector MongoDB projection ({ field: 0 } exclude, { field: 1 } include)
offset Pagination offset
limit Pagination limit
sort Sort specification
pipelineMod Function to manipulate the aggregation pipeline

context.data for write hooks

  • context.data can be an array for bulk operations — always guard with !Array.isArray(context.data).
  • For POST hooks, context.data.id may contain the new entry ID.
  • For PUT, req.param("id") gives the entry ID.

Bulk and optimized paths

  • tibi-server supports optimized bulk paths.
  • In bulk scenarios, bind still runs once at the start.
  • Per-document validation/update/delete hooks may be skipped depending on the chosen bulk path.

If a website feature depends on per-entry logic, do not assume a bulk update behaves exactly like N single updates. Check whether a dedicated bulk hook exists or whether the optimized path changes the behavior you rely on.

Action hooks

Actions are first-class endpoints and should be part of the skill set for complete website builds.

Typical action steps:

  • post.bind
  • post.validate
  • post.handle
  • post.return
  • get.handle
  • get.return

Use actions when the website needs business logic without a CRUD collection.

Typical website use cases:

  • contact forms
  • newsletter signups
  • quote/order requests
  • webhook receivers
  • utility endpoints
  • AI-assisted helper endpoints

Practical hook patterns for this starter family

  • public read filtering for active/publication state
  • SSR cache invalidation after writes
  • route-level SSR validation
  • mutation safeguards for readonly/system-managed fields
  • custom form/action validation
  • audit-output sanitizing for sensitive fields

Common pitfalls

  • Do not assume browser/Node APIs in hooks. The runtime is goja-based server-side JS.
  • Do not treat actions as fake collections unless there is a good reason.
  • Do not assume bulk hooks run per document.
  • Do not build SSR/cache logic into frontend code when the invalidation belongs in hooks.