@browserlessapi/inferlo 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/ai/index.d.ts +7 -0
- package/dist/ai/index.js +68 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/public.d.ts +1 -0
- package/dist/ai/public.js.map +1 -1
- package/dist/api-version.d.ts +32 -0
- package/dist/api-version.js +45 -0
- package/dist/api-version.js.map +1 -0
- package/dist/app.d.ts +30 -3
- package/dist/app.js +210 -43
- package/dist/app.js.map +1 -1
- package/dist/audit.d.ts +50 -0
- package/dist/audit.js +92 -0
- package/dist/audit.js.map +1 -0
- package/dist/cache.d.ts +92 -0
- package/dist/cache.js +245 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli/main.js +25 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/describe.d.ts +42 -1
- package/dist/describe.js +25 -2
- package/dist/describe.js.map +1 -1
- package/dist/fastify.d.ts +10 -2
- package/dist/fastify.js +22 -4
- package/dist/fastify.js.map +1 -1
- package/dist/h3.d.ts +76 -0
- package/dist/h3.js +61 -0
- package/dist/h3.js.map +1 -0
- package/dist/hapi.d.ts +45 -0
- package/dist/hapi.js +57 -0
- package/dist/hapi.js.map +1 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +20 -0
- package/dist/jobs.js +19 -7
- package/dist/jobs.js.map +1 -1
- package/dist/koa.d.ts +51 -0
- package/dist/koa.js +45 -0
- package/dist/koa.js.map +1 -0
- package/dist/lambda.d.ts +110 -0
- package/dist/lambda.js +169 -0
- package/dist/lambda.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +7 -3
- package/dist/mcp.js.map +1 -1
- package/dist/module.d.ts +52 -0
- package/dist/module.js +152 -0
- package/dist/module.js.map +1 -0
- package/dist/nextjs.d.ts +35 -0
- package/dist/nextjs.js +33 -0
- package/dist/nextjs.js.map +1 -0
- package/dist/openapi.d.ts +3 -0
- package/dist/openapi.js +12 -3
- package/dist/openapi.js.map +1 -1
- package/dist/permissions.d.ts +26 -0
- package/dist/permissions.js +78 -0
- package/dist/permissions.js.map +1 -0
- package/dist/react-router.d.ts +41 -0
- package/dist/react-router.js +42 -0
- package/dist/react-router.js.map +1 -0
- package/dist/request-matcher.d.ts +4 -0
- package/dist/request-matcher.js +14 -0
- package/dist/request-matcher.js.map +1 -0
- package/dist/route.d.ts +36 -1
- package/dist/route.js +24 -0
- package/dist/route.js.map +1 -1
- package/dist/serve.d.ts +20 -1
- package/dist/serve.js +48 -4
- package/dist/serve.js.map +1 -1
- package/dist/sessions.d.ts +72 -0
- package/dist/sessions.js +171 -0
- package/dist/sessions.js.map +1 -0
- package/dist/sveltekit.d.ts +72 -0
- package/dist/sveltekit.js +68 -0
- package/dist/sveltekit.js.map +1 -0
- package/dist/tracing.d.ts +118 -0
- package/dist/tracing.js +204 -0
- package/dist/tracing.js.map +1 -0
- package/llms.txt +7 -3
- package/package.json +44 -2
- package/templates/app/AGENTS.md +5 -3
package/templates/app/AGENTS.md
CHANGED
|
@@ -30,9 +30,9 @@ Do not finish until `inferlo check` passes.
|
|
|
30
30
|
## Rules
|
|
31
31
|
|
|
32
32
|
1. **One way to do each thing.** Routes: `route.get/post/put/patch/delete(path, options).handle(fn)`. Validation: `t.*` schemas. Database: `db.<table>.<method>`. AI: `ai.generate` / `ai.stream` / `ai.embed`.
|
|
33
|
-
2. **Routes are private by default.** A route without `auth` needs a signed-in user. Use `auth: "public"` only for routes anyone may call, and `auth: "<role>"` for role checks.
|
|
33
|
+
2. **Routes are private by default.** A route without `auth` needs a signed-in user. Use `auth: "public"` only for routes anyone may call, and `auth: "<role>"` for role checks. Browser sign-in uses cookie sessions: `export const session = sessions({ secret: settings.SESSION_SECRET, user: ({ userId }, { db }) => ... })`, `createApp({ auth: session })`, `await session.signIn(ctx, user.id)` in the login route and `session.signOut(ctx)` in logout; browsers send the CSRF token in `X-CSRF-Token` or a `_csrf` form field on POST, PUT, PATCH and DELETE (403 IL3005 without it).
|
|
34
34
|
3. **Validate every input with a schema.** `body`, `query` and `params` options. Reuse model schemas: `Ticket.pick("title", "body")`, `Ticket.omit("ownerId")`, `.partial()` for PATCH.
|
|
35
|
-
4. **Check ownership.** Fetch with `db.x.get(id)` (throws 404) and compare `ownerId` to `user.id`; throw `notFound()` when it does not match, so other users' ids are not revealed.
|
|
35
|
+
4. **Check ownership.** Fetch with `db.x.get(id)` (throws 404) and compare `ownerId` to `user.id`; throw `notFound()` when it does not match, so other users' ids are not revealed. When more than one rule decides access (owners, admins, teams), define named permissions once in `src/permissions.ts` with `export const access = permissions({ "notes.update": ({ user, note }) => note.ownerId === user.id || (user.roles ?? []).includes("admin") })`, pass `permissions: access` to `createApp`, and call `await access.authorize("notes.update", { user, note })` in routes and jobs (403 IL3004 when denied). Permissions that need only the user go on the route: `{ permission: "reports.view" }`; add `permissions: typeof access` to `Register` (with `import type`) so permission names are type-checked. When changes must be on record (who changed what), add `AuditLog` to the models and call `await audit("notes.update", { model: "notes", before: note, after: updated })` after the change, in routes and jobs (jobs pass `userId`).
|
|
36
36
|
5. **Related rows:** a `t.ref` field named `<name>Id` is a relation: `db.notes.findMany({ include: { owner: true } })` adds `note.owner` with one extra query, and `db.users.findMany({ include: { notes: true } })` adds `user.notes` (the rows that point to each user). Never load related rows in a loop. For lists clients page through, use `db.notes.paginate({ where, orderBy: { createdAt: "desc" }, cursor: query.cursor, limit: query.limit })`, which returns `{ items, nextCursor }`.
|
|
37
37
|
6. **Never write raw SQL with string concatenation.** Use the table methods. If you really need SQL, use `db.$sql(text, params)` with `$1` placeholders.
|
|
38
38
|
7. **Model ids, timestamps:** every model already has `id`, `createdAt`, `updatedAt`. Never define them or send them as input.
|
|
@@ -45,7 +45,9 @@ Do not finish until `inferlo check` passes.
|
|
|
45
45
|
14. **Expose to AI agents:** add `tool: true` to a route to make it an MCP tool at `/mcp`. Same auth applies.
|
|
46
46
|
15. **Slow work after the response** (email, slow AI calls) is a background job: `export const x = job("send-email", { input: t.object({ ... }) }).handle(async ({ input, db, ai }) => { ... })` in `src/jobs.ts`, registered with `createApp({ jobs: [jobs] })`, queued with `await ctx.jobs.enqueue(x, { ... })`. Pass ids in the input, not rows. In tests, run queued jobs with `await app.jobs.runDue()`. Jobs run in the server process (`app.jobs.start()` in `src/server.ts`); with `createApp({ jobQueue: { store: "database" } })` they are kept in Postgres and `npm run worker` (`inferlo worker`) runs them in separate processes. Scheduled work is a cron next to its job: `export const nightly = cron("0 3 * * *", cleanupJob)` (UTC; the job's input must be `t.object({})`).
|
|
47
47
|
16. **Security headers and health:** every response already gets security headers (CSP, HSTS over HTTPS, nosniff, frame and referrer policies) and `GET /health` and `GET /ready` are built in (`health: true` in `src/app.ts`). Change a header with `createApp({ secureHeaders: { "Content-Security-Policy": "..." } })` instead of setting it in every route, never turn them all off without a proxy that adds them, and never add your own `/health` route. On SIGTERM, `src/server.ts` calls `app.close()`, which finishes in-flight requests and jobs first. Prometheus metrics (`GET /metrics`: requests, jobs, AI calls, tokens and cost) are served when `METRICS_TOKEN` is set; never add your own `/metrics` route.
|
|
48
|
-
17. **
|
|
48
|
+
17. **Large apps: feature modules.** When the app grows to several features, group each feature's models, routes and jobs in `src/<feature>/module.ts` with `export const billing = module("billing", { imports: [users], models: [...], routes: [routes], jobs: [jobs], provides: { createInvoice } })` and register it with `createApp({ modules: [users, billing] })`. A module that uses another module's models or `provides` lists it in `imports` (IL6023 and IL6024 tell you when one is missing). Call other modules only through their `provides`; never import their internal files. Keep every model in `Register` in `src/models.ts`.
|
|
49
|
+
18. **Replacing a route:** do not delete a route clients use. Add the new one and mark the old one `deprecated: { since: "YYYY-MM-DD", sunset: "YYYY-MM-DD", link: "https://..." }` in its options; it then sends the standard Deprecation and Sunset headers and is marked in OpenAPI and MCP. Remove it after the sunset date. For a breaking change to many routes, publish a new API version instead: `createApp({ routes: [apiVersion("v1", [notesV1]), apiVersion("v2", [notes])] })` serves them under `/v1` and `/v2`, each with its own OpenAPI document (`/v1/openapi.json`); write route paths without the version prefix.
|
|
50
|
+
19. **Caching:** for results that are slow or expensive to compute, use the built-in cache instead of a module-level `Map`: `` return cache.getOrSet(`notes:stats:${user.id}`, { ttlMs: 60_000, tags: ["notes"] }, () => computeStats(db, user.id)) `` in routes and jobs (`ctx.cache`), and `await cache.invalidate("notes")` after notes change. `ttlMs` is required; put the user id in the key of anything that differs per user; values come back as JSON (dates as strings). To cache a whole GET route instead, add the route option `cache: { ttlMs: 60_000, tags: ["notes"] }` (one response per user by default; `by: "public"` only for data that is the same for everyone) and invalidate the tag where the data changes. With several instances, use `createApp({ cache: { store: "database" } })`.
|
|
49
51
|
|
|
50
52
|
## Testing
|
|
51
53
|
|